-
Dịch vụ chính
Applications
Tìm hiểu Eloquent trong Laravel (phần 2): Relationship 0 (0)
Tìm hiểu Eloquent trong Laravel (phần 2): Relationship 0 (0)
Tìm hiểu Eloquent trong Laravel (phần 2): Relationship
Xin chào anh em, lâu lắm rồi mình lại mới ngồi viết series Laravel và những điều thú vị. Thì trong bài viết lần trước về tìm hiểu Eloquent , mình đã giới thiệu cho các bạn thế nào là Eloquent và cách sử dụng nó trong project Laravel. Các bạn thấy thú vị khi đọc bài viết đó chứ, bài viết hôm nay mình sẽ đi tìm hiểu về Relationships – nó hỗ trợ rất nhiều khi bạn truy vấn giữa các bảng với nhau. Nào chúng mình cùng bắt tay đi tìm hiểu nào.
1 Các mối quan hệ trong Laravel
1.1 One To One
Có thể nói đây là mối quan hệ đơn giản nhất trong các mối quan hệ, vì nó rất dễ hiểu ta có một cái này chỉ tưởng ứng với một cái kia duy nhất và ngược lại.
<?php
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
User
extends
Model
{
public
function
account()
{
return
$this->hasOne(Account
::class);
}
}
Khi bạn truy vấn để lấy ra user có role gì thì bạn chỉ cần truy vấn như sau
$user
= User
::findOrFail(1);
$role
=
$user->account;
Ví dụ trên đó là trong trường hợp bảng accounts
nhận khóa ngoại tham chiếu là user_id
, nếu như các bạn không đặt theo quy ước khóa ngoại tham chiếu của Laravel quy định: <tên_bang_bỏ_s>_id
thì các bạn muốn lấy được quan hệ đúng các bạn phải thêm các tham số trong lúc định nghĩa quan hệ trong model
. Ví dụ khóa ngoại tham chiếu từ bẳng users
đến bảng accounts
là user_id
, bây giờ các bạn thích là id_user
thì các bạn phải thêm tham số thứ ba như sau
public
function
account()
{
return
$this->hasOne(Account
::class,
'id_user');
}
Ngoài ra, nếu bạn muốn relationship sử dụng các column khác với id
trong bảng users
thì chúng ta có thể thêm đối số thứ 3 nhé.
1.1.1 Inverse
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Account
extends
Model
{
public
function
user()
{
return
$this->belongsTo(User
::class);
}
}
Trong ví dụ mình đưa ra ở trên, Account
model sẽ match cột user_id
với giá trị id
của User
. Tuy nhiên nếu như khóa ngoại tham chiếu trên Account
không phải là user_id
thì chúng ta có thể tùy chỉnh như sau:
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Account
extends
Model
{
public
function
user()
{
return
$this->belongsTo(User
::class,
'foreign_key');
}
}
Nếu như đối chiếu với bảng users
không phải là cột id
thì bạn cũng thêm tham số như sau nhé
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Account
extends
Model
{
public
function
user()
{
return
$this->belongsTo(User
::class,
'foreign_key',
'other_key');
}
}
1.2 One To Many
Nhìn cái tên chắc hẳn các bạn cũng đã mường tượng ra như nào rồi đúng không nhi. Có thể nói nôm na như sau: một cái này sẽ có nhiều cái kia, ngược lại một cái kia sẽ thuộc về một cái này ). Nó cũng tương tự như quan hệ One to One ở trên thôi, nào cùng đi vào ví dụ để chúng mình hiểu hơn nhé.
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
User
extends
Model
{
public
function
posts()
{
return
$this->hasMany(Post
::class);
}
}
Vậy nhìn vào ví dụ trên ta sẽ thấy một user
sẽ có nhiều post
, nếu chúng ta viết như trên thì Eloquent
sẽ tự hiểu khóa ngoại ở bảng posts
đó chính là user_id
nhé. Cũng tương tự như quan hệ One to One
ở trên thì chúng ta cũng có thể truyền được tham số thứ 2 và thứ 3 khi chúng ta không đặt đúng theo convention của Eloquent mà Laravel đã quy định nhé.
public
function
posts()
{
return
$this->hasMany(Post
::class,
'foreign_key');
}
public
function
posts()
{
return
$this->hasMany(Post
::class,
'foreign_key',
'local_key');
}
Và ví dụ khi chúng ta muốn lấy ra user
có id là 1
có những bài post
nào thì chúng ta sẽ dùng câu lệnh sau nhé
$posts
= User
::findOrFail(1)->posts;
1.2.1 Inverse
Khi chúng ta định nghĩa một bài post
này thuộc về user
nào đã viết thì chúng ta định nghĩa như sau nhé
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Post
extends
Model
{
public
function
user()
{
return
$this->belongsTo(User
::class);
}
}
Ở ví dụ trên , Eloquent
đã match user_id
ở bảng posts
với id
ở bảng users
. Tuy nhiên nếu như khóa ngoại ở posts
không phải là user_id
thì chúng ta cũng truyền tham số thứ 2 và nếu như ở bảng users
không lấy trường id
làm khóa chính thì bạn truyền thêm tham số thứ 3 vào function định nghĩa quan hệ nhé.
public
function
user()
{
return
$this->belongsTo(User
::class,
'foreign_key',
'other_key');
}
1.3 Many To Many
Quan hệ này thì trông cái tên cũng có vẻ phức tạp hơn hai quan hệ trên mình đề cập, thôi thì cứ vào ví dụ cho nó dễ hiểu nhé. Bài toán đặt ra là như này một product
thì sẽ nằm trong có nhiều order
, ngược lại một order
có thể có nhiều product
. Chúng mình cần có 3 bảng để biểu diễn cho quan hệ này: orders
, products
, order_product
. Bây giờ mình sẽ đi định nghĩa quan hệ nhé.
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Order
extends
Model
{
public
function
products()
{
return
$this->belongsToMany(Product
::class);
}
}
Khi chúng ta muốn lấy ra dữ liệu một order
xem có bao nhiêu product
thì chúng ta sẽ viết như sau:
$products
= Order
::findOrFail(1)->products;
Các bạn chú ý, nếu như bảng trung gian trong trường hợp này nó sẽ đặt theo quy ước của Laravel đó chính là order_product
, đặt theo alpha nhé. Nếu như trong trường hợp bảng trung gian có tên là khác vd như product_order
thì các bạn phải thêm tham số thứ 2 trong function định nghĩa quan hệ nhé
public
function
products()
{
return
$this->belongsToMany(Product
::class,
'product_order');
}
Và nếu như hai cột liên kết khác với quy ước của Laravel thì chúng ta có thể thêm tham số thứ 3 và thứ 4. Trong đó tham số thứ 3 là tên khóa ngoại của model mà chúng ta đang định nghĩa quan hệ, tham số thứ 4 là tên khóa ngoại của model mà chúng ta đang joining
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Order
extends
Model
{
public
function
products()
{
return
$this->belongsToMany(Product
::class,
'product_order',
'order_id',
'product_id');
}
}
1.3.1 Inverse
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Product
extends
Model
{
public
function
orders()
{
return
$this->belongsToMany(Order
::class);
}
}
Tương tự bạn cũng có thể truyền tham số thứ 2, 3, 4 vào trong hàm định nghĩa quan hệ như sau
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Product
extends
Model
{
public
function
orders()
{
return
$this->belongsToMany(Order
::class,
'product_order',
'product_id',
'order_id');
}
}
1.3.2 Truy cập vào bảng trung gian của quan hệ Many to Many
Như mình đã nói ở trên thì khi làm việc với quan hệ Many to Many
thì chúng ta cần phải định nghĩa ra bảng trung gian. Eloquent cũng cấp một vài cách để tương tác với bảng trung gian đó. Chúng ta cùng xem qua ví dụ sau đây nhé
$order
= Order
::findOrFail(1);
foreach
($order->products
as
$item)
{
// Truy cập vào các trường trong bảng trung gian
echo
$item->pivot->product_id;
}
Các bạn chú ý nhé theo mặc định trong Laravel, bảng trung gian product_order
này sẽ chỉ lấy ra được các trường order_id, product_id, created_at, updated_at
. Nếu như trong bảng trung gian này các bạn định nghĩa thêm trường amount
chẳng hạn, nếu muốn lấy ra được trong khi truy vấn các bạn phải dùng cú phap withPivot('column1', 'column2')
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Order
extends
Model
{
public
function
products()
{
return
$this->belongsToMany(Product
::class)->withPivot('amount');
}
}
Nếu bạn muốn khi thêm bản ghi mới trong bảng trung gian này thì 2 trường created_at, updated_at
tự động fill giá trị thì các bạn định nghĩa thêm withTimestamps()
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Order
extends
Model
{
public
function
products()
{
return
$this->belongsToMany(Product
::class)->withTimestamps();
}
}
1.3.3 Customizing thuộc tính pivot
Nếu như các bạn đã quá nhàm chán khi chúng ta muốn truy cập vào bảng tạm thông qua thuộc tính pivot
thì giờ đây chúng ta có thể thay đổi cái tên đó bằng cứ pháp như sau
public
function
products()
{
return
$this->belongsToMany(Product
::class)->as('detail')->withTimestamps();
}
Lúc đó chúng ta sẽ truy cập được như sau
$order
= Order
::findOrFail(1);
foreach
($order->products
as
$item)
{
// Truy cập vào các trường trong bảng trung gian
echo
$item->detail->product_id;
}
1.3.4 Filter quan hệ thông qua bảng trung gian
Chúng ta có thể filter được kết quả thộng qua bảng trung gian. Ví dụ như chúng ta muốn lấy ra nhưng order mà có detail amount
lớn hơn 3 chẳng hạn thì chúng ta sẽ định nghĩa trong hàm quan hệ như sau
public
function
products()
{
return
$this->belongsToMany(Product
::class)->wherePivot('amount',
'>',
3);
}
// hoặc
public
function
products()
{
return
$this->belongsToMany(Product
::class)->wherePivotIn('amount',
[2,5]);
}
1.3.5 Custom bảng trung gian
Nếu như chúng ta muốn quan hệ Many to Many
dùng theo một model mà ta tự định nghĩa nào đó, không theo quy tắc mà Laravel định nghĩa ra thì chúng ta sẽ làm như sau
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Order
extends
Model
{
public
function
products()
{
return
$this->belongsToMany(Product
::class)->using(Detail
::class);
}
}
Và khi định nghĩa Detail
model thì chúng ta nhớ extend Illuminate\Database\Eloquent\Relations\Pivot
vào nhé
<?php
namespace
App;
use
Illuminate\Database\Eloquent\Relations\Pivot;
class
Detail
extends
Pivot
{
//
}
Thật là dễ dàng đúng không nào
1.4 Has One Through
Đây là quan hệ kết nối các model thông qua một model trung gian. Ví dụ luôn cho dễ hiểu nè
users
: id
, supplier_id
suppliers
: id
history
: id
, user_id
Chúng ta miêu tả quan hệ như sau: Supplier
model có thể truy cập vào History
thông qua User
model.
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Supplier
extends
Model
{
public
function
userHistory()
{
return
$this->hasOneThrough(History
::class, User
::class);
}
}
Tham số đầu tiên đó chính là model mà chúng ta muốn truy cập đến. Tham số thứ 2 là tên của model trung gian. Và cũng như vậy nếu như chúng ta muốn custom key của quan hệ thì chúng ta phải truyền tham số thứ 3, 4 vào hàm định nghĩa quan hệ nhé
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Supplier
extends
Model
{
public
function
userHistory()
{
return
$this->hasOneThrough(
History
::class,
User
::class,
'supplier_id',
// khóa ngoại ở bảng trung gian users
'user_id',
// khóa ngoại ở bảng đích histories
'id',
// khóa chính ở bảng suppliers
'id'
// khóa chính ở bảng users
);
}
}
Và trong model User
và model History
ta cũng định nghĩa bình thường như sau
// User.php
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
User
extends
Model
{
public
function
supplier()
{
return
$this->belongsTo(Supplier
::class);
}
}
// Supplier.php
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Supplier
extends
Model
{
public
function
user()
{
return
$this->hasOne(User
::class);
}
}
1.5 Has Many Through
Đọc cái tên quan hệ các bạn cũng có thể thấy được là các bảng quan hệ với nhau sẽ phải thông qua một cái gì đó đúng không. Đúng rồi đấy, ví dụ chúng ta có 3 bảng như sau
users
: id
, name
, team_id
.
teams
: id
, name
.
goals
: id
, user_id
, number_of_goals
.
Ta thấy như sau:
Team
có nhiềuUser
User
có nhiềuGoal
Chúng ta không thể lưu trực tiếp goal_id
trong bảng teams
được bởi vì chúng ta đã lưu team_id
ở trong bảng users
. Vậy bây giờ câu hỏi đặt ra là có bao nhiêu Goal
của một Team
, chúng ta phải thông qua (through) User
model thì chúng ta mới biết được. Chúng ta sẽ định nghĩa như sau:
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Team
extends
Model
{
public
function
goals()
{
return
$this->hasManyThrough(Goal
::class, User
::class);
}
}
Hoặc cụ thể hơn nữa các bạn sẽ truyền các tham số thứ 3, 4, 5,6 vào trong hàm định nghĩa quan hệ như sau
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Team
extends
Model
{
public
function
goals()
{
return
$this->hasManyThrough(
Goal
::class,
User
::class
'team_id',
// khóa ngoại trên bảng users
'user_id',
// khóa ngoại trên bảng goals
'id',
// khóa chính trên bảng teams
'id'
// khóa chính trên bảng users
);
}
}
Ở User
model chúng ta sẽ định nghĩa như sau
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
User
extends
Model
{
public
function
goals()
{
return
$this->hasMany(Goal
::class);
}
public
function
team()
{
return
$this->belongsTo(Team
::class);
}
}
Goal
model
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Goal
extends
Model
{
public
function
user()
{
return
$this->belongsTo(User
::class);
}
}
Và khi chúng ta muốn truy vấn thì chúng ta vẫn làm như bình thường thôi
$team
= Team
::findOrFail(1);
$goals
=
$team->goals()->get();
...
@
foreach($goals
as
$goal)
<p
>{{
$goal->number_of_goals
}}</p
>
@
endforeach
1.6 Polymorphic Relationships
Mình sẽ lấy ví dụ một chút nhé. Các bạn thử tưởng tượng rằng bây giờ chúng ta có bảng posts
và bảng pages
. Bây giờ ta muốn tạo chức năng comment cho post hoặc comment cho page thì chúng ta sẽ lại phải tạo thêm bảng posts_comments
để lưu các comment của posts
và pages_comments
để lưu các comment của bảng pages
. Điều đó gây phức tạp và chúng ta phải tạo rất nhiều bảng. Vậy Laravel đã hỗ trợ chúng ta bằng quan hệ Polymorphic
khiến giản lược và tiện lợi hơn với chúng ta rất nhiều.
1.6.1 One to One
Quan hệ này nó cũng giống như quan hệ One to One
cơ bản ở trên. Mình sẽ lấy ví dụ để các bạn hiểu thêm nhé. Bây giờ chúng ta có các bảng sau:
posts
: id
, name
videos
: id
, name
images
: id
, url
, imageable_id
, imageable_type
Mình giải thích một chút nhé:
- Tại sao lại sinh ra 2 trường
imageable_id
vàimageable_type
, thì theo convention của Laravel thì bảng lưu trung gian sẽ bắt buộc phải có 2 trường id và type nhưng để rõ ràng hơn thì sẽ lưu thêm tiền tốtên_bảng_bỏ_s +able_
. imageable_id
là lưu id của các bài post hoặc video.imageable_type
là lưu tên class model của Post và User. Ví dụ nhưApp\Post
hoặcApp\Video
.
Chúng ta sẽ định nghĩa các model trong ví dụ trên như sau
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Image
extends
Model
{
public
function
imageable()
{
return
$this->morphTo();
}
}
class
Post
extends
Model
{
public
function
image()
{
return
$this->morphOne(Image
::class,
'imageable');
}
}
class
Video
extends
Model
{
public
function
image()
{
return
$this->morphOne(Image
::class,
'imageable');
}
}
Và trong khi truy vấn dữ liệu chúng ta cứ lấy model Post hoặc Video trỏ đến image là ok.
$video
= Video
::find(1);
$image
=
$video->image;
1.6.2 One to Many
Lấy luôn ví dụ mở bài cho hiểu này ). Chúng ta có posts và pages, mỗi loại trên đều có rất nhiều comment.
posts
: id
, title
, content
pages
: id
, body
comments
: id
, commentable_id
, commentable_type
, date
Chúng ta sẽ tạo các model như sau
// Post.php
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Post
extends
Model
{
public
function
comments()
{
return
$this->morphMany(Comment
::class,
'commentable');
}
}
// Page.php
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Page
extends
Model
{
public
function
comments()
{
return
$this->morphMany(Comment
,
'commentable');
}
}
// Comment.php
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Comment
extends
Model
{
public
function
commentable()
{
return
$this->morphTo();
}
}
Chúng ta đã định nghĩa các quan hệ trên bằng các phương thức morphMany()
và morphTo
giúp chúng ta tạo quan hệ polymorphic
. Cả Page
và Post
đều có phương thức comments
nó sẽ trả về đúng comment theo model Comment
. Comment
model có phương thức commentable()
nó sẽ trả về kết quả phương thức morphTo()
chỉ ra rằng class này có liên quan đến những model khác.
Để truy vấn thì chúng ta sẽ sử dụng phương thức comments
được định nghĩa trong model
$page
= Page
::find(3);
foreach($page->comment
as
$comment)
{
}
Ez đúng không )
1.6.3 Custom polymorphic type
Như các bạn thấy Laravel sẽ lưu trương _type
đó là tên đầy đủ của class model. Như ví dụ đề cập trên thì trường commentable_type
sẽ lưu App\Post
và App\Page
khi thực hiện lưu. Tuy nhiên chúng ta có thể custom được những cái tên dài dòng đó bằng cách sử dụng Illuminate\Database\Eloquent\Relations\Relation
và đăng ký nó trong boot()
của AppServiceProvider.php
.
use
Illuminate\Database\Eloquent\Relations\Relation;
Relation
::morphMap([
'posts'
=>
'App\Post',
'pages'
=>
'App\Page',
]);
2 Truy vấn trong quan hệ
2.1 has | whereHas
Đôi khi chúng ta muốn giới hạn kết quả bản ghi lấy ra, ví dụ như ta muốn lấy ra những bài post mà có comment, không có comment không lấy.
$posts
= Post
::has('comments
)->get();
Nếu như ví dụ còn có quan hệ vote comment thì chúng ta muốn lấy ra bài post nào có comment được vote thì chúng ta sẽ lấy như sau
$posts
= Post
::has('comments
.votes
)->get();
Hoặc nếu như chúng ta muốn lấy ra những bài post phải có comment và kèm theo điều kiện content
của comment đó phải bắt đầu theo một điều kiện nào đó
$posts
= Post
::whereHas('comments',
function
($query)
{
$query->where('content',
'like',
'%gì gì đó%');
});
2.2 doesntHave | orDoesntHave
Ngược lại 2 phương thức ở trên đó chính là doesntHave
và whereDoesntHave
. Ví dụ chúng ta muốn lấy ra các bài post mà có không có comment nào $posts = Post::doesntHave('comments)->get();
Hoặc chúng ta muốn lấy ra những bài post mà không có comment nào và kèm theo một điều kiện nữa chẳng hạn
$posts
= Post
::whereDoesntHave('comments',
function
($query)
{
$query->where('content',
'like',
'foo%');
})->get();
2.3 Counting Related Models
Như các bạn biết đấy chẳng hạn mà chúng ta chỉ muốn lấy ra số comment của bài post thì Laravel cung cấp cho ta một phương thức withCount()
, nó sẽ thêm thuộc tính {relation}_count
vào kết quả trả về cho mình
$post
= Post
::find(1)->withCount('comments
)->get();
// Lấy ra số lượng comment của bài viết
$comments
=
$post->comment_count;
2.4 Eager Loading
Laravel mặc định ORM sẽ ở chế độ lazy
khi load lên tất cả các model quan hệ. Mình có ví dụ sau nhé
$posts
= Post
::all();
foreach
($posts
as
$post)
{
echo
$post->user->id;
}
Ví dụ trên thể hiện 2 điều
- Một câu truy vấn để lấy ra tất cả các bản ghi từ bảng posts
- Bới mỗi bản ghi post chúng ta lại truy vấn để lấy ra tên tác giả bài viết từ mối quan hệ. Vậy giả dụ mà có 100 bài viết thì sẽ phải truy vấn 100 lần để lấy ra tên tác giả mỗi bài viết + 1 lần truy vấn ở đầu để lấy ra tất cả các bài viết.
Hướng giải quyết, Laravel cũng cấp cho ta 2 phương thức with()
và load()
$posts
= Post
::with('user')->get();
foreach
($posts
as
$post)
{
echo
$post->user->id;
}
$posts
= Post
::all();
$posts->load('user');
foreach
($posts
as
$post)
{
echo
$post->user->id;
}
Eager Loading
còn được khai báo trong model
namespace
App;
use
Illuminate\Database\Eloquent\Model;
class
Book
extends
Model
{
protected
$with
=
['user'];
/**
* Get the author that wrote the book.
*/
public
function
author()
{
return
$this->belongsTo(User
::class);
}
}
Đôi khi đặt mặc định trong model như này nhiều trường hợp chúng ta không muốn load user ra thì laravel cung cấp cho ta phương thức without()
$posts
= Post
::without('user
)->get();
Giả sử chúng ta muốn lấy ra những bài post mà có tác giả có id
lớn hơn 3 chăng hạn thì chúng ta sẽ làm như sau
$posts
= Post
::with(['user'
All the options and events can be found in official documentation
Copyright © 2023 MTViet , All rights reserved. Giao diện mod từ NeptuneApp
Website chỉ sử dụng cookies xử lý các tùy chọn cho các chức năng , tài khoản và mật khẩu trên trang này (đối với khách đã đăng nhập). Website hiện không thu thập bất cứ thông tin cá nhân nào khác .
Danh mục bài viết
My Favourites
Hỗ trợ
Framework Website
Nhóm phần mềm Adobe
Cơ sở dữ liệu ( CSDL)
Phần mềm máy tính I
Khóa học Lập trình website
Khóa học lập trình Windows
Lập trình di động Android , IOS
Tin học đại cương , văn phòng , tin học cho người đi làm
Khóa học nghệ thuật sống , dưỡng sinh
Sử dụng Office 365
Mail Setting
Mail Auto Responder
Auto Trash Delete
Custom Signature
Chat Setting
Show Online
Chat Notifications