20 Laravel Eloquent Tips and Tricks

Lời nói đầu

Xin chào mọi người.Đầu tiên mình cảm ơn các bạn đang đọc bài chia sẻ của mình,và mình tin là những bạn đang đọc bài viết này đều là những bạn đang làm việc với Framework Laravel 😃)).Như các bạn cũng biết thì laravel là Framework rất mạnh của php và được đa số lập trình viên PHP sử dụng.Thực tế Laravel có rất nhiều tính năng hay ho và vô cùng hữu ích giúp cho các lập trình viên có thể giảm tải được thời gian code và làm cho code chặt chẽ hơn.một chức năng vô cùng hữu ích đó là Eloquent ORM, hẳn thì Eloquent ORM đã rất quen thuộc với các bạn đúng không nào, có thế nói đây là tính năng mà bất kỳ project nào sử dụng Laravel cũng phải dùng đến.

ORM là gì?

ORM (Object Relational Mapping) là một kỹ thuật lập trình dùng để chuyển đổi dữ liệu giữa một hệ thống không hướng đối tượng như cơ sở dữ liệu sang hệ thống hướng đối tượng như lập trình hướng đôi tượng trong PHP. Kỹ thuật này tạo ra các đối tượng CSDL ảo có thể được lập trình trong mã nguồn và có nhiều ưu điểm như mã nguồn trở lên rõ ràng và dễ bảo trì, dễ dàng thao tác với dữ liệu và thực hiện việc tối ưu hệ thống thông qua việc sử dụng bộ đệm… Các công việc khó hoặc không thể xử lý ở database layer sẽ được đưa lên lớp ứng dụng. Tuy nhiên hôm nay mình sẽ không đi sâu vào tìm hiểu ORM là gì, mà trong bài này mình sẽ chia sẻ 20 tips rất hay ho mà có thể nhiều bạn không để ý khi sử dụng Eloquent ORM. Bắt đầu nhé.

20 Laravel Eloquent Tips and Tricks

1. Increments and Decrements

bình thường chúng ta sử dụng câu lệnh như sau:

$article = Article::find($article_id);
$article->read_count++;
$article->save();

thay vì sử dụng những câu lệnh này các ban có thể viết như sau:

$article = Article::find($article_id);
$article->increment('read_count');

hoặc

Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // +10
Product::find($produce_id)->decrement('stock'); // -1

2. XorY methods

Eloquent có vài function là sự sự kết hợp của 2 methods theo kiểu "please do X, otherwise do Y ."

findOrFail

bình thường chúng ta sẽ viết như sau:

$user = User::find($id);
if (!$user) { abort (404); }

thay vì vậy các bạn có thể viết:

$user = User::findOrFail($id);

firstOrCreate()

thay vì các bạn sử sự phương thức create như này:

$user = User::where('email', $email)->first();
if (!$user) {
  User::create([
    'email' => $email
  ]);
}

các bạn có thể sử dụng hàm firstOrCreate:

$user = User::firstOrCreate(['email' => $email]);

3.Model boot() method

trong function boot này bạn có thể ghi đề những hành vi mặc định của Eloquent Model

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

4.Relationship with conditions and ordering

bình thường thì chúng ta vẫn định nghĩa quan hệ trong model như sau:

public function users() {
    return $this->hasMany('App\User');    
}

tuy bạn có biết là chúng ra có thể thêm vào câu điều kiện và sắp xếp như sau:

public function approvedUsers() {
    return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}

như ở thì các ta sẽ query được đến các user đã approved và để về thứ tự sắp xếp theo email.

5.Model properties: timestamps, appends etc.

ở đây có vài tham số của một Eloquent Model được coi là thuộc tính của của Model class.kiểu khai báo thường dùng là:

