07/09/2018, 17:55

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 ...

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

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('AppUser')->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(AppPost::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('AppAuthor')->withDefault();
}

Trong ví dụ, author() relation sẽ trả về đối tượng AppAuthor 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('AppAuthor')->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)

Bài liên quan

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 ...

Bùi Văn Nam viết 17:55 ngày 07/09/2018

Một số lỗi cơ bản gặp khi làm việc với laravel framework

RuntimeException in EncryptionServiceProvider.php line 29: No supported encrypter found. The cipher and / or key length are invalid in EncryptionServiceProvider.php line 29 at EncryptionServiceProvider->IlluminateEncryption{closure}(object(Application), array()) in ...

Hoàng Hải Đăng viết 16:09 ngày 12/08/2018

Những điều cần lưu ý khi làm việc với người Nhật Bản

Nhắc đến Nhật Bản, xứ sở Phù Tang xinh đẹp, ta sẽ hình dung ra một thiên đường. Đó là nơi có những khu rừng xanh ngát, có ngọn núi Phú Sĩ cao vời vợi và những cánh đồng hoa khoe sắc quanh năm. Đó là nơi có những tòa nhà chọc trời và nền công nghiệp phát triển đứng nhất nhì thế giới. Và hơn hết, ...

Tạ Quốc Bảo viết 14:22 ngày 12/08/2018

Vài thủ thuật nhỏ khi làm việc với Google Sheets

Một trong những task quan trọng của QA là quản lý documents, số liệu TCs để report, tracking Schedule ... Bạn chắc hẳn sẽ gặp tình trạng lặp đi lặp lại thao tác cập nhật nếu số liệu thay đổi hoặc bất cập trong quá trình sử dụng Google Sheets. Sau đây mình xin chia sẻ đến các bạn 1 số thủ thuật ...

Tạ Quốc Bảo viết 14:21 ngày 12/08/2018

MỘT SỐ LƯU Ý KHI LÀM VIỆC VỚI ACTIVE RECORD MIGRATIONS

Ruby on Rails database migrations là một giải pháp giúp cải thiện một vấn đề thực tế mà các developer phải đối mặt đó là: Làm thế nào để thay đổi database script một cách đáng tin tưởng để có thể nhân rộng trên môi trường development của team hoặc triển khai lên production server tại thời điểm ...

Hoàng Hải Đăng viết 13:47 ngày 12/08/2018
0