- vừa được xem lúc

Tìm hiểu về các Relationships trong Laravel

0 0 106

Người đăng: Nguyen Quang Phu

Theo Viblo Asia

Trong cuộc sống, mọi sự vật đều liên kết với nhau, ví dụ như một cuốn sách phải có tác giả, hay một ngôi trường phải có nhiều lớp học. Ở trong cơ sở dữ liệu cũng vậy, các bảng cũng có thể liên kết với nhau. Ở trong Laravel đã cung cấp sẵn cho chúng ta các mối quan hệ để có thể giảm thiếu thời gian công sức của các lập trình viên. Nó giúp chúng ta truy vấn các bảng trở nên dễ dàng hơn. Ở bài này chúng ta cùng nhau tìm hiểu về các loại quan hệ

  • One to One
  • One to Many
  • Many to Many
  • Has One Through
  • Has Many Through
  • Polymorphic Relations

Các mối quan hệ trong Laravel

1. One to One

Đây là một kiểu quan hệ đơn giản nhất, mà chúng ta có thể hiểu rằng cái này chỉ phụ thuộc vào cái kia và ngược lại. Ví dụ cho dễ hình dung, ta có bảng Users và bảng Avatar thì ở đây một người dùng thì chỉ có một cái avatar và chiếc avatar này chỉ đại diện cho user đó. Để biểu diễn mối quan hệ này ta sử dụng method hasOne

<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Authenticatable
{ ... public function avatar() { return $this->hasOne('App\Avatar'); }
} 

Ở trên ta thấy tham số truyền vào đầu tiên trong method hasOne là tên của model liên quan đến với bảng đó. Khi ta khai báo như vậy, nếu muốn lấy avatar của user có id là 1 chả hạn, ta đơn giản chỉ làm như thế này.

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

Chú ý ở trên Eloquent sẽ tự động match cột khóa ngoại có tên user_id của bảng Avatar tương ứng với trường id của bảng User, nếu trong trường hợp khóa ngoại không đặt là user_id mà đặt một tên khác thì cần truyền thêm một tham số thứ 2.

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

Tương tự với trường hợp nếu khóa chính của bảng mà bạn liên kết, bạn không đặt là id thì cần truyền tiếp một tham số thứ 3 để cho nó hiểu được là khóa ngoại của cột này tương ứng với khóa chính với cột kia

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

Inverse

<?php namespace App; use Illuminate\Database\Eloquent\Model; class Avatar extends Model
{ protected $table = 'avatar'; public function user() { $this->belongsTo('App\User'); }
} 

Ở ví dụ trước thì ta có thể từ một user để gọi tới avatar tương ứng của user đó, giờ chúng ta cũng sẽ có thể truy vấn tương tự từ avatar này để truy vấn ra user thuộc về nó.
Ở ví dụ trên, Eloquent đã tự động match cột user_id ở bảng Avatar với cột id của bảng User. Trong Eloquent mặc định xác nhận khóa ngoại là tên bảng bỏ 's' _id, ở trong trường hợp này là user_id. Nếu khóa ngoại ở bạng Avatar không đặt đúng theo qui chuẩn trước đó thì sẽ cần phải khai báo thêm 1 tham số thứ 2 nữa.

 return $this->belongsTo('App\User', 'foreign_key');

Nếu như bạn không muốn map foreign key của bảng Avatar với cột id ở bảng User mà lại là 1 cột khác chả hạn thì tiếp tục thêm 1 tham số thứ 3 như sau.

return $this->belongsTo('App\User', 'foreign_key', 'other_key');

2. One to Many

Mối quan hệ này để biểu thị một mối quan hệ cha-con. Ví dụ một user thì sẽ có nhiều bài posts. Thì mối quan hệ này sẽ được biểu diễn như sau.

<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Authenticatable
{ public function posts() { return $this->hasMany('App\Post'); }
}

Cũng tương tự quan hệ One-One Eloquent sẽ tự động tìm khóa ngoại ở model Post , ở trong trường hợp này eloquent sẽ tìm tới khóa ngoại là "snake case" tên model _id, ở ví dụ trên sẽ là user_id, Eloquent sẽ giả định user_id là khóa ngoại ở model Post. Để truy vấn tới nhiều posts thuộc một user quá đơn giản bằng cách:

