Laravel Eloquent ORM [Part 2]

Bài viết này sẽ đi sâu về Relationships, về các thao tác cơ bản trên Eloquent, các bạn có thể tham khảo tại link: https://viblo.asia/vu.huy.tuan/posts/1ZnbRlr3G2Xo.

Các bảng trong cơ sở dữ liệu của chúng ta luôn có liên kết với nhau, việc khai báo các liên kết sẽ giúp chúng ta giảm thiểu số lượng truy vấn không cần thiết đồng thời giúp chúng ta đảm bảo được tính toàn vẹn của cơ sở dữ liệu.

Laravel cũng như các framework khác, nó hỗ trợ khá nhiều kiểu liên kết như:

  • One To One
  • One To Many
  • Many To Many
  • Has Many Through
  • Polymorphic Relations
  • Many To Many Polymorphic Relations

1. One To One

one-to-one là một loại liên kết rất cơ bản, ví dụ 1 User có 1 Phone, chúng ta định nghĩa mối liên kết này trong cơ sở dữ liệu như sau:

class User extends Model {
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

Tham số đầu tiên trong phương thức hasOne là tên của model liên kết. Sau khi chúng ta định nghĩa liên kết như ở trên, chúng ta có thể sử dụng chúng như một thuộc tính động:

$phone = User::find(1)->phone;

Truy vấn SQL tương ứng với câu lệnh trên sẽ là:

select * from users where id = 1

select * from phones where user_id = 1

Chú ý rằng Eloquent sử dụng khóa ngoài của liên kết dựa theo tên của model, ví dụ với liên kết giữa 2 model UserPhone, khóa ngoài tương ứng sẽ là user_idphone_id. Nếu bảng của chúng ta có khóa ngoài có tên khác với định nghĩa ở trên, chúng ta cần phải khai báo chính xác khóa ngoài của liên kết giữa 2 bảng như sau:

return $this->hasOne('App\Phone', 'foreign_key');

return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

Để định nghĩa liên kết ngược lại liên kết 1 user có 1 phone, chúng ta sử dụng belongsTo để định nghĩa liên kết 1 phone thuộc 1 user:

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

2. One To Many

Một ví dụ đơn giản cho liên kết "one-to-many" là 1 post có nhiều comment

class Post extends Model {
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

Bây giờ chúng ta có thể truy vấn tới các comments trong 1 post dựa trên thuộc tính động như sau:

$comments = Post::find(1)->comments;

Chúng ta có thể thêm các điều kiện trong truy vấn tìm comments ở trên:

$comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();

Cũng tương tự với one-to-one, nếu khóa ngoài và khóa trong không giống như format mặc định của laravel, chúng ta cần phải khai báo nó trong định nghĩa:

return $this->hasMany('App\Comment', 'foreign_key');

return $this->hasMany('App\Comment', 'foreign_key', 'local_key');

Để định nghĩa mối quan hệ ngược lại của mối quan hệ one-to-many, chúng ta sử dụng belongsTo:

class Comment extends Model {
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

3. Many To Many

Mối quan hệ many-to-many là một kiểu quan hệ phức tạp hơn. Một ví dụ về một mối quan hệ như là một người sử dụng (user) với nhiều vai trò (role), nơi mà vai trò (role) cũng được chia sẻ bởi người dùng (user) khác. Ví dụ, nhiều người sử dụng có thể có vai trò Admin. Ba bảng cơ sở dữ liệu cần thiết cho mối quan hệ này: User, Rolerole_user.

Chúng ta có thể định nghĩa many-to-many bằng phương thức belongsToMany:

class User extends Model {
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}

Bấy giờ chúng ta có thể lấy roles từ model User:

$roles = User::find(1)->roles;

Tương tự với các liên kết đã định nghĩa ở trên, nếu tên bảng liên kết không như format mặc định của laravel, chúng ta có thể truyền nó vào như tham số thứ 3:

return $this->belongsToMany('App\Role', 'user_roles');

return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');

Chúng ta cũng có thể định nghĩa liên kết ngược lại trên model Role:

class Role extends Model {
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

4. Has Many Through

Mối quan hệ "has-many-through" cung cấp một shortcut thuận tiện cho việc truy cập vào các mối quan hệ xa thông qua một mối quan hệ trung gian. Ví dụ, một Country có thể có nhiều Post thông qua model User.

countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string

Mặc dù bảng posts không chứa cột country_id, nhưng liên kết hasManyThrough cho phép chúng ta lấy dữ liệu về posts của một country thông qua `$country->posts'.

class Country extends Model {
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

Chúng ta có thể khai báo đầy đủ hơn như sau:

class Country extends Model {
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
    }
}

5. Polymorphic Relations

Quan hệ đa hình (Polymorphic relations) cho phép một model thuộc về nhiều hơn một model khác dựa vào một liên kết duy nhất. Ví dụ, bạn có thể có một model Photo thuộc về một trong hai model Staff hoặc model Order. Chúng ta sẽ xác định mối quan hệ này như sau:

class Photo extends Model {
    public function imageable()
    {
        return $this->morphTo();
    }
}

class Staff extends Model {
    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }
}

class Order extends Model {
    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }
}

Bây giờ chúng ta có thể lấy ra các photos của một staff hoặc của một order:

$staff = Staff::find(1);

foreach ($staff->photos as $photo)
{
    //
}

Truy xuất ngược của mối quan hệ đa hình. Chúng ta có thể truy xuất ngược lại staff hoặc order từ photo:

$photo = Photo::find(1);

$imageable = $photo->imageable;

Cấu trúc bảng dữ liệu của mối quan hệ đa hình

staff
    id - integer
    name - string

orders
    id - integer
    price - integer

photos
    id - integer
    path - string
    imageable_id - integer
    imageable_type - string

6. Many To Many Polymorphic Relations

Mở rộng hơn của mối quan hệ đa hình đã được nói ở trên, chúng ta có mối quan hệ many-to-many Ví dụ: một PostVideo model có thể chia sẻ một mối quan hệ đa hình tới một Tag model. Cấu trúc bảng dữ liệu sẽ được khai báo như sau:

posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string

Tiếp theo, chúng ta khai báo các quan hệ trong các model. Cả model PostVideo đều có môt liên kết morphToMany thông qua model tags:

class Post extends Model {

    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }

}

Model Tag cần phải định nghĩa 1 phương thức cho mỗi liên kết của nó:

class Tag extends Model {

    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }

    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }

}

All Rights Reserved