Laravel: Eloquent Relationship là cái qq gì vậy???
Bài đăng này đã không được cập nhật trong 6 năm
(Bài viết chống chỉ định với các thành phần nghiêm túc)
Chào mừng mày đã đến với bài viết: Relationship là cái quái quỷ gì vậy!!!
Giới thiệu qua một chút: Laravel hỗ trợ hệ quản trị cơ sở dữ liệu quan hệ, nếu mày từng dùng Laravel chắc cũng biết đi. Trong đó có Query Builder và Eloquent ORM.
Nói đơn giản dễ hiểu thì Query Builder tương tự như truy vấn SQL bình thường, còn ORM (Object Relational Mapping) tương tự, nhưng nó được "đối tượng hóa" nên có ActiveRecord đẹp, thân thiện và làm giảm tính phức tạp của cú pháp truy vấn. Tuy vậy, để có thể "đối tượng hóa" những quan hệ của một cơ sở dữ liệu thì cũng quá cmn phức tạp. Vì sao à? Vì mấy cái cú pháp đơn giản, dựng sẵn thì không phải của mày (cũng không phải của tao ) nên muốn sử dụng thì phải học thôi. Mà đã học là phải học hết, không thể học kiểu dở dở dang dang. Vì vậy, nếu đã đọc bài này, tao hi vọng mày đọc hết bài, vậy thôi. Hẹn mày ở dòng cuối cùng nhé hehe.
1. Giới thiệu
Trong cơ sở dữ liệu quan hệ, quan trọng nhất chính là mối quan hệ giữa các bảng. Dưới đây là những quan hệ được hỗ trợ bởi Eloquent trong Laravel:
1. One To One
2. One To Many
3. Many To Many
4. Has Many Through
5. Ponymorphic Relations
6. Many To Many Ponymorphic Relations
2. Các relationships
1. One To One
Ví dụ đơn giản: mối quan hệ 1 người có thể có một ngôi nhà.
Để định nghĩa mối quan hệ này, đầu tiên, chúng ta sẽ định nghĩa một hàm house()
trong model User:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function house()
{
return $this->hasOne(House::class);
}
}
Phương thức hasOne sẽ trả về record theo quan hệ 1-1 tương ứng. Để có thể sử dụng mối quan hệ này, ta sẽ sử dụng những hàm đã định nghĩa như một thuộc tính của model:
$house = User::findOrFail(1)->house;
Với việc truy vấn 1 đối tượng (sử dụng find()
hoặc findOrFail()
), ta có thể trỏ trực tiếp tới quan hệ. Tuy nhiên, nếu truy vấn nhiều bản ghi, ta nên sử dụng phương thức with()
để truy vấn hiệu quả hơn với Eager Loading:
$user = House::select([*])->with(['house'])->get();
Như vậy, ta có thể lấy về users kèm theo house tương ứng của chúng.
Eloquent mặc định khóa ngoại cho mối quan hệ là nametable_id. Ví dụ, với hàm house()
trong User, khóa chính là trường id của User, và khóa ngoại là trường 'user_id' của House. Nếu bạn muốn định nghĩa lại, hãy thêm tham số vào phương thức hasOne()
:
hasOne(Model::class, 'foreign_key', 'local_key');
trong đó, 'foreign_key' là tên khóa ngoại, và 'local_key' là tên khóa chính.
Tiếp theo, chúng ta sẽ định nghĩa một hàm user()
trong model House:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class House extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Phương thức user()
sẽ trả về một bản ghi tương ứng theo quan hệ 1-1:
$user = House::findOrFail(1)->user;
Lưu ý, tương tự như hasOne()
, ta hoàn toàn có thể định nghĩa lại các khóa mặc định của hàm belongsTo():
belongsTo(Model::class, 'foreign_key', 'other_key');
Nếu mày thắc mắc là rằng tại sao lại đặt hasOne()
ở model User, và belongsTo()
ở model House, thì lý do là, phải có người thì mới có nhà. Hay nói cách khác đây là mối quan hệ, mỗĩ người có thể có một căn nhà, và mỗi căn nhà thì thuộc về một người.
2. One To Many
Ví dụ: Một công ty có thể có rất nhiều thành viên.
Đầu tiên, ta sẽ định nghĩa bên phía công ty - model Company một hàm members()
:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
public function members()
{
return $this->hasMany(User::class);
}
}
Tiếp theo, bên phía model User, ta sẽ định nghĩa một hàm company()
:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function company()
{
return $this->belongsTo(Company::class);
}
}
Để sử dụng mối quan hệ này, ví dụ, từ phía Company, ta có thể truy vấn bằng cách coi member là một thuộc tính của Company:
$members = Company::find(1)->member;
Lưu ý, tương tự hasOne()
, hasMany()
cũng cho phép thay đổi tên khóa chính và khóa ngoại mặc định:
hasMany(Model::class, 'foreign_key', 'local_key');
3. Many To Many
Quan hệ Many To Many, hay n-n là quan hệ tương đối phức tạp. Tao sẽ cố gắng nói đơn giản nhất có thể. Cơ mà tao cũng không chắc đâu
Ví dụ: Một sinh viên thì có thể đăng kí nhiều môn học, và một môn học thì có thể được đăng kí bởi nhiều sinh viên.
Theo chuẩn N3, để tránh dư thừa dữ liệu thì chúng ta cần có 3 model: User, Subject và Subject_User
Lưu ý: Tại sao lại là Subject_User mà không phải User_Subject? Vì theo quy tắc đặt tên của Laravel Convention (thứ tự alpha-beta), thì Subject đứng trước User
Để định nghĩa quan hệ này, ta cần định nghĩa hàm users()
phía Subject và hàm subjects()
phía User, như sau:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function subjects()
{
return $this->belongsToMany(Subject::class);
}
}
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function users()
{
return $this->belongsToMany(Subject::class);
}
}
Lưu ý, ta có thể định nghĩa lại tên bảng trung gian, khóa ngoại của belongsToMany()
:
belongsToMany(Model::class, 'intermediate_table', 'foreign_key_1', 'foreign_key_2')
Tương tự như 1-1 và 1-n, ta có thể coi subjects và users là các thuộc tính của model để thực hiện truy vấn.
Cách lấy dữ liệu của model trung gian, ở đây là Subject_User:
Sử dụng thuộc tính pivot:
$ids = User::find(1)->pivot->id;
Mặc định, chỉ có các khóa của model tồn tại trong đối tượng pivot. Nếu bảng pivot có nhiều thuộc tính hơn, ta cần chỉ định chúng khi định nghĩa quan hệ bằng hàm withPivot()
:
return $this->belongsToMany(Model::class)->withPivot('column1', 'column2', ...);
Còn nếu muốn lọc quan hệ trả về, chúng ta sẽ sử dụng hàm wherePivot()
, hoặc wherePivotIn()
.
4. Has Many Through
Đây là một cách rất hay, giúp chúng ta tạo ra một liên kết truy vấn thuận lợi cho những mối quan hệ "xa xôi" bắn đại bác cả ngày mới tới. Ví dụ: Một Country có nhiều bài Post thông qua các User. Dưới đây là mẫu về các bảng:
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
Ta có thể thấy, bảng posts không hề có country_id, và country cũng không có liên hệ trực tiếp nào với posts. Dù biết là thông qua User, một Country có thể có nhiều Posts, nhưng làm sao để có thể lấy được posts từ country? Giờ ta sẽ xây dựng một mối quan hệ Has Many Througn để giải quyết vấn đề này. Trong Model Country:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
public function posts()
{
return $this->hasManyThrough(Post::class, User::class);
}
}
Lưu ý, ta có thể định nghĩa lại khóa chính và khóa ngoại của các bảng trong hàm hasManyThrougn()
. Ví dụ như:
return $this->hasManyThrough(Post::class, User::class, 'country_id', 'user_id', 'id');
5. Polymorphic Relations
Đơn giản thì nó là quan hệ đa hình. Tao sẽ lấy luôn ví dụ cụ thể cho dễ hiểu nhé. Tao có 3 bảng: medias, posts và videos. Trong đó posts và videos đều có thể coi là các medias.
posts
id - integer
title - string
content - text
videos
id - integer
title - string
url - string
medias
id - integer
body - text
target_id - integer
target_type - string
Để xây dựng mối quan hệ này, ta sẽ lần lượt xây dựng các model Media, Post và Video:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Media extends Model
{
/**
* Get all of the owning commentable models.
*/
public function target()
{
return $this->morphTo();
}
}
class Post extends Model
{
public function medias()
{
return $this->morphMany(Media::class, 'target');
}
}
class Video extends Model
{
public function medias()
{
return $this->morphMany(Media::class, 'target');
}
}
Mối quan hệ được định nghĩa sử dụng morphOne()
hay morphMany()
phụ thuộc quan hệ giữa các model, ở đây là 1-n.
6. Many To Many Polymorphic Relations
Phức tạp hơn so với Polymorphic Relations, đây là quan hệ đa hình nhiều nhiều. Ví dụ:
posts
id - integer
name - string
videos
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
target_id - integer
target_type - string
Để xây dựng mối quan hệ này, ta cần định nghĩa:
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'target');
}
}
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'target');
}
}
<?php
namespace App\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
public function posts()
{
return $this->morphedByMany(Post::class, 'target');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'target');
}
}
Trên đây là một số chia sẻ của mình về Eloquent: Relationship. Mình xưng hô như vậy vì đơn giản mình chỉ chia sẻ những gì mình biết, chứ mình không dạy ai cả. Mong là bài viết xàm xí này của mình có thể giúp ích cho các bạn. Cảm ơn vì đã đọc đến dòng cuối cùng này.
Tài liệu tham khảo: https://laravel.com/docs/5.5/eloquent-relationships
All rights reserved