$user = App\User::find(1)->posts;

và ngược lại

<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model
{ public function user() { return $this->belongsTo('App\User'); }
} 

3. Many to Many

Quan hệ này thì nó sẽ phức tạp hơn với hai quan hệ trước nó. Ví dụ một product sẽ thuộc nhiều orders mà một order lại có nhiều products . Để biểu diễn được quan hệ này chúng ta cần sử dụng đến một bảng thế 3, mình sẽ đặt tên là order_product. và đồng thời sẽ chứa 2 cột là order_idproduct_id. Ở mối quan hệ này ta sẽ sử dụng method belongsToMany. Ví dụ biểu diễn method Product với bảng order

namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model
{ public function orders() { return $this->belongsToMany('App\Order'); }
}

bây giờ muốn lấy ra được một product có bao nhiêu order thì chỉ cần

$orders = App\Product::find(1)->orders;

Có một chú ý là, Eloquent sẽ tự động tìm đến bảng trung gian đặt tên theo thứ tự alphabet, trong trường hợp này bảng sẽ tên là order_product. Tuy nhiên nếu bạn muốn đặt 1 cái tên khác mà không theo quy ước ví dụ là product_order thì chỉ cần truyền thêm tham số thứ 2 vào method belongsToMany.

 return $this->belongsToMany('App\Order', 'product_order');

Tương tự bạn cũng có thể custom lại tên của 2 tên của cột liên kết tương ứng với tham số thứ 3 và thứ 4 của method belongsToMany. Với tham số thứ 3 sẽ là khóa ngoại của bảng đang định nghĩa quan hệ và tham số thứ 4 là bảng chúng ta muốn join. Ví dụ trong trường hợp này.

namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model
{ public function orders() { return $this->belongsToMany('App\Order', 'product_order', 'product_id', 'order_id'); }
}

Ngược lại, đối với bảng Order ta định nghĩa như sau:

namespace App; use Illuminate\Database\Eloquent\Model; class Order extends Model
{ public function product() { return $this->belongsToMany('App\Product'); }
}

và chúng ta cũng tương tự như định nghĩa model Product, chúng ta cũng có thể truyền các tham số vào nếu muốn

namespace App; use Illuminate\Database\Eloquent\Model; class Order extends Model
{ public function product() { return $this->belongsToMany('App\Product', 'product_order', 'order_id', 'product_id'); }
}

Lấy giá trị của bảng trung gian Để làm việc với mối quan hệ Many to Many này thì chúng ta cần sử dụng đến một bảng trung gian. Eloquent cũng hỗ trợ giúp chúng ta lấy được các giá trị của bảng này. Để truy cập đến các cột của bảng trung gian chúng ta sẽ sử dụng thuộc tính pivot. Ví dụ :

$product = App\Product::find(1); foreach($product->orders as $order)
{ echo $order->pivot->created_at;
}

Theo mặc định thì Eloquent chỉ lấy các trường trung gian và created_at, update_at nếu chúng ta muốn lấy ra giá trị của một cột khác thì cần khai báo thêm như sau, giả sử chúng ta cần lấy thêm trường address

return $this->belongsToMany('App\Product')->withPivot('address');

Hoặc là khi bạn muốn hai trường created_atupdate_at của bảng trung gian tự động cập nhật giá trị thì khai báo thêm

return $this->belongsToMany('App\Product')->withTimestamps();

Đôi khi người dùng lại muốn thay đổi tên của thuôc tính pivot thì phải làm như thế nào, chỉ cần sử dụng method as được khai báo trong model là xong. Ví dụ

return $this->belongsToMany('App\Product') ->as('newname') ->withTimestamps();

giờ muốn truy cập các thuộc tính của bảng trung gian thay thế pivot thành newname là được.

$product = App\Product::find(1); foreach($product->orders as $order)
{ echo $order->newname->created_at;
}

Một câu hỏi nữa được đặt ra là nếu muốn lấy các sản phẩm với điều kiện của bảng trung gian là hợp lệ thì sẽ như thế nào, rất đơn giản, Laravel cũng hỗ trợ chúng ta trong vấn đề này.

