调用百度翻译,加入seo

This commit is contained in:
fthvgb1 2018-01-01 21:22:16 +08:00
parent 7db8c91e69
commit 2a5996e6c1
13 changed files with 298 additions and 20 deletions

View File

@ -28,8 +28,11 @@ class TopicsController extends Controller
return view('topics.index', compact('topics')); return view('topics.index', compact('topics'));
} }
public function show(Topic $topic) public function show(Topic $topic, Request $request)
{ {
if (!empty($topic->slug) && $topic->slug != $request->slug) {
return redirect($topic->link(), 301);
}
return view('topics.show', compact('topic')); return view('topics.show', compact('topic'));
} }
@ -38,7 +41,7 @@ class TopicsController extends Controller
$topic->fill($request->all()); $topic->fill($request->all());
$topic->user_id = Auth::id(); $topic->user_id = Auth::id();
$topic->save(); $topic->save();
return redirect()->route('topics.show', $topic->id)->with('success', '添加成功!.'); return redirect()->to($topic->link())->with('success', '添加成功!.');
} }
public function edit(Topic $topic) public function edit(Topic $topic)
@ -75,7 +78,7 @@ class TopicsController extends Controller
$this->authorize('update', $topic); $this->authorize('update', $topic);
$topic->update($request->all()); $topic->update($request->all());
return redirect()->route('topics.show', $topic->id)->with('success', '编辑成功!.'); return redirect()->route('topics.show', [$topic->id, $topic->slug])->with('success', '编辑成功!');
} }
public function destroy(Topic $topic) public function destroy(Topic $topic)
@ -83,6 +86,6 @@ class TopicsController extends Controller
$this->authorize('destroy', $topic); $this->authorize('destroy', $topic);
$topic->delete(); $topic->delete();
return redirect()->route('topics.index')->with('message', 'Deleted successfully.'); return redirect()->route('topics.index')->with('message', '删除成功.');
} }
} }

View File

@ -7,6 +7,11 @@ class Topic extends Model
protected $fillable = ['title', 'category_id', 'body', 'excerpt', 'slug']; protected $fillable = ['title', 'category_id', 'body', 'excerpt', 'slug'];
public function link($params = [])
{
return route('topics.show', array_merge([$this->id, $this->slug], $params));
}
public function scopeWithOrder($query, $order) public function scopeWithOrder($query, $order)
{ {
// 不同的排序,使用不同的数据读取逻辑 // 不同的排序,使用不同的数据读取逻辑

View File

@ -32,6 +32,11 @@ class User extends Authenticatable
return asset($this->avatar); return asset($this->avatar);
} }
public function isAuthorOf($model)
{
return $this->id == $model->user_id;
}
public function topics() public function topics()
{ {
return $this->hasMany(Topic::class); return $this->hasMany(Topic::class);

View File

@ -3,6 +3,7 @@
namespace App\Observers; namespace App\Observers;
use App\Models\Topic; use App\Models\Topic;
use App\Tools\SlugTranslateHandler;
// creating, created, updating, updated, saving, // creating, created, updating, updated, saving,
// saved, deleting, deleted, restoring, restored // saved, deleting, deleted, restoring, restored
@ -18,6 +19,10 @@ class TopicObserver
{ {
$topic->body = clean($topic->body, 'user_topic_body'); $topic->body = clean($topic->body, 'user_topic_body');
$topic->excerpt = make_excerpt($topic->body); $topic->excerpt = make_excerpt($topic->body);
// 如 slug 字段无内容,即使用翻译器对 title 进行翻译
if (!$topic->slug) {
$topic->slug = app(SlugTranslateHandler::class)->translate($topic->title);
}
} }
public function updating(Topic $topic) public function updating(Topic $topic)

View File

@ -9,11 +9,11 @@ class TopicPolicy extends Policy
{ {
public function update(User $user, Topic $topic) public function update(User $user, Topic $topic)
{ {
return $topic->user_id == $user->id; return $user->isAuthorOf($topic);
} }
public function destroy(User $user, Topic $topic) public function destroy(User $user, Topic $topic)
{ {
return true; return $user->isAuthorOf($topic);
} }
} }

View File

@ -0,0 +1,81 @@
<?php
/**
* Created by PhpStorm.
* User: xing
* Date: 2018/1/1
* Time: 20:51
*/
namespace App\Tools;
use GuzzleHttp\Client;
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);
/**
* 获取结果如果请求成功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));
}
}

View File

@ -11,13 +11,15 @@
"php": ">=7.0.0", "php": ">=7.0.0",
"doctrine/dbal": "^2.6", "doctrine/dbal": "^2.6",
"fideloper/proxy": "~3.3", "fideloper/proxy": "~3.3",
"guzzlehttp/guzzle": "~6.3",
"hieu-le/active": "~3.5", "hieu-le/active": "~3.5",
"intervention/image": "^2.4", "intervention/image": "^2.4",
"laravel/framework": "5.5.*", "laravel/framework": "5.5.*",
"laravel/tinker": "~1.0", "laravel/tinker": "~1.0",
"mews/captcha": "~2.0", "mews/captcha": "~2.0",
"mews/purifier": "~2.0", "mews/purifier": "~2.0",
"overtrue/laravel-lang": "^3.0" "overtrue/laravel-lang": "^3.0",
"overtrue/pinyin": "~3.0"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "^3.1", "barryvdh/laravel-debugbar": "^3.1",

163
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0623f829dd1042d2c133664fd6525faa", "content-hash": "8d5baf0bc06734b3359648bb624b4f3c",
"packages": [ "packages": [
{ {
"name": "caouecs/laravel-lang", "name": "caouecs/laravel-lang",
@ -763,6 +763,122 @@
], ],
"time": "2017-06-15T17:19:42+00:00" "time": "2017-06-15T17:19:42+00:00"
}, },
{
"name": "guzzlehttp/guzzle",
"version": "6.3.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699",
"reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.4",
"php": ">=5.5"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.0 || ^5.0",
"psr/log": "^1.0"
},
"suggest": {
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.2-dev"
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"time": "2017-06-22T18:50:49+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"time": "2016-12-20T10:07:11+00:00"
},
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "1.4.2", "version": "1.4.2",
@ -1747,6 +1863,51 @@
], ],
"time": "2017-10-28T12:10:43+00:00" "time": "2017-10-28T12:10:43+00:00"
}, },
{
"name": "overtrue/pinyin",
"version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/overtrue/pinyin.git",
"reference": "3b781d267197b74752daa32814d3a2cf5d140779"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/pinyin/zipball/3b781d267197b74752daa32814d3a2cf5d140779",
"reference": "3b781d267197b74752daa32814d3a2cf5d140779",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"type": "library",
"autoload": {
"psr-4": {
"Overtrue\\Pinyin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Carlos",
"homepage": "http://github.com/overtrue"
}
],
"description": "Chinese to pinyin translator.",
"homepage": "https://github.com/overtrue/pinyin",
"keywords": [
"Chinese",
"Pinyin",
"cn2pinyin"
],
"time": "2017-07-10T07:20:01+00:00"
},
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",
"version": "v2.0.11", "version": "v2.0.11",

View File

@ -14,6 +14,11 @@ return [
| |
*/ */
'baidu_translate' => [
'appid' => env('BAIDU_TRANSLATE_APPID'),
'key' => env('BAIDU_TRANSLATE_KEY'),
],
'mailgun' => [ 'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'), 'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'), 'secret' => env('MAILGUN_SECRET'),

