20 mẹo khi làm việc với Laravel Eloquent
1. Increments và Decrements
Thay vì:
$article = Article::find($article_id);
$article->read_count++;
$article->save();
Bạn có thể rút gọn thành như thế này:
$article = Article::find($article_id);
$article->increment('read_count');
Như thế này cũng ok:
Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // tăng +10
Product::find($produce_id)->decrement('stock'); // giảm -1
2. Hàm X hoặc Y
Eloquent có khá một vài hàm kết hợp hai hàm lại với nhau, kiểu như “vui lòng thực hiện X còn không thì thực hiện Y”. Điều này làm cho code rất clear và cool, các bạn hãy cố gắng sử dụng nếu có thể.
Ví dụ 1 – findOrFail()
:
Thay vì viết như thế này:
$user = User::find($id);
if (!$user) { abort (404); }
Hãy thử viết lại như sau:
$user = User::findOrFail($id);
Ví dụ 2 – firstOrCreate()
:
Thay vì viết như thế này:
$user = User::where('email', $email)->first();
if (!$user) {
User::create([
'email' => $email
]);
}
Chỉ cần viết như thế này:
$user = User::firstOrCreate(['email' => $email]);
3. Model boot() method
Trong Eloquent có 1 nơi huyền diệu (magical place) được gọi là boot(), ở đó bạn có thể ghi đè lên các hành vi mặc định (default behavior):
class User extends Model
{
public static function boot()
{
parent::boot();
static::updating(function($model)
{
// do some logging
// override some property like $model->something = transform($something);
});
}
}
Có lẽ một trong những ví dụ phổ biến nhất là thiết lập một số giá trị trường tại thời điểm tạo đối tượng (model object). Giả sử bạn muốn tạo trường UUID tại thời điểm model được tạo.
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->uuid = (string)Uuid::generate(); // trường uuid được sinh tự động
});
}
4. Relationship with conditions and ordering
Mối quan hệ với điều kiện và sắp xếp.
Đây là một cách điển hình để xác định mối quan hệ:
// liên kết với bảng users
public function users() {
return $this->hasMany('App\User');
}
Nhưng bạn có biết rằng vào thời điểm này, chúng ta đã có thể thêm where hoặc orderBy?
Ví dụ, nếu bạn muốn có mối quan hệ cụ thể cho một số loại người dùng, và cũng được sắp xếp theo email, bạn có thể viết lại như sau:
public function approvedUsers() {
// liên kết với bảng users, user đó phải được approved và sắp xếp các kết quả có được theo email
return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}
5. Model properties: timestamps, appends etc.
Có một vài "tham số (parameters)" của một mô hình Eloquent, dưới dạng các thuộc tính của lớp đó. Các kiểu phổ biến nhất có lẽ là:
class User extends Model {
protected $table = 'users';
protected $fillable = ['email', 'password']; // trường nào có thể fill với User::create()
protected $dates = ['created_at', 'deleted_at']; // trường nào sẽ là Carbon-ized
protected $appends = ['field1', 'field2']; // giá trị bổ sung được trả về trong JSON
}
Chờ chút, có nhiều hơn bạn nghĩ:
protected $primaryKey = 'uuid'; // xác định khóa chính cho bảng, mặc định là id
public $incrementing = false; // bỏ tính năng increment
protected $perPage = 25; // mặc định phân trang là 15, bạn có thể tăng giảm tùy ý
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at'; // mặc định created_at, updated_at có thể thay bằng ngay_tao, ngay_cap_nhat
public $timestamps = false; // không sử dụng timestamp
Và có nhiều hơn nữa, mình đã liệt kê những điều thú vị nhất, để biết thêm hãy kiểm tra code của lớp mô hình trừu tượng mặc định và kiểm tra tất cả các Traits được sử dụng (HasAttributes, HasEvents, HasGlobalScopes, HasRelationships, HasTimestamps ...).
6. Find multiple entries - tìm nhiều entries
Mọi người đều biết phương thức find()
, đúng không?
$user = User::find(1);
Mình khá ngạc nhiên khi ít người biết rằng nó có thể chấp nhận nhiều ID như một mảng:
$users = User::find([1,2,3]);
7. WhereX
Có một cách tao nhã hơn để làm điều này:
$users = User::where('approved', 1)->get();
thành điều này:
$users = User::whereApproved(1)->get();
Vâng, bạn có thể thay đổi tên của bất kỳ trường nào và thêm nó vào như một hậu tố "where" và nó sẽ hoạt động bằng ma thuật. Ví dụ: status -> whereStatus, user_id -> whereUserId
Ngoài ra, có một số phương pháp được xác định trước trong Eloquent, liên quan đến ngày / giờ:
User::whereDate('created_at', date('Y-m-d'));
User::whereDay('created_at', date('d'));
User::whereMonth('created_at', date('m'));
User::whereYear('created_at', date('Y'));
8. Order by relationship
Một "mẹo nhỏ" phức tạp hơn một chút. Điều gì sẽ xảy ra nếu bạn có 1 chủ đề (topic) trên diễn đàn nhưng muốn sắp xếp chúng theo bài đăng mới nhất? Yêu cầu khá phổ biến trong các diễn đàn với các chủ đề cập nhật mới nhất ở trên cùng, phải không?
Đầu tiên, mô tả mối quan hệ riêng biệt cho bài đăng mới nhất về chủ đề:
public function latestPost()
{
return $this->hasOne(\App\Post::class)->latest();
}
Và sau đó, trong controller, chúng ta có thể thử viết như sau:
$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');
9. Eloquent::when() – no more if-else’s
Nhiều người trong chúng ta viết các truy vấn có điều kiện với "if-else", trông giống như thế này:
// nếu lọc bởi likes
if (request('filter_by') == 'likes') {
$query->where('likes', '>', request('likes_amount', 0));
}
// nếu lọc bởi date
if (request('filter_by') == 'date') {
$query->orderBy('created_at', request('ordering_rule', 'desc'));
}
Nhưng có một cách tốt hơn - khi sử dụng when():
$query = Author::query();
$query->when(request('filter_by') == 'likes', function ($q) {
return $q->where('likes', '>', request('likes_amount', 0));
});
$query->when(request('filter_by') == 'date', function ($q) {
return $q->orderBy('created_at', request('ordering_rule', 'desc'));
});
Có thể trông không ngắn hơn hoặc thanh lịch hơn, nhưng mạnh nhất ở đây là khi truyền các tham số động, câu query sẽ trở nên rất gọn gàng:
$query = User::query();
// thay vì phải viết nhiều if, else thì chỉ cần 1
$query->when(request('role', false), function ($q, $role) {
return $q->where('role_id', $role);
});
$authors = $query->get();
10. BelongsTo Default Models
Giả sử bạn có Post
belonging to Author
và trong code trong file Blade như sau:
{{ $post->author->name }}
Nhưng nếu tác giả bị xóa hoặc không được đặt vì lý do nào đó thì sao? Bạn sẽ nhận được một lỗi, một cái gì đó giống như “property of non-object”.
Tất nhiên, bạn có thể ngăn chặn nó như thế này:
{{ $post->author->name ?? '' }}
Nhưng bạn có thể làm điều đó trên Eloquent relationship level:
public function author()
{
return $this->belongsTo('App\Author')->withDefault();
}
Trong ví dụ, author()
relation sẽ trả về đối tượng App\Author
rỗng, nếu không có tác giả nào được đính kèm vào bài viết.
Hơn nữa, chúng ta có thể gán giá trị thuộc tính mặc định cho mô hình mặc định đó.
public function author()
{
return $this->belongsTo('App\Author')->withDefault([
'name' => 'Guest Author' // tên tác giả mặc định
]);
}
11. Order by Mutator
Hãy tưởng tượng bạn có điều này:
function getFullNameAttribute()
{
return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}
Bây giờ, bạn muốn sắp xếp theo full name ư? Nó sẽ không chạy đâu:
$clients = Client::orderBy('full_name')->get(); // báo lỗi
Giải pháp rất đơn giản. Chúng ta cần sắp xếp kết quả sau khi lấy được chúng.
$clients = Client::get()->sortBy('full_name'); // works!
Lưu ý rằng tên hàm là khác nhau - nó không phải orderBy, nó là sortBy.
12. Default ordering in global scope
Điều gì sẽ xảy ra nếu bạn muốn có User::all()
luôn được sắp xếp theo 1 trường nào đó chẳng hạn name? Bạn có thể gán vào global scope. Hãy quay trở lại phương thức boot() lúc nãy:
protected static function boot()
{
parent::boot();
// Order by name ASC
static::addGlobalScope('order', function (Builder $builder) {
$builder->orderBy('name', 'asc');
});
}
Xem kỹ hơn Query Scopes ở đây.
13. Raw query methods
Đôi khi, chúng ta cần thêm các truy vấn thô vào các câu lệnh Eloquent. May mắn thay, có chức năng cho điều đó.
// whereRaw
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();
// havingRaw
Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();
// orderByRaw
User::where('created_at', '>', '2016-01-01')
->orderByRaw('(updated_at - created_at) desc')
->get();
14. Replicate: make a copy of a row
Ngắn gọn, không cần giải thích nhiều, đây là cách tốt nhất để tạo bản sao 1 hàng trong cơ sở dữ liệu:
$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();
15. Chunk() method for big tables
Chính xác thì không liên quan đến Eloquent , thiên về Collection hơn, nhưng vẫn mạnh mẽ - để xử lý các tập dữ liệu lớn, bạn có thể chia nhỏ (chunk) chúng thành nhiều phần.
Thay vì viết như thế này:
$users = User::all();
foreach ($users as $user) {
// ...
}
Bạn có thể làm thế này:
// lấy 100 dòng ra xử lý, xong rồi lấy tiếp ...
User::chunk(100, function ($users) {
foreach ($users as $user) {
// ...
}
});
16. Create additional things when creating a model
Tất cả chúng ta đều biết lệnh Artisan này:
php artisan make:model Company
Nhưng bạn có biết có ba flag (-- hoặc -) hữu ích để tạo các file có liên quan đến model không?
php artisan make:model Company -mcr
- -m tạo cho file migration
- -c tạo luôn controller
- -r controller được tạo ở trên là resource controller
Nhất cử mà 1 đống tiễn phải không =))
17. Override updated_at when saving
Bạn có biết rằng -> phương thức save() có thể chấp nhận các tham số không? Kết quả là, chúng ta có thể yêu cầu nó “bỏ qua (ignore)” chức năng mặc định updated_at để điền vào mong muốn. Xem này:
// mặc định laravel sẽ dùng updated_at là thời gian hiện tại 2018-06-13 10:00:00
$product = Product::find($id);
$product->updated_at = '2019-01-01 10:00:00';
$product->save(['timestamps' => false]);
Ở đây, mình sẽ ghi đè update_at mặc định bằng ngày tháng được xác định trước của mình.
18. What is the result of an update()?
Đã bao giờ bạn tự hỏi đoạn code này có thực sự trả về?
$result = $products->whereNull('category_id')->update(['category_id' => 2]);
Ý mình là, có những update được thực hiện trong cơ sở dữ liệu, nhưng đó là gì?
Câu trả lời là hàng bị ảnh hưởng. Vì vậy, nếu bạn cần kiểm tra xem có bao nhiêu hàng bị ảnh hưởng, bạn không cần phải gọi bất kỳ thứ gì khác - phương thức update() sẽ trả lại số này cho bạn.
19. Transform brackets into an Eloquent query
Điều gì sẽ xảy ra nếu bạn có and-or trộn lẫn trong truy vấn SQL của bạn, ví dụ như sau:
... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)
Làm thế nào để dịch nó thành Eloquent? Đây là cách sai:
$q->where('gender', 'Male');
$q->orWhere('age', '>=', 18);
$q->where('gender', 'Female');
$q->orWhere('age', '>=', 65);
Thứ tự sẽ không đúng. Cách đúng là phức tạp hơn một chút, sử dụng các hàm đóng như các truy vấn con:
$q->where(function ($query) {
$query->where('gender', 'Male')
->where('age', '>=', 18);
})->orWhere(function($query) {
$query->where('gender', 'Female')
->where('age', '>=', 65);
})
20. orWhere with multiple parameters
Cuối cùng, bạn có thể chuyển một mảng các tham số vào orWhere() mà không cần phải viết rời rạc.
Cách “thông thường”:
$q->where('a', 1);
$q->orWhere('b', 2);
$q->orWhere('c', 3);
Bạn có thể làm như sau:
$q->where('a', 1);
$q->orWhere(['b' => 2, 'c' => 3]);
Mình chắc chắn có nhiều bí ấn đáng giá hơn, nhưng mình hy vọng ít nhất một vài trong số đó là những điều mới mẻ đối với bạn!
Nguồn: https://laravel-news.com/eloquent-tips-tricks (Chung Nguyễn blog dịch)
Ủng hộ Chung Nguyễn Blog
Chung Nguyễn Blog sử dụng FlashVPS - Dịch vụ quản trị máy chủ chuyên nghiệp để quản lý VPS
#FlashVPS là dịch vụ cloud panel trên nền tảng web hỗ trợ khách hàng:
- * Quản lý máy chủ số lượng nhiều
- * Không có kinh nghiệm quản lý máy chủ
- * Thích sử dụng giao diện web đơn giản, trực quan hơn terminal
- * Quá nhàm chán với việc ghi nhớ và lặp lại việc gõ các câu lệnh
- * Muốn tự động hóa mọi thao tác
- * Muốn tiết kiệm thời gian quản trị máy chủ
- * Muốn tiết kiệm tiền bạc, nhân lực quản trị máy chủ 👉 https://flashvps.dev
Các bài viết trên website thường xuyên được đăng tải và cập nhật trên trang Facebook Chung Nguyễn Blog hãy tặng cho Chung một LIKE nhé! Mãi yêu các bạn!
813 👍
Bình luận