SEO友好URL (百度翻译API),用队列实现异步处理

SEO友好URL

(Search Engine Optimization):汉译为搜索引擎优化。
一个SEO友好的URL格式可以大大提升网站的爬取率以及索引率。此外,在URL这个地方部署关键词可以提升网页的相关性。

1.创建观察者类 确保 在app目录下创建Observers 文件夹

然后我们在Observers目录中创建 TopicObserver.php类文件

知识点:
在 Laravel 的世界中,你对 Eloquent 大多数操作都会或多或少的触发一些模型事件,Laravel 事先已经定义好了 10 个模型事件以供我们使用,它们分别是:
creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored。
creating 是在数据创建之前触发,created 是数据创建之后触发
updating 是在数据更新之前触发,updated 是在数据更新之后触发
deleting 是在删除数据之前触发,deleted是在删除数据之后触发
saving 是在保存数据之前触发 ,saved 是在数据创建之后触发
需要注意的是 当模型不存在,需要新增的时候,依次触发的顺序则是
saving -> creating -> created -> saved
当模型已存在,不是新建的时候,依次触发的顺序是:
saving -> updating -> updated -> saved
saving 和 saved 则会在 Eloquent 实例的 original 数组真值更改前后触发

2.去百度翻译 API:注册个百度翻译 获取api 的key

接口作用:用于把我们标题或关键字翻译成英文。

这里逻辑是:调用接口翻译成英文,要是调用或翻译失败我们就将标题翻译成拼音,然后生成slug字段内容。
所以这里需要两个扩展包:

3.安装Guzzle扩展包:发送 http 请求的(我这里直接下载最新的,没指定版本) ,这里用于对接百度翻译 API接口

composer require "guzzlehttp/guzzle"

4.安装依赖 PinYin

$ composer require "overtrue/pinyin"

5.修改配置

config/services.php

'baidu_translate' => [
        'appid' => env('BAIDU_TRANSLATE_APPID'),
        'key' => env('BAIDU_TRANSLATE_KEY'),
    ],

.env

BAIDU_TRANSLATE_APPID=201703xxxxxxxxxxxxx
BAIDU_TRANSLATE_KEY=q0s6axxxxxxxxxxxxxxxxx

6.封装一个翻译类

app/Handlers/SlugTranslateHandler

<?php

namespace App\Handlers;

use GuzzleHttp\Client;
use Overtrue\Pinyin\Pinyin;

use Illuminate\Support\Str;

class SlugTranslateHandler
{
    public function translate($text)
    {
        //实例化 HTTP客户端
        $http = new Client;

        //初始化配置信息
        $api = 'https://fanyi-api.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);

        //      获取结果,如果请求成功,dd($result) 结果如下:
        //       array:3 [▼
        //      "from" => "zh"
        //     "to" => "en"
        //     "trans_result" => array:1 [▼
        //         0 => array:2 [▼
        //             "src" => "XSS 安全漏洞"
        //             "dst" => "XSS security vulnerability"
        //         ]
        //     ]
        // ]

        //获取翻译结果
       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));
    }
}

7.用Redis队列实现异步处理任务 (这样就不影响用户创建话题帖子了!)

7.1.Composer 安装Redis依赖:

composer require "predis/predis"

修改环境变量QUEUE_DRIVER 的值为 redis:
.env

# SESSION_DRIVER=file
SESSION_DRIVER=redis

7.2.处理队列中任务失败的情况
使用 queue:failed-table 命令来创建 failed_jobs 表的迁移文件:

$ php artisan queue:failed-table

会新建 database/migrations/{timestamp}_create_failed_jobs_table.php 文件

最新版本laravel10默认已有这个文件了,使用 以下 命令生成 failed_jobs 表就好了:

$ php artisan migrate

3.生成任务类
使用以下 Artisan 命令来生成一个新的队列任务:

$ php artisan make:job TranslateSlug

该命令会在 app/Jobs 目录下生成一个新的类:
app/Jobs/TranslateSlug.php 编辑任务内容(调用翻译接口并异步更新slug字段):

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

use App\Models\Topic;
use App\Handlers\SlugTranslateHandler;
use Illuminate\Support\Facades\DB;

class TranslateSlug implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $topic;
    /**
     * Create a new job instance.
     */
    public function __construct(Topic $topic)
    {
        // 队列任务构造器中接收了 Eloquent 模型,将会只序列化模型的 ID
        $this->topic = $topic;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $slug = app(SlugTranslateHandler::class)->translate($this->topic->title);

        DB::table('topics')->where('id', $this->topic->id)->update(['slug' => $slug]);
    }
}

8.在我们创建的观察类(即Topic 模型监控器)app/Observers/TopicObserver.php中用 队列执行的方式 调用 Slug 翻译

namespace App\Observers;
use App\Jobs\TranslateSlug;
...
  /**
     * 保存进数据库以后(因为创建帖子的时候,要创建完成后才有id)
     */
    public function saved(Topic $topic)
    {
        //使用队列 实现异步修改slug 从而不影响用户创建话题
        if (!$topic->slug) {
            // 推送任务到队列
            dispatch(new TranslateSlug($topic));
        }
    }

9.修改一下路由

Route::get('topics/{topic}/{slug?}','TopicsController@show')->name('topics.show')

在模型 Topic 模型中创建link() 方法

<?php
namespace App\Models;
class Topic extends Model{
    .
    .
    .

    public function link($params = [])
    {
        return route('topics.show', array_merge([$this->id, $this->slug], $params));
    }}

参数 $params 允许附加 URL 参数的设

强制跳转

当文章有 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() 方法 添加如下限制

   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'));
    }

ok了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容