laravel生成seo标准的url链接
大家好好久没有更新博客了,今天和大家聊一聊 如何使用laravel 生成优雅的符合seo标准的url让我们的网站url看起来比较好看些,本博客就是采用的此方法。
下面开始我们的教程
首先我们先创建一张带有slug字段的数据表 topics
然后我们先生成 model 假设 你的modl在app目录下 我们执行以下命令:
php artisan make:model Topic
然后生成控制器 TpoicsController
php artisan make:controller TpoicsController
web.php 添加路由
Route::get('topics/{topic}/{slug?}','TopicsController@show')->name('topics.show');
URI 最后一个参数表达式 {slug?}
,?
意味着参数可选,这是为了兼容我们数据库中 Slug 为空的文章数据。这种写法可以同时兼容以下两种链接:
http://zysmile.test/topics/115 http://zysmile.test/topics/115/slug-translation-test
然后我们 创建观察者类 确保 在app目录下创建Observers 文件夹
然后我们在Observers目录中创建 TopicObserver.php 注意是类文件
为了照顾下基础薄弱的小伙伴我们在此讲解下模型事件。
在 Laravel
的世界中,你对 Eloquent
大多数操作都会或多或少的触发一些模型事件,今天就来看一下模型事件的使用。
Laravel
事先已经定义好了 10
个模型事件以供我们使用,它们分别是:
creating
, created
, updating
, updated
, saving
, saved
, deleting
, deleted
, restoring
, restored
。
事件名称都很浅显易懂,如果你是认真写代码话都应该可以看明白
不过你可能对 上面些事件有些疑惑 怎么有两个相同的比如creating
, created
为了我们教程的顺利进行我们简单的说明下
creating 是在数据创建之前触发,created 是数据创建之后触发
updating 是在数据更新之前触发,updated 是在数据更新之后触发
deleting 是在删除数据之前触发,deleted是在删除数据之后触发
saving 是在保存数据之前触发 ,saved 是在数据创建之后触发
需要注意的是 当模型不存在,需要新增的时候,依次触发的顺序则是
saving
-> creating
-> created
-> saved
当模型已存在,不是新建的时候,依次触发的顺序是:
saving
-> updating
-> updated
-> saved
saving
和 saved
则会在 Eloquent
实例的 original
数组真值更改前后触发
这点需要特别注意!
在创建观察者类文件 TopicObserver 之前我们先去百度注册个百度翻译 获取api 的key 点击前往百度翻译
需要注意的是如果你是本地 homestead 的环境 请填写你的外网ip地址,而不是 192.168.10.10 切记!
准备工作完成后,我们正式开始后续的工作
我们先安装 http扩展包
1. 安装依赖 Guzzle
Guzzle 库是一套强大的 PHP HTTP 请求套件,我们使用 Guzzle 的 HTTP 客户端来请求 百度翻译 接口。
使用 Composer 安装 Guzzle 类库:
$ composer require "guzzlehttp/guzzle:~6.3"
2. 安装依赖 PinYin
PinYin 是 安正超 开发的,基于 CC-CEDICT 词典的中文转拼音工具,是一套优质的汉字转拼音解决方案。我们使用 PinYin 来作为翻译的后备计划,当百度翻译 API 不可用时,程序会自动使用 PinYin 汉字转拼音方案来生成 Slug。
$ composer require "overtrue/pinyin:~4.0"
以上两个包安装完成后,我们假定你已经注册申请完成百度api的key, 确保我们能顺利进行下面的教程部分。
接下来我们将 appid
和 key
集成到的项目中。一般情况下,我们将这种第三方服务授权认证信息存放于 services.php
里:
config/services.php
<?php return [ . . . 'baidu_translate' => [ 'appid' => env('BAIDU_TRANSLATE_APPID'), 'key' => env('BAIDU_TRANSLATE_KEY'), ],];
我们还希望不同环境下对配置信息有不同的设定,所以在此处我们通过 env()
方法来引用环境变量,接下来我们需要在本地环境变量中新增这两个值,请将这两个值替换为你申请的值:
.env
BAIDU_TRANSLATE_APPID=201703xxxxxxxxxxxxx BAIDU_TRANSLATE_KEY=q0s6axxxxxxxxxxxxxxxxx
接下来我们封装一个翻译类 SlugTranslateHandler
我们在app目录下创建个Handlers目录 app/Handlers
代码如下:
<?php namespace App\Handlers; use GuzzleHttp\Client; use Illuminate\Support\Str; use Overtrue\Pinyin\Pinyin; class SlugTranslateHandler { public function translate($text) { // 实例化 HTTP 客户端 $http = new Client(); // 初始化配置信息 $api = 'http://api.fanyi.baidu.com/api/trans/vip/translate?'; $appid = config('services.baidu_translate.appid'); $key = config('services.baidu_translate.key'); $salt = time(); // 如果没有配置百度翻译,自动使用兼容的拼音方案 if (empty($appid) || empty($key)) { return $this->pinyin($text); } // 根据文档,生成 sign // http://api.fanyi.baidu.com/api/trans/product/apidoc // appid+q+salt+密钥 的MD5值 $sign = md5($appid. $text . $salt . $key); // 构建请求参数 $query = http_build_query([ "q" => $text, "from" => "zh", "to" => "en", "appid" => $appid, "salt" => $salt, "sign" => $sign, ]); // 发送 HTTP Get 请求 $response = $http->get($api.$query); $result = json_decode($response->getBody(), true); // 尝试获取获取翻译结果 if (isset($result['trans_result'][0]['dst'])) { return Str::slug($result['trans_result'][0]['dst']); } else { // 如果百度翻译没有结果,使用拼音作为后备计划。 return $this->pinyin($text); } } public function pinyin($text) { return Str::slug(app(Pinyin::class)->permalink($text)); } }
翻译调用
我们只需在入库前对 slug 字段进行赋值即可,我们可以方便地在 Eloquent 观察器中实现:
app/Observers/TopicObserver.php
我们在之前创建的TopicObserver.php中 写入如下代码:
<?phpnamespace App\Observers;use App\Models\Topic;use App\Handlers\SlugTranslateHandler;// creating, created, updating, updated, saving,// saved, deleting, deleted, restoring, restoredclass TopicObserver{ public function saving(Topic $topic) { // XSS 过滤 $topic->body = clean($topic->body, 'user_topic_body'); // 生成话题摘录 $topic->excerpt = make_excerpt($topic->body); // 如 slug 字段无内容,即使用翻译器对 title 进行翻译 if ( ! $topic->slug) { $topic->slug = app(SlugTranslateHandler::class)->translate($topic->title); } }}
app()
允许我们使用 Laravel 服务容器 ,此处我们用来生成 SlugTranslateHandler 实例。
我们发布一篇文章测试下看看数据库有没有生成
我们发现已经生成
显示修改
现在我们已经能生成 Slug 了,接下来我们需要将其显示出来。路由规则有了变化,我们需要修改路由文件。为了方便调用和管理,我们将在 Topic 模型中新建方法 link()
来生成模型 URL,然后调用了 route('topics.show', $topic->id)
的地方修改为 $topic->link()
。
<a href="{{ $topic->link() }}" title="{{ $topic->title }}"> {{ $topic->title }}</a>
下面我们在模型 Topic 模型中创建link() 方法
<?phpnamespace App\Models;class Topic extends Model{ . . . public function link($params = []) { return route('topics.show', array_merge([$this->id, $this->slug], $params)); }}
参数 $params
允许附加 URL 参数的设定
这样我们就完成了 url 的seo 美化 但是还有点bug 就是之前我们设定过路由
Route::get('topics/{topic}/{slug?}','TopicsController@show')->name('topics.show')
强制跳转
当文章有 Slug 的时候,我们希望用户一直使用正确的、带着 Slug 的链接来访问。我们可以在控制器中对 Slug 进行判断,当条件允许的时候,我们将发送 301 永久重定向指令给浏览器,跳转到带 Slug 的链接:
app/Http/Controllers/TopicsController.php
后面的slug 我们可以看到虽然是生成了slug 但是用户在浏览器中输入的如果不是我们生成的slug会导致输入任意slug都可以访问到我们的文章
这是我们不愿意看到的,我们要确保已经生成的slug url 必须是和url中的是一致匹配的,如果用户恶意输入错误的slug 我们将修正这个url的slug
我们在app/Http/Controllers/TopicsController.php 文件中 路由定义的show() 方法 添加如下限制
<?php...class TopicsController extends Controller{ . . . public function show(Request $request, Topic $topic) { // 防止用户恶意输入 if ( ! empty($topic->slug) && $topic->slug != $request->slug) { return redirect($topic->link(), 301); } return view('topics.show', compact('topic')); } . . . }
代码讲解:
我们需要访问用户请求的路由参数 Slug,在
show()
方法中我们注入$request
;! empty($topic->slug)
如果话题的 Slug 字段不为空;&& $topic->slug != $request->slug
并且话题 Slug 不等于请求的路由参数 Slug;redirect($topic->link(), 301)
301 永久重定向到正确的 URL 上。
通过以上现在我们已经完成了 laravel url 的美化功能,我们测试下看看
可以看到 我们成功了实现这个效果,本次教程就到这里了,喜欢我的教程请扫码关注网站首页的二维码,猪哥哥等你哦!