class User extends Model {
    protected $table = 'users';
    protected $fillable = ['email', 'password']; // which fields can be filled with User::create()
    protected $dates = ['created_at', 'deleted_at']; // which fields will be Carbon-ized
    protected $appends = ['field1', 'field2']; // additional values returned in JSON
    protected $primaryKey = 'uuid'; // it doesn't have to be "id"
    public $incrementing = false; // and it doesn't even have to be auto-incrementing!
    protected $perPage = 25; // Yes, you can override pagination count PER MODEL (default 15)
    const CREATED_AT = 'created_at';
    const UPDATED_AT = 'updated_at'; // Yes, even those names can be overridden
    public $timestamps = false; // or even not used at all
}

6. Find multiple entries

Có lẽ các bạn đều biết phương thức find() đúng không? và nó thường được sử dụng như thế này:

$user = User::find(1);

tuy nhiên bạn có biết rằng hàm find() có thể tìm kiếm multiple entries như sau:

$users = User::find([1,2,3]);

7. WhereX

câu truy vấn điều kiện hay được dùng là:

$users = User::where('approved', 1)->get();

cùng với kết quả đó,bạn có thể sử dụng câu lệnh sau:

$users = User::whereApproved(1)->get(); 

bạn có thể thay đổi bất kỳ trường nào và append nó như là một hậu tố của where.và đã có vài phương thức đã được định nghĩa trước liên quan đến date/time

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

hơi khó một chút.nếu có vấn đề như sau: bạn sẽ làm gì nếu bạn có forums topics nhưng bạn muốn sắp xếp chúng theo thứ bởi bài Post mới nhất.Có một yêu cầu khá phổ biến đó là những mài đăng mới nhất sẽ được cho lên đầu.Và các thược hiện sẽ là:
tại Model:

public function latestPost()
{
    return $this->hasOne(\App\Post::class)->latest();
}

Tại Controller:

$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');

9. Eloquent::when() – no more if-else’s

rất nhiều bạn trong chung ta viết câu lệnh điều kiện if-else như sau:

if (request('filter_by') == 'likes') {
    $query->where('likes', '>', request('likes_amount', 0));
}
if (request('filter_by') == 'date') {
    $query->orderBy('created_at', request('ordering_rule', 'desc'));
}

có một cách tố hơn là 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'));
});

nếu bạn thấy nó không ngắn và tối ưu hơn, cách tốt nhất là sử dụng tham sô(parammeters);

$query = User::query();
$query->when(request('role', false), function ($q, $role) { 
    return $q->where('role_id', $role);
});
$authors = $query->get();

10. BelongsTo Default Models

ví dụ bạn có một bài Post thuộc về một Author. thì code trong file blade se là:

{{ $post->author->name }}

nhưng nếu trong trường hợp mà Author này bị xóa, hoặc không được set vì một vài lý do thì bạn sẽ bị lỗi. tất nhiên là chúng ta các cách phòng ngừa là:

{{ $post->author->name ?? '' }}

tuy nhiên bạn có thể làm điều đó trong Eloquent relationship:

public function author()
{
    return $this->belongsTo('App\Author')->withDefault();
}

trong ví dụ trên author() sẽ trả về model App\Author rỗng nếu không có author nào được đính kèm với bài Post.
hơn nữa thì chúng ta có thể gán giá trị thuộc tính mặc định cho Model mặc định

public function author()
{
    return $this->belongsTo('App\Author')->withDefault([
        'name' => 'Guest Author'
    ]);
}

11. Order by Mutator

tưởng tượng rằng bạn có như sau:

function getFullNameAttribute()
{
  return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}

bây giờ. bạn muốn sắp sếp theo full_name

$clients = Client::orderBy('full_name')->get(); // doesn't work

câu lệnh trên sẽ không chạy.
giải pháp ở đây khá đơn giản.chúng ta phải kết quả sau khi đã lấy được

$clients = Client::get()->sortBy('full_name'); // works!

chú ý là tên function hơi khác một chút. không phải là orderBy mà là sortBy.

12. Default ordering in global scope

bạn sẽ làm gì nếu User::all() luôn luôn sắp xếp theo trường name? bạn có thể gán nó cho một global scope.hãy trở lại với hàm boot()

