Eloquent Relationship cho phép chúng ta định nghĩ ra các mối quan hệ (relationship) giữa các model với nhau. Từ đó có thể query, làm việc với các model được định nghĩa quan hệ một cách đơn giản.
Các mỗi quan hệ này thực ra là một bản config trên code về cấu trúc link giũa các table trong database
Ví dụ: Một post sẽ có nhiều comment, hoặc một post chỉ thuộc về một tác giả
1. Định nghĩa relationship trong model
# One To One
Ví dụ: Mỗi một User thì chỉ có duy nhất một số điện thoại (phone)
Để định nghĩa mối quan hệ (1-1) này chúng ta dùng phương thức hasOne
với cú pháp như sau:
hasOne($relationModel, $foreignKey, $localKey');
Trong đó:
$relationModel
là model sẽ được link với model hiện tại$foreignKey
là khóa ngoại của table$relationModel
ở trên dùng để liên kết giũa 2 bảng với nhau. Mặc định thì$foreignKey
sẽ là tên table của model hiện tại cộng với ‘_id‘, ví dụ model User thì sẽ làuser_id
.$localKey
là cột chứa dữ liệu để liên kết với table của$relationModel
. Mặc định thì$localKey
sẽ là primary key của model hiện tại
Để xác định mối quan hệ này, chúng ta đặt một method phone
trên User model. Phương thức phone
nên trả về kết quả của 1 phương thức hasOne
trên cơ sở của lớp Eloquent model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Get the phone associated with the user.
*/
public function phone()
{
return $this->hasOne(Phone::class);
}
}
Với ví dụ trên thì $foreignKey
là user_id
và $localKey
là id
Sau khi đã định nghĩa xong relationship cho Model lúc này bạn có thể query đến relation model bằng cách gọi đến phương thức định nghĩa relation như một thuộc tính trong model hiện tại
Ví dụ: Truy vấn dữ liệu trong relation model
// truy vấn data của table phone của table user với id = 1
$phone = User::find(1)->phone;
Bạn cũng có thể định nghĩa được mối quan hệ đảo ngược của quan hệ 1-1 này. Ví dụ một số điện thoại (phone) thì sẽ chỉ thuộc về một User
belongsTo($relatedModel, $foreignKey, $ownerKey);
Trong đó:
$relatedModel
là model của bạn muốn liên kết.$foreignKey
là column của bảng hiện tại sẽ dùng để liên kết. Mặc định $foreignKey sẽ là tên của phương thức cộng với primary key của$relatedModel
.$ownerKey
là column của bảng $relatedModel sẽ dùng để liên kết. Mặc định$ownerKey
là khóa chính của$relatedModel
model.
Ví dụ: Định nghĩa mối quan hệ đảo ngược cho model Phone
và User
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
/**
* Get the user that owns the phone.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
Lúc này query User trong Phone sẽ như sau:
// Lấy user có số điện thoại +84123456789
$user = Phone::where('number', '+84123456789')->user;
# One To Many
Mối quan hệ một nhiều được sử dụng trong trường hợp một model (A) sẽ được link đến một hoặc nhiều model khác (B). Ví dụ một bài post sẽ có rất nhiều comment
Ví dụ: Định nghĩa quan hệ một nhiều giữa model Post
và model Comment
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany(Comment::class);
}
}
Query đến function comment trong model Post
use App\Models\Post;
$comments = Post::find(1)->comments;
foreach ($comments as $comment) {
//
}
Lấy các comment của post có title = foo
$comment = Post::find(1)->comments()
->where('title', 'foo')
->first();
Nếu bạn muốn định nghĩa mối quan hệ đảo ngược cho comment với post trong trường hợp này thì có thể sử dụng như đối với one to one ở trên. Vì lúc này một comment vẫn thuộc về một post
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo(Post::class);
}
}
Và query cũng tương tự như đối với one to one ở trên
use App\Models\Comment;
$comment = Comment::find(1);
return $comment->post->title;
# Has One Through
Đây thực ra cũng là mối quan hệ 1-1 tuy nhiên chúng phải link với nhau thông qua một model khác.
Ví dụ: Một bộ phận
sẽ thuộc về một chiếc xe
và một chiếc xe sẽ thuộc về một người
. Trong trường hợp này bạn muốn kiểm tra xem một bộ phận
thuộc về người
nào đó thì bạn cần phải liên kết với một bảng trung gian trong trường hợp này thì chiếc xe
chính là trung gian.
Để cho dễ hiểu bạn có thểm xem qua data struct và cách định nghĩa như sau:
Cấu trúc của các bảng
mechanics
id - integer
name - string
cars
id - integer
model - string
mechanic_id - integer
owners
id - integer
name - string
car_id - integer
Lúc này để định nghĩa owner của mechanics các bạn có thể sử dụng phương thức hasOneThrough
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner()
{
return $this->hasOneThrough(Owner::class, Car::class);
}
}
Và nếu như các column name của các bạn không theo rule trên thì bạn có thể thiết lập thủ công vào phương thức hasOneThrough
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner()
{
return $this->hasOneThrough(
Owner::class,
Car::class,
'mechanic_id', // Foreign key on the cars table...
'car_id', // Foreign key on the owners table...
'id', // Local key on the mechanics table...
'id' // Local key on the cars table...
);
}
}
# Has Many Through
Mối quan hệ này cũng tương tự như mối quan hệ one to many, chỉ khác là nó phải cần thêm một model thứ 3 để xác định được
Ví dụ: Một project
sẽ có nhiều môi trường
và một environment
sẽ có nhiều lần deploy
. Như vậy để có thể lấy ra được các deploy
của project
bạn phải cần thêm thông tin của environment
. Ở đây environment
đóng vai trò làm trung gian
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
/**
* Get all of the deployments for the project.
*/
public function deployments()
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}
}
Nếu trong trường hợp tên các cột của bạn không theo rule trên thì bạn có thể xác định thủ công vào trong phương thức hasManyThrough
với các biến truyền vào như sau:
class Project extends Model
{
public function deployments()
{
return $this->hasManyThrough(
Deployment::class,
Environment::class,
'project_id', // Foreign key on the environments table...
'environment_id', // Foreign key on the deployments table...
'id', // Local key on the projects table...
'id' // Local key on the environments table...
);
}
}
# Many To Many
Đây là mối quan hệ thường gặp trong các chức năng phân quyền của ứng dụng
Ví dụ: 1 user
sẽ có nhiều roles
và 1 role
cũng sẽ thuộc về nhiều user
Để xác định relationship này, cần thiết phải có 3 bảng: users, roles và role_user. Bảng role_user sẽ chứa 2 column user_id và role_id
users
id - integer
name - string
roles
id - integer
name - string
role_user
user_id - integer
role_id - integer
Quan hệ many-to-many được định nghĩa bằng cách gọi phương thức belongsToMany
dựa trên Eloquent class
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
Một khi các mối quan hệ được xác định, bạn có thể truy cập vào roles
bằng cách truy cập dynamic property:
$user = App\User::find(1);
Giống như tất cả các relationship khác, bạn có thể gọi phương thức roles
và tiếp tục cho thêm vào các query:
$roles = App\User::find(1)->roles()->orderBy('name')->get();
Như đã đề cập trước đó, để xác định tên bảng của bảng tham gia vào relationship, Eloquent sẽ join 2 model liên quan theo thứ tự của bảng chữ cái. Tuy nhiên, bạn cũng có thể ghi đè quy ước này. Bạn có thể làm như vậy bằng cách thêm vào 1 đối số thứ 2 trong phương thức belongsToMany
như sau:
return $this->belongsToMany('Role::class', 'role_user');
Ngoài tùy biến trên, bạn có thể tùy biến các tên cột của các keys bằng cách truyền thêm đối số cho phương thức belongsToMany
. Đối số thứ 3 là tên foreign key mà bạn đang xác định relationship, trong khi đối số thứ 4 là tên foreign key trong model mà bạn đang join đến
return $this->belongsToMany('Role::class', 'role_user', 'user_id', 'role_id');
Định nghĩa Inverse của quan hệ trên
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users()
{
return $this->belongsToMany(User::class);
}
}
Truy xuất vào table trung gian
$user = User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
Nếu bảng pivot của bạn chứa các thuộc tính mở rộng, bạn phải xác định chúng khi xác định các mối quan hệ:
return $this->belongsToMany('Role::class')->withPivot('column1', 'column2');
Nếu bạn muốn bảng pivot của bạn tự động có created_at
và updated_at
, sử dụng các phương thức withTimestamps()
vào trong định nghĩa của mối quan hệ:
return $this->belongsToMany('Role::class')->withTimestamps();
Bạn cũng có thể lọc các kế quả trả về bởi belongsToMany
bằng cách sử dụng phương thức wherePivot
and wherePivotIn
khi định nghĩa các mối quan hệ:
return $this->belongsToMany('Role::class')->wherePivot('approved', 1);
return $this->belongsToMany('Role::class')->wherePivotIn('approved', [1, 2]);
# Has Many Through
Quan hệ “has-many-through” cung cấp một thuận tiện short-cut để 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
model có thể có nhiều Post
model thông qua một User
model trung gian. Trong ví dụ này, bạn có thể dễ dàng lấy tất cả các blog post cho 1 country
Cấu trúc table:
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
Để thực hiện các truy vấn này, Eloquent kiểm tra các country_id
trên bảng user trung gian. Sau khi tìm ra id
của user phù hợp, chúng được sử dụng để truy vấn bảng posts. Bây giờ chúng ta đã xem xét các cấu trúc bảng cho các mối quan hệ, hãy định nghĩa nó trên Country
model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
/**
* Get all of the posts for the country.
*/
public function posts()
{
return $this->hasManyThrough('Post::class', 'User::class');
}
}
Nếu bạn muốn tùy chỉnh các foreign key của relationship, bạn có thể truyền vào các đối số thứ 3 và thứ 4 của phương thức hasManyThrough
. Đối số thứ 3 là foreign key của model trung gian, đối số thứ 4 là foreign key của model cuối cùng và đối số thứ 5 là local key
class Country extends Model
{
public function posts()
{
return $this->hasManyThrough(
'Post::class', 'User::class',
'country_id', 'user_id', 'id'
);
}
}
2. Default Model
Trong các phương thức belongsTo
, hasOne
, hasOneThrough
và morphOne
sử dụng để định nghĩa kiểu quan hệ giữa các model với nhau nó sẽ trả về null
nếu như dữ liệu của quan hệ không tồn tại trong database
Trong trường hợp này laravel có cung cấp cho mọi người định nghĩa thêm giá trị default với phương thức withDefault
/**
* Get the author of the post.
*/
public function user()
{
return $this->belongsTo(User::class)->withDefault();
}
Để xác định thêm attribute mặc định được truyền vào model bạn có thể truyền thêm mảng attribute vào phương thức hoặc một closure trả về mảng giá trị attribute
/**
* Get the author of the post.
*/
public function user()
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest Author',
]);
}
Hoặc
/**
* Get the author of the post.
*/
public function user()
{
return $this->belongsTo(User::class)->withDefault(function ($user, $post) {
$user->name = 'Guest Author';
});
}
Leave a Comment