Laravel - Tối ưu URL với Eloquent Sluggable
Bài đăng này đã không được cập nhật trong 3 năm
1. Tại sao nên tối ưu cấu trúc đường dẫn?
Việc tối ưu cấu trúc đường dẫn sẽ giúp bạn tạo ra các đường dẫn thân thiện (Friendly URLs) để các công cụ tìm kiếm dễ dàng đọc, lập chỉ mục, dễ nhớ và ngắn gọn ngoài ra còn có thể bao gồm từ khóa có trong nội dung của đường dẫn để tốt nhất cho việc SEO.
Ví dụ về đường dẫn thân thiện và không thân thiện:
-
Đường dẫn không thân thiện
-
Đường dẫn thân thiện
Mặc định Laravel cũng đã giúp bạn để các đường dẫn trở nên dễ đọc và dễ nhìn như http://localhost.dev/post/10, http://localhost.dev/post/10/comments
Tuy nhiên đường dẫn như vậy thường sẽ không cung cấp nhiều thông tin cho cả người đọc và các công cụ tìm kiếm. Chẳng hạn con số 10 ở đây có ý nghĩa gì đối với người dùng? Các bạn đều biết đó là giá trị integer đại diện cho primary key của bản ghi được tìm thấy trong bảng “post” tuy nhiên nó sẽ có ý nghĩa hơn rất nhiều nếu sử dụng đường dẫn như sau: http://localhost.dev/post/duong-dan-than-thien-trong-laravel-5.
Các bạn có thể thấy được tác dụng của việc tối ưu cấu trúc đường dẫn, trong phần sau của ìa viết mình sẽ hướng dẫn các bạn cách tạo ra đường dẫn thân thiện trong Laravel 5.
2. Tối ưu URL với Eloquent Sluggable
Việc tạo đường dẫn thân thiện trong Laravel tương đối đơn giản dưới sự hỗ trợ của package eloquent-sluggable
.
2.1. Cài đặt
CHÚ Ý: Tùy thuộc vào phiên bản laravel mà chúng ta cài đặt
package
với các version khác nhau
Laravel Version Sluggable Version 4.x 2.x 5.1, 5.2 4.0 5.1*, 5.2*, 5.3 4.1
Chúng ta có thể cài đặt package thông qua Composer
$ composer require cviebrock/eloquent-sluggable:^4.1
Sau đó, cập nhật config config/app.php
'providers' => [
// ...
Cviebrock\EloquentSluggable\ServiceProvider::class,
];
Sau đó, bạn sẽ cập nhật việc cấu hình bằng cách chạy lệnh php artisan vendor:publish
trong console:
php artisan vendor:publish --provider="Cviebrock\EloquentSluggable\ServiceProvider"
2.2. Cập nhật Eloquent Models
Để sử dụng slug
thì trong model
cũng như database
của bạn cần phải có collumn
chứa nó, chúng ta có thể thêm vào bằng migration
. Tiếp theo chúng ta cập nhật lại các models
cần sử dụng slug
. Nhữn models
đó cần phải sử dụng Sluggable
trait đồng thời phải khai báo phương thức sluggable()
, phương thức này trả về 1 mảng chứa các config
liên quan tới slug
của model
hiện tại. Các bạn xem thêm tại: Configuration
use Cviebrock\EloquentSluggable\Sluggable;
class Post extends Model
{
use Sluggable;
/**
* Return the sluggable configuration array for this model.
*
* @return array
*/
public function sluggable()
{
return [
'slug' => [
'source' => 'title'
]
];
}
}
2.3. Sử dụng
Sau các bước trên, slug
sẽ được tự động tạo mỗi khi chúng ta thực hiện phương thức save()
của model
:
$post = new Post([
'title' => 'My Awesome Blog Post',
]);
$post->save();
Đê lấy slug
:
echo $post->slug;
Nếu chúng ta thực hiện sao chép model
với phương thức replicate()
của Eloquent
thì package
sẽ tự động tạo lại slug
cho giá trị được sao chép để đảm bảo slug
là unique
.
$post = new Post([
'title' => 'My Awesome Blog Post',
]);
$post->save();
// $post->slug is "my-awesome-blog-post"
$newPost = $post->replicate();
// $newPost->slug is "my-awesome-blog-post-1"
2.4. Class SlugService
Tất cả các xử lý logic về việc tạo slug
đều được quản lý bởi class \Cviebrock\EloquentSluggable\Services\SlugService
Bạn có thể tạo ra slug
mà không thực hiện việc tạo và lưu vào model
bằng phương thức createSlug
:
use \Cviebrock\EloquentSluggable\Services\SlugService;
$slug = SlugService::createSlug(Post::class, 'slug', 'My First Post');
Ngoài 3 tham số cơ bản trên, phương thức createSlug
còn có thêm tham số thứ 4 là một array
chứa config
, ví dụ:
$slug = SlugService::createSlug(Post::class, 'slug', 'My First Post', ['unique' => false]);
Với việc đặt unique = false
, slug
được tạo ra từ ví dụ trên sẽ ko là unique
.
2.5. Events
Các model Sluggable
sẽ thực thi 2 Eloquent events "slugging"
and "slugged"
.
Event "slugging"
được thực hiện trước khi slugs
được tạo ra. Nếu hàm callback
từ event
này trả về là false
thì slug
sẽ không được tạo ra.
Event slugged
được thực thi ngay sau slug
được tạo ra. Nó sẽ không được gọi nếu model
không cần slugging
(bạn cần khai báo việc cần slugging
hay không với phương thức needsSlugging()
).
Post::registerModelEvent('slugging', function($post) {
if ($post->someCondition()) {
// the model won't be slugged
return false;
}
});
Post::registerModelEvent('slugged', function($post) {
Log::info('Post slugged: ' . $post->getSlug());
});
2.6. Configuration
Configuration
được thiết kế để trở lên linh hoạt nhất có thể. Bạn có thể cài đặt mặc định cho tất cả các models
và thực hiện việc setting riêng cho từng models
sau đó.
Mặc định, configuration
tổng quát cho các models
được lưu trong file app/config/sluggable.php
. Nếu bạn chưa publish
file sluggable.php
thì package
sẽ lấy dữ liệu từ vendor/cviebrock/eloquent-sluggable/resources/config/sluggable.php
như là dữ liệu mặc định.
return [
'source' => null,
'maxLength' => null,
'method' => null,
'separator' => '-',
'unique' => true,
'uniqueSuffix' => null,
'includeTrashed' => false,
'reserved' => null,
];
Cho từng models
, configuration
được quản lý với phương thức sluggable()
. Phương thức này sẽ trả về một danh sách chứa config
cho model
:
public function sluggable()
{
return [
'title-slug' => [
'source' => 'title'
],
'author-slug' => [
'source' => ['author.firstname', 'author.lastname']
],
];
}
Chúng ta có thể tạo nhiều slugs
cho cùng model
, dựa trên các source
khác nhau cùng với các options
khác nhau, ví dụ:
public function sluggable()
{
return [
'title-slug' => [
'source' => 'title'
],
'author-slug' => [
'source' => ['author.firstname', 'author.lastname'],
'separator' => '_'
],
];
}
2.7. SluggableScopeHelpers Trait
Bằng việc thêm SluggableScopeHelpers
trait vào model
của bạn, bạn có thể thực hiện một số phương thức như:
$post = Post::whereSlug($slugString)->get();
$post = Post::findBySlug($slugString);
$post = Post::findBySlugOrFail($slugString);
Xem thêm tại: SCOPE-HELPERS.md.
2.8. Route Model Binding
Như các bạn đã biết, kể từ laravel 5.2
, Laravel đã bắt đầu hỗ trợ Model Binding
trong Route
.
2.8.1. Implicit Binding
Để thay đổi việc Implicit binding
bằng id
sang slug
, chúng ta chỉ cần thêm phương thức getRouteKeyName()
vào model
và trả về tên của slug
:
use Cviebrock\EloquentSluggable\Sluggable;
use Cviebrock\EloquentSluggable\SluggableScopeHelpers;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use Sluggable;
public function sluggable() {
return [
'slug' => [
'source' => 'title',
]
];
}
/**
* Get the route key for the model.
*
* @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
}
Tiếp theo chúng ta set up
routes tương tự như việc sử dụng id
cho binding
trong Eloquent documentation:
Route::get('api/posts/{post}', function (App\Post $post) {
return $post->title;
});
Nếu bạn sử dụng SluggableScopeHelpers trait, bạn có thể bind
route bằng slug
mặc định:
public function getRouteKeyName()
{
return $this->getSlugKeyName();
}
2.8.2 Explicit Binding
Bạn cũng có thể sử dụng phương thức RouteServiceProvider::boot
như được miêu tả trong Laravel Documentation để quản lý explicit route model binding
.
public function boot()
{
parent::boot();
Route::model('user', App\User::class);
}
All rights reserved