protected static function boot()
{
    parent::boot();

    // Order by name ASC
    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('name', 'asc');
    });
}

13. Raw query methods

thỉnh thoảng chúng ta muôn thêm raw queries vào trong câu lệnh Eloquent. may mắn là chúng ta có các hàm cho nó.

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

đây là cách để tạo bản sao của một bản ghi

$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();

15.Chunk() method for big tables

Không phải chính xác là Eloquent, nhưng mà vẫn rất là mạnh mẽ để xử lý các tập dữ liệu lớn. chúng ta có thể chia nhỏ thành nhiều phần.
Thường chúng ta vẫn hay viết:

$users = User::all();
foreach ($users as $user) {
    // ...

Thay vào đó bạn có thể viết lại như sau:

User::chunk(100, function ($users) {
    foreach ($users as $user) {
        // ...
    }
});

16.Create additional things when creating a model

Chúng ta đều biết đến câu lệnh Artisan này đúng không 😄

php artisan make:model Company

nhưng bạn có biết là chúng ta có 3 flags vô cùng hữu ích để tạo những files có quan hệ với Model

php artisan make:model Company -mcr
  • -m tương ứng là sẽ tao ra file migration
  • -c tương ứng là sẽ tạo file Controller
  • -r tương là sẽ biểu thị rằng Controller được tạo ra là Resource Controller

17.Override updated_at when saving

Bạn có biết rằng phương thức ->save() có thể nhận vào các tham số.Với kết quả nhận được, bạn có thể ignore phương thức update_at của timestamps.

$product = Product::find($id);
$product->updated_at = '2019-01-01 10:00:00';
$product->save(['timestamps' => false]);

chúng ta vừa override updated_at với giá trị được định nghĩa trước.

18.What is the result of an update()?

Các bạn đã bao giờ tự hỏi đoạn code dưới đây sẽ trả về giá trị gì không?

$result = $products->whereNull('category_id')->update(['category_id' => 2]);

ý của mình là chúng ta đang thực hiện chức năng update. nhưng giá trị $result trả về chứa cái gì.
Câu trả lời chính là giá trị của row bị ảnh hưởng của phương thức update này.Nếu như các bạn muốn kiểm tra xem có bao nhiêu row bị ảnh hưởng bởi lệnh update này thì bạn cũng không cần phải gọi bất kỳ hàm nào nữa. update() sẽ trả về giá trị cho bạn.

19. Transform brackets into an Eloquent query

Điều gì sẽ xảy ra khi bạn lết hợp and-or trong câu truy vấn SQL như thế này:

... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)

Làm thế nào để chuyển câu truy vấn trên vào trong Eloquent.Cách dưới đây là một cách biến đổi sai

$q->where('gender', 'Male');
$q->orWhere('age', '>=', 18);
$q->where('gender', 'Female');
$q->orWhere('age', '>=', 65);

Thứ tự thực hiện sẽ sai. Cách biến đổi đúng thì phúc tạp hơn một chút.Chúng ta dùng Closure Function như là sub-queries.

$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

Và cuối cùng.Bạn có thể chuyền vào một mảng các tham số vào orWhere.
Bình thường chúng ta vẫn hay viết như sau:

$q->where('a', 1);
$q->orWhere('b', 2);
$q->orWhere('c', 3);

Thay vì vậy, các bạn có thể viết lại như sau:

$q->where('a', 1);
$q->orWhere(['b' => 2, 'c' => 3]);

Lời cảm ơn

Đến đây mình đã chia sẻ với các bạn 20 Eloquent Trick and Tip. Có thể còn nhiều những TricksTip nữa nhưng với bài chia sẻ này mình hi vọng có thể giúp các bạn tối ưu hơn về các sử dụng câu truy vấn trong Laravel. Cảm ơn các bạn đã theo dõi bài đọc của mình. Thanks You!

Tài liệu tham khảo

https://laravel-news.com/eloquent-tips-tricks


All Rights Reserved