public function products()
{ return $this->belongsToMany(Product::class)->wherePivot('price', '>', 20000);
}

Ở đây sẽ lấy ra các Order có giá lớn hơn 20000.

4. Has One Through

Đây là một mối quan hệ liên kết các bảng với nhau thông qua một bảng trung gian Ví dụ có 3 bảng:

users id - integer supplier_id - integer suppliers id - integer history id - integer user_id - integer

Mặc dù bảng history không chứ supplier_id nhưng chúng ta vẫn có thể truy cập đến user's history bới mối quan hệ hasOneThrough như sau

<?php namespace App; use Illuminate\Database\Eloquent\Model; class Supplier extends Model
{ public function userHistory() { return $this->hasOneThrough('App\History', 'App\User'); }
}

Với tham số thứ nhất được truyền vào là tên của model mà chúng ta muốn truy cập, tham số thứ 2 là model trung gian. Chúng ta cũng có thể custom các key liên quan đến mối quan hệ này lần lượt là các tham số sau vào hàm định nghĩa quan hệ.

class Supplier extends Model
{ public function userHistory() { return $this->hasOneThrough( 'App\History', 'App\User', 'supplier_id', // Khóa ngoại của bảng trung gian user 'user_id', // Khóa ngoại của bảng chúng ta muốn truy cập đến 'id', // Khóa mà chúng ta muốn liên kết ở bảng supplier 'id' // Khóa mà chúng ta muốn liên kết ở bảng user ); }
}

hai bảng userhistory chúng ta định nghia như bình thường

// 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); }
}

5. Has Many Through

Mối quan hệ has many through này cung cấp cho chúng ta cách truy cập bảng liên kết dễ dàng hơn thông qua bảng trung gian. Ví dụ một Team có nhiều bài Post thông qua bảng trung gian là User.

teams id - integer name - string users id - integer team_id - integer name - string posts id - integer user_id - integer title - string

Mặc dù bảng posts không chứa khóa ngoại team_id, nhưng với quan hệ hasManyThrough sẽ cung cấp cho chúng ta lấy tất cả posts của một teams bằng cách $team->posts. Để thực hiện việc này thì Eloquent sẽ kiểm tra team_id thông qua bảng users. Chúng ta sẽ biểu diễn quan hệ như sau:

<?php namespace App; use Illuminate\Database\Eloquent\Model; class Team extends Model
{ public function posts() { return $this->hasManyThrough('App\Post', 'App\User'); }
}

Tham số đầu tiên của quan hệ này là tên model mà chúng ta muốn truy cập, tham số thứ hai là model trung gian. Chúng ta cũng có thể custom lại tên của các khóa ngoại bằng cách thêm các tham số, với tham số thứ 3 là khóa ngoại của bảng trung gian, tham số thứ 4 là khóa ngoại của bảng mà chúng ta muốn gọi tới, tham số thứ 5 là trường mà chúng ta muốn liên kết ở bảng đang sử dụng, tham số thứ 6 là trường mà chúng ta muốn liên kết ở bảng trung gian.

class Tean extends Model
{ public function posts() { return $this->hasManyThrough( 'App\Post', 'App\User', 'country_id', // khóa ngoại của bảng trung gian 'user_id', // khóa ngoại của bảng mà chúng ta muốn gọi tới 'id', //trường mà chúng ta muốn liên kết ở bảng đang sử dụng 'id' // trường mà chúng ta muốn liên kết ở bảng trung gian. ); }
}

6. Polymorphic

Đây là mối quan hệ đa hình trong Laravel cho phép 1 Model có thể belongsTo nhiều Model khác mà chỉ cần dùng 1 associate.

6.1 One to One Polymorphic

Mối quan hệ này tương tự quan hệ One to One, tuy nhiên mục đích của mối quan hệ này là 1 model có thể belongsTo 1 hay nhiều model khác. Ví dụ một bài post có một image và một product cũng có một image, nếu như bình thường thì các bạn phải tạo thêm 2 bảng là post_image để lưu ảnh của postproduct_iamge để lưu ảnh của product, nếu có ti tỉ các bảng cần đến image thì lại phải tạo thêm bấy nhiêu bảng để lưu ảnh, vậy thì sẽ quá phức tạp và rối, vậy nên mới sinh ra mối quan hệ polymorphic này. Ví dụ :

posts id - integer name - string products id - integer name - string images id - integer url - string imageable_id - integer imageable_type - string

Đây là cách để xây dựng mối quan hệ polymorphic này. Với imageable_id sẽ lưu id của bảng posts và bảng products, còn trường imageable_type sẽ lưu tên class model PostProduct. Theo convention của laravel thì bảng lưu trung gian sẽ bắt buộc phải có 2 trường idtype nhưng để rõ ràng hơn thì sẽ lưu thêm tiền tố tên_bảng_bỏ_s +able_. Cấu trúc model

<?php 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('App\Image', 'imageable'); }
} class Product extends Model
{ public function image() { return $this->morphOne('App\Image', 'imageable'); }
}

Để lấy ra image thuộc bài posts thì sẽ trỏ tới image là được.

$post = App\Post::find(1); $image = $post->image;

và ngược lại từ image có thể suy ngược lại post hay product phụ thuộc vào nó.

$image = App\Image::find(1); $imageable = $image->imageable;

6.1 One to Many Polymorphic

Mối quan hệ này cũng gần giống với quan hệ One to Many. Ví dụ một User có thể comment ở cả Post lẫn Video thì chỉ cần 1 bảng comments trong trường hợp này

posts id - integer title - string body - text videos id - integer title - string url - string comments id - integer body - text commentable_id - integer commentable_type - string

Cấu trúc model

<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model
{ public function commentable() { return $this->morphTo(); }
} class Post extends Model
{ public function comments() { return $this->morphMany('App\Comment', 'commentable'); }
} class Video extends Model
{ public function comments() { return $this->morphMany('App\Comment', 'commentable'); }
}

Lấy ra các giá trị tất cả các comment của post ta làm như sau

$post = App\Post::find(1); foreach ($post->comments as $comment) { echo $comment->content;
}

hay là từ comment để lấy ngược lại cái post hay videos thuộc về nó