View File

@ -62,7 +62,7 @@
<select class="form-control" name="category_id" required> <select class="form-control" name="category_id" required>
<option value="" hidden disabled selected>请选择分类</option> <option value="" hidden disabled selected>请选择分类</option>
@foreach ($categories as $value) @foreach ($categories as $value)
<option value="{{ $value->id }}">{{ $value->name }}</option> <option value="{{ $value->id }}" {{ $topic->category_id == $value->id ? 'selected' : '' }}>{{ $value->name }}</option>
@endforeach @endforeach
</select> </select>
</div> </div>

View File

@ -42,15 +42,24 @@
{!! $topic->body !!} {!! $topic->body !!}
</div> </div>
<div class="operate"> @can('update', $topic)
<hr> <div class="operate">
<a href="{{ route('topics.edit', $topic->id) }}" class="btn btn-default btn-xs" role="button"> <hr>
<i class="glyphicon glyphicon-edit"></i> 编辑 <a href="{{ route('topics.edit', $topic->id) }}" class="btn btn-default btn-xs pull-left"
</a> role="button">
<a href="#" class="btn btn-default btn-xs" role="button"> <i class="glyphicon glyphicon-edit"></i> 编辑
<i class="glyphicon glyphicon-trash"></i> 删除 </a>
</a>
</div> <form action="{{ route('topics.destroy', $topic->id) }}" method="post">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" class="btn btn-default btn-xs pull-left" style="margin-left: 6px">
<i class="glyphicon glyphicon-trash"></i>
删除
</button>
</form>
</div>
@endcan
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
<ul class="list-group"> <ul class="list-group">
@foreach ($topics as $topic) @foreach ($topics as $topic)
<li class="list-group-item"> <li class="list-group-item">
<a href="{{ route('topics.show', $topic->id) }}"> <a href="{{ route('topics.show', [$topic->id,$topic->slug]) }}">
{{ $topic->title }} {{ $topic->title }}
</a> </a>
<span class="meta pull-right"> <span class="meta pull-right">

View File

@ -30,7 +30,9 @@ Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm'
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email'); Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset'); Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController@reset'); Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::resource('topics', 'TopicsController', ['only' => ['index', 'show', 'create', 'store', 'update', 'edit', 'destroy']]); Route::resource('topics', 'TopicsController', ['only' => ['index', 'create', 'store', 'update', 'edit', 'destroy']]);
Route::get('topics/{topic}/{slug?}', 'TopicsController@show')->name('topics.show');
Route::resource('categories', 'CategoriesController', ['only' => ['show']]); Route::resource('categories', 'CategoriesController', ['only' => ['show']]);
Route::post('upload_image', 'TopicsController@uploadImage')->name('topics.upload_image'); Route::post('upload_image', 'TopicsController@uploadImage')->name('topics.upload_image');