Tiếp tục với chuỗi bài về Eoloquent ORM trong Laravel. Phần này mình sẽ giới thiệu với mọi người về Eloquent relationship trong Laravel 8.
1. Eloquent Relationship là gì?.
Eloquent Relationship là tính năng trong Laravel nó 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.
VD: Một post sẽ có nhiều comment, hoặc một post chỉ thuộc về một tác giả.
2. Định nghĩa relationship trong model.
Các eloquent relationship được định nghĩ trong model nhưng một phương thức bình thường. Và chúng ta cũng có thể dùng nó để tạo ra các query builder khác một cách nhanh chóng và đơn giản bằng cách sử dụng chúng như method.
Dưới đây sẽ là các kiểu quan hệ trong eloquent.
One To One.
One to one (1-1) là một trong những mỗi quan hệ đơn giản nhất trong database.
VD: 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ệ 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 với mỗi quan hệ 1-1.$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 bảng của model hiện tại cộng với '_id', ví dụ model User thì sẽ làusers_id
.$localKey
là cột chứa dữ liệu để liên kết với bẳng của$relationModel
. Mặc định thì$localKey
sẽ là primary key của model hiện tại.
VD: Chúng ta sẽ định nghĩa mối quan hệ này bằng cách định nghĩa thêm một phương thức có tên là phone
trong User
model như sau:
<?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);
}
}
Khi mình định nghĩa như này thì $foreignKey
ở đây sẽ là user_id
và $localKey
sẽ 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.
VD: Truy vấn dữ liệu trong relation model.
// truy vấn data của table phone của user 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 thôi.
Để định nghĩa mối quan hệ đảo ngược của quan hệ one to one này các bạn sủ dụng phương thức belong
với cú pháp:
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.
VD: Đị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.
Dể định nghĩa mối quan hệ này chúng ta sử dụng phương thức hasMany với các tham số truyền vào tương tự như phương thức hasOne ở trên.
VD: Đị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);
}
}
Ở mối quan hệ này thì khi query đến relationship nó sẽ trả về một collection với các item sẽ là model object.
VD: query đến comment trong post.
use App\Models\Post;
$comments = Post::find(1)->comments;
foreach ($comments as $comment) {
//
}
Như mình đã nói ở trên, các mối quan hệ trong model này còn đóng vai trò như một query builder, nên bạn cũng có thể dùng cách này để apply thêm điều kiện cho query.
VD: 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.
VD:
<?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.
VD:
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.
VD: 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
.
VD:
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.
VD: Một project sẽ có nhiều môi trường và một môi trường 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 môi trường. Ở đây môi trường đóng vai trò làm trung gian.
Các bạn có thể tham khảo ví dụ sau cho dễ hiểu hơn.
Cấu trúc các bảng.
projects
id - integer
name - string
environments
id - integer
project_id - integer
name - string
deployments
id - integer
environment_id - integer
commit_hash - string
Định nghĩa quan hệ:
Để định nghĩa quan hệ này các bạn sử dụng phương thức hasManyThrough
.
VD:
<?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.
Để cho dễ hiểu mình sẽ đi thẳng vào ví dụ.
Cấu trúc các bảng.
users
id - integer
name - string
roles
id - integer
name - string
role_user
user_id - integer
role_id - integer
Ở đây các bạn có thể thấy thì một chú ta sẽ có nhiều users và roles và để lấy ra được các roles của user thì chúng ta sẽ lấy ra các role_user
của user_id
đó và query trong roles table. Mối quan hệ như thế là many to many.
Lúc này để định nghĩa quan hệ này trong model các bạn sử dụng phương thức belongsToMany
.
VD:
<?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);
}
}
Tương tự, bạn có thể định nghĩa mối quan hệ đảo ngược tương tự như 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);
}
}
Trong một số trường hợp, bạn muốn lấy ra thêm data của bảng trung gian, bạn có thể sử dụng attribute pivot
.
VD: lấy ra created_at
của user_role
.
use App\Models\User;
$user = User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
3. 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
.
Nếu không truyền gì vào phương thức này thì Laravel sẽ trả về model object với không có giá trị attribute được thêm vào model object.
VD:
/**
* 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.
VD:
/**
* 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';
});
}
4. Lời kết.
Phần này mình chỉ giới thiệu với mọi người các mối quan hệ cơ bản trong Eloquent ORM và cách query cơ bản trong các mối quan hệ. Ngoài ra, trong eloquent ORM Laravel còn hộ trợ rất nhiều các mối quan hệ phức tạp hơn mà mình không trình bày ở đây vì nó có thể khó khá hiểu với các bạn tại thời điểm này (bạn nào cần có thể tham khảo tại đây).
Đăng ký nhận tin.
Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!
0 Comments