 $comment = App\Comment::find(1); $commentable = $comment->commentable;

6.1 Many to Many Polymorphic

Quan hệ này sẽ phức tạp hơn một chút. Ví dụ một post hay là video có thể có nhiều tags. Sử dụng mối quan hệ many to many polymorphic cho phép bạn truy vấn lấy ra các tags thuộc về một post hay video

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

Cấu trúc model

//post.php <?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model
{ public function tags() { return $this->morphToMany('App\Tag', 'taggable'); }
}
//tag.php
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Tag extends Model
{ public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } public function videos() { return $this->morphedByMany('App\Video', 'taggable'); }
}

Ok bây giờ muốn lấy ra các tag thuộc về một post ta cũng làm tương tự nhưng mối quan hệ khác.

$post = App\Post::find(1); foreach ($post->tags as $tag) { //
} 

hoặc là ngược lại

$tag = App\Tag::find(1); foreach ($tag->videos as $video) { //
}

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 525

- vừa được xem lúc

Cài đặt WSL / WSL2 trên Windows 10 để code như trên Ubuntu

Sau vài ba năm mình chuyển qua code trên Ubuntu thì thật không thể phủ nhận rằng mình đã yêu em nó. Cá nhân mình sử dụng Ubuntu để code web thì thật là tuyệt vời.

0 0 396

- vừa được xem lúc

Đặt tên commit message sao cho "tình nghĩa anh em chắc chắn bền lâu"????

. Lời mở đầu. .

1 1 738

- vừa được xem lúc

Tìm hiểu về Resource Controller trong Laravel

Giới thiệu. Trong laravel, việc sử dụng các route post, get, group để gọi đến 1 action của Controller đã là quá quen đối với các bạn sử dụng framework này.

0 0 358

- vừa được xem lúc

Phân quyền đơn giản với package Laravel permission

Như các bạn đã biết, phân quyền trong một ứng dụng là một phần không thể thiếu trong việc phát triển phần mềm, dù đó là ứng dụng web hay là mobile. Vậy nên, hôm nay mình sẽ giới thiệu một package có thể giúp các bạn phân quyền nhanh và đơn giản trong một website được viết bằng PHP với framework là L

0 0 449

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433