نسخه:

الکوئنت: روابط

معرفی

جداول پایگاه داده اغلب با یکدیگر مرتبط هستند. به عنوان مثال، یک پست وبلاگ ممکن است نظرات زیادی داشته باشد یا یک سفارش می تواند مربوط به کاربری باشد که آن را ارسال کرده است. Eloquent مدیریت و کار با این روابط را آسان می کند و از انواع روابط رایج پشتیبانی می کند:

تعریف روابط

روابط Eloquent به عنوان روش هایی در کلاس های مدل Eloquent شما تعریف می شوند. از آنجایی که روابط نیز به عنوان سازندگان پرس و جو قوی عمل می کنند ، تعریف روابط به عنوان متدها، قابلیت های زنجیره ای متد و پرس و جوی قدرتمندی را فراهم می کند. برای مثال، ممکن است محدودیت‌های پرس و جو اضافی را در این posts رابطه زنجیره‌ای کنیم:

$user->posts()->where('active', 1)->get();

اما، قبل از غوطه ور شدن بیش از حد در استفاده از روابط، بیایید یاد بگیریم که چگونه هر نوع رابطه ای را که توسط Eloquent پشتیبانی می شود، تعریف کنیم.

یک به یک

رابطه یک به یک نوع بسیار ابتدایی رابطه پایگاه داده است. به عنوان مثال، یک User مدل ممکن است با یک مدل مرتبط باشد Phone . برای تعریف این رابطه، phone روشی را روی مدل قرار می دهیم User . متد phone باید hasOne متد را فراخوانی کرده و نتیجه آن را برگرداند. این hasOne روش از طریق کلاس پایه مدل برای مدل شما در دسترس است Illuminate\Database\Eloquent\Model :

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
 
class User extends Model
{
/**
* Get the phone associated with the user.
*/
public function phone(): HasOne
{
return $this->hasOne(Phone::class);
}
}

اولین آرگومان ارسال شده به hasOne متد، نام کلاس مدل مرتبط است. هنگامی که رابطه تعریف شد، ممکن است رکورد مربوطه را با استفاده از ویژگی های دینامیکی Eloquent بازیابی کنیم. ویژگی‌های پویا به شما امکان می‌دهند به روش‌های رابطه دسترسی داشته باشید، گویی که آنها ویژگی‌هایی هستند که در مدل تعریف شده‌اند:

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

Eloquent کلید خارجی رابطه را بر اساس نام مدل والد تعیین می کند. در این حالت، Phone مدل به طور خودکار دارای یک user_id کلید خارجی فرض می شود. اگر می خواهید این قرارداد را لغو کنید، می توانید یک آرگومان دوم را به hasOne متد ارسال کنید:

return $this->hasOne(Phone::class, 'foreign_key');

علاوه بر این، Eloquent فرض می‌کند که کلید خارجی باید دارای مقداری مطابق با ستون کلید اصلی والد باشد. به عبارت دیگر، Eloquent مقدار ستون کاربر را id در user_id ستون رکورد جستجو می کند Phone . اگر می‌خواهید این رابطه از یک مقدار کلید اصلی غیر از ویژگی id مدل شما استفاده کند $primaryKey ، می‌توانید آرگومان سومی را به hasOne متد ارسال کنید:

return $this->hasOne(Phone::class, 'foreign_key', 'local_key');

تعریف معکوس رابطه

بنابراین، ما می توانیم Phone از User مدل خود به مدل دسترسی پیدا کنیم. در مرحله بعد، بیایید یک رابطه در Phone مدل تعریف کنیم که به ما امکان می دهد به کاربر صاحب تلفن دسترسی پیدا کنیم. ما می توانیم معکوس یک hasOne رابطه را با استفاده از belongsTo روش تعریف کنیم:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Phone extends Model
{
/**
* Get the user that owns the phone.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

هنگام فراخوانی روش، Eloquent تلاش می‌کند مدلی را user بیابد که با ستون روی مدل مطابقت داشته باشد . User id user_id Phone

Eloquent نام کلید خارجی را با بررسی نام روش رابطه و پسوند نام متد با _id . بنابراین، در این مورد، Eloquent فرض می کند که Phone مدل دارای یک user_id ستون است. با این حال، اگر کلید خارجی مدل Phone نیست user_id ، می‌توانید یک نام کلید سفارشی را به عنوان آرگومان دوم به belongsTo متد ارسال کنید:

/**
* Get the user that owns the phone.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'foreign_key');
}

اگر مدل والد به عنوان کلید اصلی خود استفاده نمی‌کند id ، یا می‌خواهید مدل مرتبط را با استفاده از ستون دیگری پیدا کنید، می‌توانید آرگومان سومی را به متد ارسال کنید که belongsTo کلید سفارشی جدول والد را مشخص می‌کند:

/**
* Get the user that owns the phone.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'foreign_key', 'owner_key');
}

یک به بسیاری

یک رابطه یک به چند برای تعریف روابطی استفاده می شود که در آن یک مدل واحد والدین یک یا چند مدل فرزند است. به عنوان مثال، یک پست وبلاگ ممکن است تعداد بی نهایت نظر داشته باشد. مانند سایر روابط Eloquent، روابط یک به چند با تعریف یک روش در مدل Eloquent شما تعریف می شود:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
 
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}

به یاد داشته باشید، Eloquent به طور خودکار ستون کلید خارجی مناسب را برای Comment مدل تعیین می کند. طبق قرارداد، Eloquent نام "snake case" مدل والد را می گیرد و آن را پسوند می کند _id . بنابراین، در این مثال، Eloquent ستون کلید خارجی مدل Comment را post_id .

هنگامی که متد رابطه تعریف شد، می توانیم با دسترسی به ویژگی به مجموعه نظرات مرتبط دسترسی پیدا کنیم comments . به یاد داشته باشید، از آنجایی که Eloquent "ویژگی های رابطه پویا" را ارائه می دهد، می توانیم به روش های رابطه به گونه ای دسترسی داشته باشیم که گویی به عنوان ویژگی در مدل تعریف شده اند:

use App\Models\Post;
 
$comments = Post::find(1)->comments;
 
foreach ($comments as $comment) {
// ...
}

comments از آنجایی که همه روابط به عنوان سازنده پرس و جو نیز عمل می کنند، می توانید با فراخوانی متد و ادامه دادن به شرایط زنجیره ای در پرس و جو، محدودیت های بیشتری به پرس و جوی رابطه اضافه کنید :

$comment = Post::find(1)->comments()
->where('title', 'foo')
->first();

مانند hasOne متد، می‌توانید کلیدهای خارجی و محلی را نیز با ارسال آرگومان‌های اضافی به hasMany متد لغو کنید:

return $this->hasMany(Comment::class, 'foreign_key');
 
return $this->hasMany(Comment::class, 'foreign_key', 'local_key');

یک به بسیاری (معکوس) / متعلق به

اکنون که می‌توانیم به همه نظرات یک پست دسترسی پیدا کنیم، بیایید یک رابطه تعریف کنیم تا یک نظر به پست اصلی خود دسترسی پیدا کند. برای تعریف معکوس یک hasMany رابطه، یک روش رابطه در مدل فرزند تعریف کنید که belongsTo متد را فراخوانی می کند:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}

هنگامی که رابطه تعریف شد، می‌توانیم با دسترسی به post "ویژگی رابطه پویا" پست والد یک نظر را بازیابی کنیم:

use App\Models\Comment;
 
$comment = Comment::find(1);
 
return $comment->post->title;

در مثال بالا، Eloquent تلاش می‌کند Post مدلی را پیدا کند که با ستون روی مدل id مطابقت داشته باشد . post_id Comment

Eloquent نام کلید خارجی پیش‌فرض را با بررسی نام روش رابطه و پسوند نام متد با _ نام ستون کلید اصلی مدل والد تعیین می‌کند. بنابراین، در این مثال، Eloquent فرض می‌کند که Post کلید خارجی مدل روی comments جدول است post_id .

با این حال، اگر کلید خارجی برای رابطه شما از این قراردادها پیروی نمی کند، می توانید یک نام کلید خارجی سفارشی را به عنوان آرگومان دوم برای متد ارسال کنید belongsTo :

/**
* Get the post that owns the comment.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class, 'foreign_key');
}

اگر مدل والد شما به‌عنوان کلید اصلی خود استفاده نمی‌کند id ، یا می‌خواهید مدل مرتبط را با استفاده از ستون دیگری پیدا کنید، می‌توانید آرگومان سومی را به متد ارسال کنید که belongsTo کلید سفارشی جدول والد شما را مشخص می‌کند:

/**
* Get the post that owns the comment.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
}

مدل های پیش فرض

روابط belongsTo , hasOne , hasOneThrough , و morphOne به شما این امکان را می دهد که یک مدل پیش فرض تعریف کنید که اگر رابطه داده شده باشد، برگردانده می شود null . این الگو اغلب به عنوان الگوی شی تهی شناخته می شود و می تواند به حذف بررسی های شرطی در کد شما کمک کند. در مثال زیر، اگر هیچ کاربری به مدل متصل نباشد، user رابطه یک مدل خالی برمی‌گرداند : App\Models\User Post

/**
* Get the author of the post.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class)->withDefault();
}

برای پر کردن مدل پیش‌فرض با ویژگی‌ها، می‌توانید یک آرایه یا بسته را به withDefault متد ارسال کنید:

/**
* Get the author of the post.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest Author',
]);
}
 
/**
* Get the author of the post.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class)->withDefault(function (User $user, Post $post) {
$user->name = 'Guest Author';
});
}

پرس و جو متعلق به روابط است

هنگام پرس و جو برای فرزندان یک رابطه " متعلق به "، می توانید به صورت دستی این where عبارت را برای بازیابی مدل های Eloquent مربوطه بسازید:

use App\Models\Post;
 
$posts = Post::where('user_id', $user->id)->get();

با این حال، ممکن است استفاده از whereBelongsTo روشی که به طور خودکار رابطه مناسب و کلید خارجی مدل داده شده را تعیین می کند، راحت تر باشد:

$posts = Post::whereBelongsTo($user)->get();

همچنین می توانید یک نمونه مجموعه برای whereBelongsTo روش ارائه دهید. هنگام انجام این کار، لاراول مدل هایی را که متعلق به هر یک از مدل های والد مجموعه هستند بازیابی می کند:

$users = User::where('vip', true)->get();
 
$posts = Post::whereBelongsTo($users)->get();

به طور پیش فرض، لاراول رابطه مرتبط با مدل داده شده را بر اساس نام کلاس مدل تعیین می کند. با این حال، می توانید نام رابطه را به صورت دستی با ارائه آن به عنوان آرگومان دوم متد مشخص کنید whereBelongsTo :

$posts = Post::whereBelongsTo($user, 'author')->get();

یکی از بسیاری را دارد

گاهی اوقات یک مدل ممکن است مدل‌های مرتبط زیادی داشته باشد، اما شما می‌خواهید به راحتی «آخرین» یا «قدیمی‌ترین» مدل مرتبط رابطه را بازیابی کنید. به عنوان مثال، یک User مدل ممکن است به بسیاری از مدل‌ها مرتبط باشد Order ، اما شما می‌خواهید روشی مناسب برای تعامل با آخرین سفارشی که کاربر قرار داده است، تعریف کنید. شما می توانید این کار را با استفاده از hasOne نوع رابطه ترکیب شده با ofMany روش های زیر انجام دهید:

/**
* Get the user's most recent order.
*/
public function latestOrder(): HasOne
{
return $this->hasOne(Order::class)->latestOfMany();
}

به همین ترتیب، می‌توانید روشی را برای بازیابی «قدیمی‌ترین» یا اولین مدل مرتبط یک رابطه تعریف کنید:

/**
* Get the user's oldest order.
*/
public function oldestOrder(): HasOne
{
return $this->hasOne(Order::class)->oldestOfMany();
}

به طور پیش‌فرض، متدهای latestOfMany و oldestOfMany آخرین یا قدیمی‌ترین مدل مرتبط را بر اساس کلید اصلی مدل، که باید قابل مرتب‌سازی باشد، بازیابی می‌کنند. با این حال، گاهی اوقات ممکن است بخواهید یک مدل واحد را از یک رابطه بزرگتر با استفاده از معیارهای مرتب سازی متفاوت بازیابی کنید.

به عنوان مثال، با استفاده از ofMany روش، ممکن است گران ترین سفارش کاربر را بازیابی کنید. این ofMany متد ستون قابل مرتب‌سازی را به‌عنوان اولین آرگومان خود می‌پذیرد و کدام تابع جمع ( min یا max ) را هنگام پرس و جو برای مدل مرتبط اعمال می‌کند:

/**
* Get the user's largest order.
*/
public function largestOrder(): HasOne
{
return $this->hasOne(Order::class)->ofMany('price', 'max');
}

از آنجایی که PostgreSQL از اجرای تابع در برابر ستون‌های UUID پشتیبانی نمی‌کند MAX ، در حال حاضر امکان استفاده از یکی از چندین رابطه در ترکیب با ستون‌های PostgreSQL UUID وجود ندارد.

تبدیل روابط "بسیار" به دارای یک رابطه

اغلب، هنگام بازیابی یک مدل واحد با استفاده از متدهای latestOfMany ، oldestOfMany یا، ofMany از قبل یک رابطه "دارای بسیاری" برای همان مدل تعریف شده است. برای راحتی کار، لاراول به شما اجازه می دهد تا با استفاده از one متد روی رابطه، این رابطه را به راحتی به یک رابطه "یک" تبدیل کنید :

/**
* Get the user's orders.
*/
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
 
/**
* Get the user's largest order.
*/
public function largestOrder(): HasOne
{
return $this->orders()->one()->ofMany('price', 'max');
}

Advanced یکی از بسیاری از روابط را دارد

می توان روابط پیشرفته تری ساخت "یکی از چندین" را دارد. به عنوان مثال، یک Product مدل ممکن است مدل های مرتبط زیادی داشته باشد Price که حتی پس از انتشار قیمت گذاری جدید در سیستم حفظ می شوند. علاوه بر این، داده‌های قیمت‌گذاری جدید برای محصول ممکن است از قبل منتشر شود تا در تاریخ آینده از طریق یک published_at ستون اعمال شود.

بنابراین، به طور خلاصه، ما باید آخرین قیمت منتشر شده را در جایی که تاریخ منتشر شده در آینده نیست، بازیابی کنیم. علاوه بر این، اگر دو قیمت تاریخ انتشار یکسانی داشته باشند، قیمت با بیشترین شناسه را ترجیح می دهیم. برای انجام این کار، باید آرایه‌ای را به ofMany متد ارسال کنیم که حاوی ستون‌های قابل مرتب‌سازی است که آخرین قیمت را تعیین می‌کنند. علاوه بر این، یک بسته به عنوان آرگومان دوم برای ofMany روش ارائه خواهد شد. این بسته شدن مسئول اضافه کردن محدودیت های تاریخ انتشار اضافی به پرس و جو رابطه خواهد بود:

/**
* Get the current pricing for the product.
*/
public function currentPricing(): HasOne
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function (Builder $query) {
$query->where('published_at', '<', now());
});
}

دارای یکی از طریق

رابطه "دارای یک از طریق" یک رابطه یک به یک را با مدل دیگری تعریف می کند. با این حال، این رابطه نشان می‌دهد که مدل اعلام‌کننده را می‌توان با یک نمونه از مدل دیگر با ادامه مدل سوم مطابقت داد .

به عنوان مثال، در یک برنامه تعمیرگاه خودرو، هر Mechanic مدل ممکن است با یک Car مدل و هر مدل ممکن است با یک مدل Car مرتبط باشد . Owner در حالی که مکانیک و مالک هیچ رابطه مستقیمی با پایگاه داده ندارند، مکانیک می تواند از طریق مدل به مالک دسترسی داشته باشد Car . بیایید به جداول لازم برای تعریف این رابطه نگاه کنیم:

mechanics
id - integer
name - string
 
cars
id - integer
model - string
mechanic_id - integer
 
owners
id - integer
name - string
car_id - integer

اکنون که ساختار جدول را برای رابطه بررسی کردیم، بیایید رابطه را بر روی Mechanic مدل تعریف کنیم:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
 
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner(): HasOneThrough
{
return $this->hasOneThrough(Owner::class, Car::class);
}
}

اولین آرگومان ارسال شده به hasOneThrough متد، نام مدل نهایی است که می خواهیم به آن دسترسی داشته باشیم، در حالی که آرگومان دوم، نام مدل میانی است.

یا، اگر روابط مربوطه قبلاً روی همه مدل‌های درگیر در رابطه تعریف شده‌اند، می‌توانید با فراخوانی روش through و ارائه نام آن روابط، یک رابطه «دارای یک از طریق» را روان تعریف کنید. به عنوان مثال، اگر Mechanic مدل دارای یک cars رابطه است و Car مدل دارای یک owner رابطه است، می توانید یک رابطه "دارای یک از طریق" را تعریف کنید که مکانیک و مالک را به این صورت متصل می کند:

// String based syntax...
return $this->through('cars')->has('owner');
 
// Dynamic syntax...
return $this->throughCars()->hasOwner();

کنوانسیون های کلیدی

قراردادهای کلید خارجی Eloquent معمولی هنگام انجام پرس و جوهای رابطه استفاده می شود. اگر می خواهید کلیدهای رابطه را سفارشی کنید، می توانید آنها را به عنوان آرگومان های سوم و چهارم به hasOneThrough متد ارسال کنید. آرگومان سوم نام کلید خارجی در مدل میانی است. آرگومان چهارم نام کلید خارجی در مدل نهایی است. آرگومان پنجم کلید محلی است، در حالی که آرگومان ششم کلید محلی مدل میانی است:

class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner(): HasOneThrough
{
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...
);
}
}

یا، همانطور که قبلاً بحث شد، اگر روابط مربوطه قبلاً در همه مدل‌های درگیر در رابطه تعریف شده‌اند، می‌توانید با فراخوانی روش through و ارائه نام آن روابط، یک رابطه «دارای یک از طریق» را روان تعریف کنید. این رویکرد مزیت استفاده مجدد از قراردادهای کلیدی که قبلاً در روابط موجود تعریف شده است را ارائه می دهد:

// String based syntax...
return $this->through('cars')->has('owner');
 
// Dynamic syntax...
return $this->throughCars()->hasOwner();

دارای بسیاری از طریق

رابطه "دارای بسیاری از طریق" یک راه راحت برای دسترسی به روابط دور از طریق یک رابطه میانی فراهم می کند. به عنوان مثال، فرض کنید در حال ساخت یک پلت فرم استقرار مانند Laravel Vapor هستیم . یک Project مدل ممکن است Deployment از طریق یک مدل میانی به بسیاری از مدل ها دسترسی پیدا کند Environment . با استفاده از این مثال، شما به راحتی می توانید تمام استقرارها را برای یک پروژه معین جمع آوری کنید. بیایید به جداول مورد نیاز برای تعریف این رابطه نگاه کنیم:

projects
id - integer
name - string
 
environments
id - integer
project_id - integer
name - string
 
deployments
id - integer
environment_id - integer
commit_hash - string

اکنون که ساختار جدول را برای رابطه بررسی کردیم، بیایید رابطه را بر روی Project مدل تعریف کنیم:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
 
class Project extends Model
{
/**
* Get all of the deployments for the project.
*/
public function deployments(): HasManyThrough
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}
}

اولین آرگومان ارسال شده به hasManyThrough متد، نام مدل نهایی است که می خواهیم به آن دسترسی داشته باشیم، در حالی که آرگومان دوم، نام مدل میانی است.

یا، اگر روابط مربوطه قبلاً روی همه مدل‌های درگیر در رابطه تعریف شده‌اند، می‌توانید با فراخوانی روش through و ارائه نام آن روابط، یک رابطه «دارای بسیاری از طریق» را روان تعریف کنید. به عنوان مثال، اگر Project مدل دارای یک environments رابطه است و Environment مدل دارای یک deployments رابطه است، می توانید یک رابطه "دارای بسیاری از طریق" تعریف کنید که پروژه و استقرارهای مشابه را به هم متصل می کند:

// String based syntax...
return $this->through('environments')->has('deployments');
 
// Dynamic syntax...
return $this->throughEnvironments()->hasDeployments();

اگرچه Deployment جدول مدل شامل ستونی نیست project_id ، اما این hasManyThrough رابطه از طریق $project->deployments . برای بازیابی این مدل ها، Eloquent ستون روی جدول مدل project_id میانی را بررسی می کند. Environment پس از یافتن شناسه های محیط مربوطه، از آنها برای پرس و جوی Deployment جدول مدل استفاده می شود.

کنوانسیون های کلیدی

قراردادهای کلید خارجی Eloquent معمولی هنگام انجام پرس و جوهای رابطه استفاده می شود. اگر می خواهید کلیدهای رابطه را سفارشی کنید، می توانید آنها را به عنوان آرگومان های سوم و چهارم به hasManyThrough متد ارسال کنید. آرگومان سوم نام کلید خارجی در مدل میانی است. آرگومان چهارم نام کلید خارجی در مدل نهایی است. آرگومان پنجم کلید محلی است، در حالی که آرگومان ششم کلید محلی مدل میانی است:

class Project extends Model
{
public function deployments(): HasManyThrough
{
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...
);
}
}

یا، همانطور که قبلاً بحث شد، اگر روابط مربوطه قبلاً در همه مدل‌های درگیر در رابطه تعریف شده‌اند، می‌توانید با فراخوانی روش through و ارائه نام آن روابط، یک رابطه «دارای بسیاری از طریق» را روان تعریف کنید. این رویکرد مزیت استفاده مجدد از قراردادهای کلیدی که قبلاً در روابط موجود تعریف شده است را ارائه می دهد:

// String based syntax...
return $this->through('environments')->has('deployments');
 
// Dynamic syntax...
return $this->throughEnvironments()->hasDeployments();

روابط بسیار به بسیاری

روابط چند به چند اندکی پیچیده تر از hasOne روابط هستند hasMany . نمونه ای از رابطه چند به چند، کاربری است که نقش های زیادی دارد و آن نقش ها توسط سایر کاربران در برنامه نیز به اشتراک گذاشته می شود. به عنوان مثال، ممکن است به یک کاربر نقش "نویسنده" و "ویرایشگر" اختصاص داده شود. با این حال، این نقش ها ممکن است به کاربران دیگر نیز اختصاص داده شود. بنابراین، یک کاربر دارای نقش های زیادی است و یک نقش دارای کاربران زیادی است.

ساختار جدول

برای تعریف این رابطه، به سه جدول پایگاه داده نیاز است: users , roles و role_user . جدول role_user از ترتیب حروف الفبای نام مدل های مرتبط و شامل user_id و role_id ستون ها مشتق شده است. این جدول به عنوان یک جدول میانی برای پیوند دادن کاربران و نقش ها استفاده می شود.

به یاد داشته باشید، از آنجایی که یک نقش می تواند به کاربران زیادی تعلق داشته باشد، نمی توانیم به سادگی یک user_id ستون را روی roles جدول قرار دهیم. این بدان معناست که یک نقش فقط می تواند به یک کاربر تعلق داشته باشد. به منظور پشتیبانی از نقش هایی که به چندین کاربر اختصاص داده می شوند، role_user جدول مورد نیاز است. می‌توانیم ساختار جدول رابطه را به صورت زیر خلاصه کنیم:

users
id - integer
name - string
 
roles
id - integer
name - string
 
role_user
user_id - integer
role_id - integer

ساختار مدل

روابط چند به چند با نوشتن متدی تعریف می شوند که نتیجه متد را برمی گرداند belongsToMany . این belongsToMany روش توسط Illuminate\Database\Eloquent\Model کلاس پایه ارائه شده است که توسط همه مدل های Eloquent برنامه شما استفاده می شود. برای مثال، بیایید یک roles متد را روی User مدل خود تعریف کنیم. اولین آرگومان ارسال شده به این متد، نام کلاس مدل مرتبط است:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}

هنگامی که رابطه تعریف شد، می توانید با استفاده از ویژگی رابطه پویا به نقش های کاربر دسترسی داشته باشید roles :

use App\Models\User;
 
$user = User::find(1);
 
foreach ($user->roles as $role) {
// ...
}

roles از آنجایی که همه روابط به عنوان سازنده پرس و جو نیز عمل می کنند، می توانید با فراخوانی متد و ادامه دادن به شرایط زنجیره ای در پرس و جو، محدودیت های بیشتری به پرس و جوی رابطه اضافه کنید :

$roles = User::find(1)->roles()->orderBy('name')->get();

برای تعیین نام جدول جدول میانی رابطه، Eloquent دو نام مدل مرتبط را به ترتیب حروف الفبا به هم می پیوندد. با این حال، شما آزادید که این قرارداد را لغو کنید. می توانید این کار را با ارسال آرگومان دوم به belongsToMany متد انجام دهید:

return $this->belongsToMany(Role::class, 'role_user');

علاوه بر سفارشی کردن نام جدول میانی، می‌توانید با ارسال آرگومان‌های اضافی به متد، نام ستون‌های کلیدهای روی جدول را نیز سفارشی کنید belongsToMany . آرگومان سوم، نام کلید خارجی مدلی است که بر اساس آن رابطه را تعریف می کنید، در حالی که آرگومان چهارم، نام کلید خارجی مدلی است که به آن ملحق می شوید:

return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');

تعریف معکوس رابطه

برای تعریف «معکوس» یک رابطه چند به چند، باید متدی را روی مدل مرتبط تعریف کنید که نتیجه متد را نیز برمی گرداند belongsToMany . برای تکمیل مثال کاربر/نقش، اجازه دهید users روش را روی Role مدل تعریف کنیم:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}

همانطور که می بینید، رابطه دقیقاً مانند User همتای مدل خود به استثنای ارجاع به App\Models\User مدل تعریف می شود. از آنجایی که ما در حال استفاده مجدد از این belongsToMany روش هستیم، تمام جدول های معمول و گزینه های سفارشی سازی کلید هنگام تعریف «معکوس» روابط چند به چند در دسترس هستند.

بازیابی ستون های جدول میانی

همانطور که قبلاً آموختید، کار با روابط چند به چند مستلزم وجود یک جدول میانی است. Eloquent راه های بسیار مفیدی برای تعامل با این جدول ارائه می دهد. به عنوان مثال، فرض کنید User مدل ما مدل های زیادی دارد Role که به آنها مربوط می شود. پس از دسترسی به این رابطه، ممکن است با استفاده از pivot ویژگی روی مدل ها به جدول میانی دسترسی پیدا کنیم :

use App\Models\User;
 
$user = User::find(1);
 
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}

توجه داشته باشید که به هر Role مدلی که بازیابی می کنیم به طور خودکار یک pivot ویژگی اختصاص می یابد. این ویژگی شامل مدلی است که جدول میانی را نشان می دهد.

به‌طور پیش‌فرض، فقط کلیدهای مدل در pivot مدل وجود خواهند داشت. اگر جدول میانی شما دارای ویژگی های اضافی است، باید آنها را هنگام تعریف رابطه مشخص کنید:

return $this->belongsToMany(Role::class)->withPivot('active', 'created_by');

اگر می‌خواهید جدول میانی شما دارای created_at مُهرهای زمانی باشد updated_at که به‌طور خودکار توسط Eloquent نگهداری می‌شوند، withTimestamps هنگام تعریف رابطه، متد را فراخوانی کنید:

return $this->belongsToMany(Role::class)->withTimestamps();

جداول میانی که از مهرهای زمانی Eloquent استفاده می‌کنند، باید دارای ستون‌های هر دو created_at و مهر زمان باشند. updated_at

سفارشی کردن pivot نام ویژگی

همانطور که قبلا ذکر شد، ویژگی های جدول میانی ممکن است در مدل ها از طریق pivot ویژگی قابل دسترسی باشند. با این حال، شما آزاد هستید که نام این ویژگی را سفارشی کنید تا هدف آن را در برنامه شما بهتر نشان دهد.

به عنوان مثال، اگر برنامه شما دارای کاربرانی باشد که ممکن است در پادکست ها مشترک شوند، احتمالاً بین کاربران و پادکست ها رابطه چند به چند دارید. اگر اینطور است، ممکن است بخواهید به subscription جای خصیصه جدول میانی خود را به تغییر نام دهید pivot . این را می توان با استفاده از as روش در هنگام تعریف رابطه انجام داد:

return $this->belongsToMany(Podcast::class)
->as('subscription')
->withTimestamps();

هنگامی که ویژگی جدول میانی سفارشی مشخص شد، می توانید با استفاده از نام سفارشی به داده های جدول میانی دسترسی داشته باشید:

$users = User::with('podcasts')->get();
 
foreach ($users->flatMap->podcasts as $podcast) {
echo $podcast->subscription->created_at;
}

فیلتر کردن کوئری ها از طریق ستون های جدول میانی

همچنین می توانید نتایج بازگردانده شده توسط belongsToMany پرس و جوهای رابطه را با استفاده از روش های wherePivot , wherePivotIn , wherePivotNotIn , wherePivotBetween , wherePivotNotBetween , wherePivotNull و wherePivotNotNull هنگام تعریف رابطه فیلتر کنید:

return $this->belongsToMany(Role::class)
->wherePivot('approved', 1);
 
return $this->belongsToMany(Role::class)
->wherePivotIn('priority', [1, 2]);
 
return $this->belongsToMany(Role::class)
->wherePivotNotIn('priority', [1, 2]);
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNull('expired_at');
 
return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNotNull('expired_at');

سفارش پرس و جو از طریق ستون های جدول میانی

با استفاده از این روش می‌توانید نتایجی را که توسط belongsToMany کوئری‌های رابطه بازگردانده می‌شوند، سفارش دهید orderByPivot . در مثال زیر، تمام آخرین نشان‌ها را برای کاربر بازیابی می‌کنیم:

return $this->belongsToMany(Badge::class)
->where('rank', 'gold')
->orderByPivot('created_at', 'desc');

تعریف مدل های جدول میانی سفارشی

اگر می خواهید یک مدل سفارشی برای نمایش جدول میانی رابطه چند به چند خود تعریف کنید، می توانید using در هنگام تعریف رابطه، متد را فراخوانی کنید. مدل‌های محوری سفارشی به شما این فرصت را می‌دهند تا رفتارهای اضافی را روی مدل محوری تعریف کنید، مانند روش‌ها و قالب‌ها.

مدل های محوری چند به چند سفارشی باید Illuminate\Database\Eloquent\Relations\Pivot کلاس را گسترش دهند در حالی که مدل های محوری چند شکلی سفارشی باید Illuminate\Database\Eloquent\Relations\MorphPivot کلاس را گسترش دهند. به عنوان مثال، ممکن است مدلی را تعریف کنیم Role که از یک RoleUser مدل محوری سفارشی استفاده می کند:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class)->using(RoleUser::class);
}
}

هنگام تعریف RoleUser مدل، باید Illuminate\Database\Eloquent\Relations\Pivot کلاس را گسترش دهید:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Relations\Pivot;
 
class RoleUser extends Pivot
{
// ...
}

مدل‌های محوری ممکن است از این ویژگی استفاده نکنند SoftDeletes . اگر نیاز به حذف نرم رکوردهای محوری دارید، مدل محوری خود را به یک مدل واقعی Eloquent تبدیل کنید.

مدل‌های محوری سفارشی و شناسه‌های افزایشی

اگر یک رابطه چند به چند تعریف کرده‌اید که از یک مدل محوری سفارشی استفاده می‌کند، و آن مدل محوری دارای یک کلید اصلی افزایش خودکار است، باید مطمئن شوید که کلاس مدل محوری سفارشی شما یک incrementing ویژگی را تعریف می‌کند که روی true .

/**
* Indicates if the IDs are auto-incrementing.
*
* @var bool
*/
public $incrementing = true;

روابط چند شکلی

یک رابطه چند شکلی به مدل فرزند اجازه می دهد تا با استفاده از یک ارتباط واحد به بیش از یک نوع مدل تعلق داشته باشد. به عنوان مثال، تصور کنید در حال ساخت اپلیکیشنی هستید که به کاربران اجازه می دهد پست ها و ویدیوهای وبلاگ را به اشتراک بگذارند. در چنین برنامه ای، یک Comment مدل ممکن است به هر دو مدل Post و و تعلق داشته باشد Video .

یک به یک (چند شکلی)

ساختار جدول

یک رابطه چند شکلی یک به یک شبیه به یک رابطه معمولی یک به یک است. با این حال، مدل فرزند می تواند به بیش از یک نوع مدل با استفاده از یک ارتباط واحد تعلق داشته باشد. برای مثال، یک وبلاگ Post و a User ممکن است یک رابطه چند شکلی با یک Image مدل به اشتراک بگذارند. استفاده از یک رابطه چند شکلی یک به یک به شما امکان می دهد یک جدول واحد از تصاویر منحصر به فرد داشته باشید که ممکن است با پست ها و کاربران مرتبط باشد. ابتدا ساختار جدول را بررسی می کنیم:

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

imageable_id به ستون های و imageable_type روی جدول توجه کنید images . ستون imageable_id حاوی مقدار شناسه پست یا کاربر است، در حالی که imageable_type ستون حاوی نام کلاس مدل والد است. ستون imageable_type توسط Eloquent استفاده می‌شود تا تعیین کند که کدام «نوع» مدل والد را هنگام دسترسی به رابطه بازگرداند imageable . در این مورد، ستون شامل یک App\Models\Post یا App\Models\User .

ساختار مدل

سپس، اجازه دهید تعاریف مدل مورد نیاز برای ایجاد این رابطه را بررسی کنیم:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class Image extends Model
{
/**
* Get the parent imageable model (user or post).
*/
public function imageable(): MorphTo
{
return $this->morphTo();
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
 
class Post extends Model
{
/**
* Get the post's image.
*/
public function image(): MorphOne
{
return $this->morphOne(Image::class, 'imageable');
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
 
class User extends Model
{
/**
* Get the user's image.
*/
public function image(): MorphOne
{
return $this->morphOne(Image::class, 'imageable');
}
}

بازیابی رابطه

هنگامی که جدول پایگاه داده و مدل های شما تعریف شد، می توانید از طریق مدل های خود به روابط دسترسی پیدا کنید. به عنوان مثال، برای بازیابی تصویر برای یک پست، می توانیم به image ویژگی ارتباط پویا دسترسی داشته باشیم:

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

می توانید با دسترسی به نام روشی که فراخوانی را انجام می دهد، والد مدل چند شکلی را بازیابی کنید morphTo . در این مورد، این imageable روش روی Image مدل است. بنابراین، ما به آن متد به عنوان یک ویژگی رابطه پویا دسترسی خواهیم داشت:

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

بسته به نوع مدلی که تصویر را در اختیار دارد، رابطه imageable روی مدل یک یا نمونه Image برمی‌گرداند . Post User

کنوانسیون های کلیدی

در صورت لزوم، می‌توانید نام ستون‌های «id» و «type» را که توسط مدل فرزند چندشکل خود استفاده می‌شود، مشخص کنید. اگر این کار را انجام می دهید، مطمئن شوید که همیشه نام رابطه را به عنوان اولین آرگومان به morphTo متد ارسال می کنید. به طور معمول، این مقدار باید با نام روش مطابقت داشته باشد، بنابراین می توانید از ثابت PHP استفاده کنید __FUNCTION__ :

/**
* Get the model that the image belongs to.
*/
public function imageable(): MorphTo
{
return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id');
}

یک به چند (چند شکلی)

ساختار جدول

یک رابطه چند شکلی یک به چند شبیه به یک رابطه معمولی یک به چند است. با این حال، مدل فرزند می تواند به بیش از یک نوع مدل با استفاده از یک ارتباط واحد تعلق داشته باشد. به عنوان مثال، تصور کنید کاربران برنامه شما می توانند روی پست ها و ویدیوها "نظر بدهند". با استفاده از روابط چند شکلی، می توانید از یک comments جدول واحد برای حاوی نظرات برای پست ها و ویدیوها استفاده کنید. ابتدا، بیایید ساختار جدول مورد نیاز برای ایجاد این رابطه را بررسی کنیم:

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

ساختار مدل

سپس، اجازه دهید تعاریف مدل مورد نیاز برای ایجاد این رابطه را بررسی کنیم:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class Comment extends Model
{
/**
* Get the parent commentable model (post or video).
*/
public function commentable(): MorphTo
{
return $this->morphTo();
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
 
class Post extends Model
{
/**
* Get all of the post's comments.
*/
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
}
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
 
class Video extends Model
{
/**
* Get all of the video's comments.
*/
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
}

بازیابی رابطه

هنگامی که جدول پایگاه داده و مدل‌های شما تعریف شد، می‌توانید از طریق ویژگی‌های رابطه پویا مدل خود به روابط دسترسی پیدا کنید. به عنوان مثال، برای دسترسی به تمام نظرات یک پست، می توانیم از comments ویژگی پویا استفاده کنیم:

use App\Models\Post;
 
$post = Post::find(1);
 
foreach ($post->comments as $comment) {
// ...
}

همچنین می‌توانید والد یک مدل فرزند چندشکلی را با دسترسی به نام روشی که فراخوانی را انجام می‌دهد، بازیابی کنید morphTo . در این مورد، این commentable روش روی Comment مدل است. بنابراین، برای دسترسی به مدل والد نظر، به آن متد به عنوان یک ویژگی رابطه پویا دسترسی خواهیم داشت:

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

بسته به نوع مدلی که والد نظر است، رابطه commentable روی مدل یک یا نمونه Comment برمی‌گرداند . Post Video

یکی از بسیاری (چند شکلی)

گاهی اوقات یک مدل ممکن است مدل‌های مرتبط زیادی داشته باشد، اما شما می‌خواهید به راحتی «آخرین» یا «قدیمی‌ترین» مدل مرتبط رابطه را بازیابی کنید. به عنوان مثال، یک User مدل ممکن است به بسیاری از Image مدل‌ها مرتبط باشد، اما شما می‌خواهید روشی مناسب برای تعامل با جدیدترین تصویری که کاربر آپلود کرده است، تعریف کنید. شما می توانید این کار را با استفاده از morphOne نوع رابطه ترکیب شده با ofMany روش های زیر انجام دهید:

/**
* Get the user's most recent image.
*/
public function latestImage(): MorphOne
{
return $this->morphOne(Image::class, 'imageable')->latestOfMany();
}

به همین ترتیب، می‌توانید روشی را برای بازیابی «قدیمی‌ترین» یا اولین مدل مرتبط یک رابطه تعریف کنید:

/**
* Get the user's oldest image.
*/
public function oldestImage(): MorphOne
{
return $this->morphOne(Image::class, 'imageable')->oldestOfMany();
}

به طور پیش‌فرض، متدهای latestOfMany و oldestOfMany آخرین یا قدیمی‌ترین مدل مرتبط را بر اساس کلید اصلی مدل، که باید قابل مرتب‌سازی باشد، بازیابی می‌کنند. با این حال، گاهی اوقات ممکن است بخواهید یک مدل واحد را از یک رابطه بزرگتر با استفاده از معیارهای مرتب سازی متفاوت بازیابی کنید.

به عنوان مثال، با استفاده از ofMany روش، ممکن است بیشترین "پسند" تصویر کاربر را بازیابی کنید. این ofMany متد ستون قابل مرتب‌سازی را به‌عنوان اولین آرگومان خود می‌پذیرد و کدام تابع جمع ( min یا max ) را هنگام پرس و جو برای مدل مرتبط اعمال می‌کند:

/**
* Get the user's most popular image.
*/
public function bestImage(): MorphOne
{
return $this->morphOne(Image::class, 'imageable')->ofMany('likes', 'max');
}

ایجاد روابط پیشرفته تر "یکی از چندین" امکان پذیر است. برای کسب اطلاعات بیشتر، لطفاً به یکی از اسناد متعدد مراجعه کنید .

خیلی به خیلی ها (چند شکلی)

ساختار جدول

روابط چند شکلی خیلی به چند کمی پیچیده تر از روابط «مورف یک» و «مورف بسیاری» است. به عنوان مثال، یک Post مدل و Video مدل می توانند یک رابطه چند شکلی با یک Tag مدل به اشتراک بگذارند. استفاده از یک رابطه چند شکلی چند به چند در این شرایط به برنامه شما اجازه می دهد تا یک جدول واحد از برچسب های منحصر به فرد داشته باشد که ممکن است با پست ها یا ویدیوها مرتبط باشد. ابتدا، بیایید ساختار جدول مورد نیاز برای ایجاد این رابطه را بررسی کنیم:

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

قبل از فرو رفتن در روابط چند شکلی چند به چند، ممکن است از خواندن اسناد مربوط به روابط معمولی چند به چند بهره مند شوید .

ساختار مدل

در مرحله بعد، ما آماده تعریف روابط روی مدل ها هستیم. مدل های Post و Video هر دو حاوی متدی خواهند بود tags که morphToMany متد ارائه شده توسط کلاس مدل پایه Eloquent را فراخوانی می کند.

این morphToMany روش نام مدل مرتبط و همچنین "نام رابطه" را می پذیرد. بر اساس نامی که به نام جدول میانی خود اختصاص داده‌ایم و کلیدهایی که در آن وجود دارد، به این رابطه به عنوان "taggable" اشاره خواهیم کرد:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class Post extends Model
{
/**
* Get all of the tags for the post.
*/
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable');
}
}

تعریف معکوس رابطه

در مرحله بعد، روی Tag مدل، باید برای هر یک از مدل‌های والد احتمالی آن، یک متد تعریف کنید. بنابراین در این مثال یک posts متد و یک videos متد را تعریف می کنیم. هر دوی این روش ها باید نتیجه روش را برگردانند morphedByMany .

این morphedByMany روش نام مدل مرتبط و همچنین "نام رابطه" را می پذیرد. بر اساس نامی که به نام جدول میانی خود اختصاص داده‌ایم و کلیدهایی که در آن وجود دارد، به این رابطه به عنوان "taggable" اشاره خواهیم کرد:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class Tag extends Model
{
/**
* Get all of the posts that are assigned this tag.
*/
public function posts(): MorphToMany
{
return $this->morphedByMany(Post::class, 'taggable');
}
 
/**
* Get all of the videos that are assigned this tag.
*/
public function videos(): MorphToMany
{
return $this->morphedByMany(Video::class, 'taggable');
}
}

بازیابی رابطه

هنگامی که جدول پایگاه داده و مدل های شما تعریف شد، می توانید از طریق مدل های خود به روابط دسترسی پیدا کنید. به عنوان مثال، برای دسترسی به تمام برچسب‌های یک پست، می‌توانید از tags ویژگی ارتباط پویا استفاده کنید:

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

می توانید با دسترسی به نام روشی که فراخوانی را انجام می دهد، والد یک رابطه چند شکلی را از مدل فرزند چند شکلی بازیابی کنید morphedByMany . در این مورد، این posts یا videos متدهای موجود در Tag مدل است:

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

انواع چند شکلی سفارشی

به‌طور پیش‌فرض، لاراول از نام کلاس کاملاً واجد شرایط برای ذخیره «نوع» مدل مرتبط استفاده می‌کند. به عنوان مثال، با توجه به مثال رابطه یک به چند در بالا که در آن یک Comment مدل ممکن است به یک Post یا یک Video مدل تعلق داشته باشد، پیش‌فرض به ترتیب یا یا commentable_type خواهد بود . با این حال، ممکن است بخواهید این مقادیر را از ساختار داخلی برنامه خود جدا کنید. App\Models\Post App\Models\Video

به عنوان مثال، به جای استفاده از نام مدل به عنوان "نوع"، ممکن است از رشته های ساده مانند post و استفاده کنیم video . با انجام این کار، مقادیر ستون "نوع" چند شکلی در پایگاه داده ما، حتی اگر مدل ها تغییر نام داده شوند، معتبر می مانند:

use Illuminate\Database\Eloquent\Relations\Relation;
 
Relation::enforceMorphMap([
'post' => 'App\Models\Post',
'video' => 'App\Models\Video',
]);

در صورت تمایل می توانید enforceMorphMap متد را در boot متد کلاس خود فراخوانی کنید یا یک ارائه دهنده خدمات جداگانه ایجاد کنید. App\Providers\AppServiceProvider

شما می توانید نام مستعار مورف یک مدل معین را در زمان اجرا با استفاده از روش مدل تعیین کنید getMorphClass . برعکس، می‌توانید نام کلاس کاملاً واجد شرایط مرتبط با نام مستعار مورف را با استفاده از Relation::getMorphedModel روش تعیین کنید:

use Illuminate\Database\Eloquent\Relations\Relation;
 
$alias = $post->getMorphClass();
 
$class = Relation::getMorphedModel($alias);

هنگام افزودن یک "نقشه مورف" به برنامه موجود خود، هر *_type مقدار ستون قابل تغییر در پایگاه داده شما که هنوز دارای یک کلاس کاملاً واجد شرایط است باید به نام "نقشه" آن تبدیل شود.

روابط پویا

می توانید از این resolveRelationUsing روش برای تعریف روابط بین مدل های Eloquent در زمان اجرا استفاده کنید. اگرچه معمولاً برای توسعه نرم‌افزارهای معمولی توصیه نمی‌شود، اما گاهی اوقات ممکن است هنگام توسعه بسته‌های لاراول مفید باشد.

متد resolveRelationUsing نام رابطه مورد نظر را به عنوان اولین آرگومان خود می پذیرد. آرگومان دومی که به متد ارسال می‌شود باید یک بسته باشد که نمونه مدل را می‌پذیرد و یک تعریف معتبر رابطه Eloquent را برمی‌گرداند. به طور معمول، باید روابط پویا را در روش بوت یک ارائه دهنده خدمات پیکربندی کنید :

use App\Models\Order;
use App\Models\Customer;
 
Order::resolveRelationUsing('customer', function (Order $orderModel) {
return $orderModel->belongsTo(Customer::class, 'customer_id');
});

هنگام تعریف روابط پویا، همیشه آرگومان های نام کلیدی صریح را برای روش های رابطه Eloquent ارائه دهید.

روابط پرس و جو

از آنجایی که تمام روابط Eloquent از طریق متدها تعریف می‌شوند، می‌توانید آن متدها را برای به دست آوردن نمونه‌ای از رابطه فراخوانی کنید، بدون اینکه واقعاً یک پرس و جو برای بارگذاری مدل‌های مرتبط اجرا کنید. علاوه بر این، همه انواع روابط Eloquent نیز به عنوان سازندگان پرس و جو عمل می کنند ، و به شما این امکان را می دهند تا قبل از اجرای نهایی پرس و جو SQL در پایگاه داده خود، به زنجیره محدودیت ها در کوئری رابطه ادامه دهید.

به عنوان مثال، یک برنامه وبلاگ را تصور کنید که در آن یک User مدل دارای چندین Post مدل مرتبط است:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
 
class User extends Model
{
/**
* Get all of the posts for the user.
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}

شما می توانید posts رابطه را پرس و جو کنید و محدودیت های اضافی را به این رابطه اضافه کنید:

use App\Models\User;
 
$user = User::find(1);
 
$user->posts()->where('active', 1)->get();

شما می توانید از هر یک از متدهای سازنده پرس و جو لاراول در رابطه استفاده کنید، پس حتما اسناد سازنده پرس و جو را بررسی کنید تا در مورد همه روش هایی که در دسترس شما هستند اطلاعات کسب کنید.

بندهای زنجیره ای orWhere بعد از روابط

همانطور که در مثال بالا نشان داده شد، شما می توانید در هنگام پرس و جوی روابط، محدودیت های اضافی را به آنها اضافه کنید. با این حال، هنگام زنجیر کردن orWhere جملات به یک رابطه احتیاط کنید، زیرا orWhere بندها به طور منطقی در همان سطح محدودیت رابطه گروه بندی می شوند:

$user->posts()
->where('active', 1)
->orWhere('votes', '>=', 100)
->get();

مثال بالا SQL زیر را تولید می کند. همانطور که می بینید، این بند به پرس و جو دستور می دهد تا هر پستی که بیش از 100 رای داشته باشد را or برگرداند . پرس و جو دیگر محدود به یک کاربر خاص نیست:

select *
from posts
where user_id = ? and active = 1 or votes >= 100

در بیشتر شرایط، باید از گروه های منطقی برای گروه بندی بررسی های شرطی بین پرانتز استفاده کنید:

use Illuminate\Database\Eloquent\Builder;
 
$user->posts()
->where(function (Builder $query) {
return $query->where('active', 1)
->orWhere('votes', '>=', 100);
})
->get();

مثال بالا SQL زیر را تولید می کند. توجه داشته باشید که گروه بندی منطقی محدودیت ها را به درستی گروه بندی کرده است و پرس و جو برای یک کاربر خاص محدود می ماند:

select *
from posts
where user_id = ? and (active = 1 or votes >= 100)

روش های رابطه در مقابل ویژگی های پویا

اگر نیازی به اضافه کردن محدودیت‌های اضافی به پرس و جوی رابطه Eloquent ندارید، می‌توانید به این رابطه مانند یک ویژگی دسترسی داشته باشید. برای مثال، با ادامه استفاده از مدل‌های ما User و Post نمونه، ممکن است به همه پست‌های کاربر مانند این دسترسی داشته باشیم:

use App\Models\User;
 
$user = User::find(1);
 
foreach ($user->posts as $post) {
// ...
}

ویژگی های رابطه پویا "بارگذاری تنبل" را انجام می دهند، به این معنی که آنها فقط زمانی که شما واقعا به آنها دسترسی داشته باشید، داده های رابطه خود را بارگذاری می کنند. به همین دلیل، توسعه دهندگان اغلب از بارگذاری مشتاق برای پیش بارگذاری روابطی استفاده می کنند که می دانند پس از بارگذاری مدل به آن دسترسی خواهند داشت. بارگذاری مشتاق کاهش قابل توجهی در پرس و جوهای SQL را فراهم می کند که باید برای بارگذاری روابط یک مدل اجرا شوند.

پرس و جو از وجود رابطه

هنگام بازیابی رکوردهای مدل، ممکن است بخواهید نتایج خود را بر اساس وجود یک رابطه محدود کنید. به عنوان مثال، تصور کنید می خواهید تمام پست های وبلاگی را که حداقل یک نظر دارند، بازیابی کنید. برای انجام این کار، می توانید نام رابطه را به has و orHas متدها ارسال کنید:

use App\Models\Post;
 
// Retrieve all posts that have at least one comment...
$posts = Post::has('comments')->get();

همچنین می توانید یک عملگر و مقدار شمارش را برای سفارشی کردن بیشتر پرس و جو تعیین کنید:

// Retrieve all posts that have three or more comments...
$posts = Post::has('comments', '>=', 3)->get();

عبارات تودرتو has ممکن است با استفاده از نماد "نقطه" ساخته شوند. برای مثال، می‌توانید تمام پست‌هایی را که حداقل یک نظر دارند که حداقل یک تصویر دارند، بازیابی کنید:

// Retrieve posts that have at least one comment with images...
$posts = Post::has('comments.images')->get();

اگر حتی به قدرت بیشتری نیاز دارید، می‌توانید از روش‌ها whereHas و orWhereHas برای تعریف محدودیت‌های پرس و جو اضافی در has جستارهای خود استفاده کنید، مانند بررسی محتوای یک نظر:

use Illuminate\Database\Eloquent\Builder;
 
// Retrieve posts with at least one comment containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
 
// Retrieve posts with at least ten comments containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();

Eloquent در حال حاضر از پرس و جو برای وجود رابطه در میان پایگاه های داده پشتیبانی نمی کند. روابط باید در یک پایگاه داده وجود داشته باشد.

پرس و جوهای وجود رابطه درون خطی

اگر می خواهید وجود یک رابطه را با یک شرط ساده و منفرد که به پرس و جوی رابطه متصل شده است، جستجو کنید، ممکن است استفاده از روش های، و، و را whereRelation راحت orWhereRelation تر whereMorphRelation بیابید orWhereMorphRelation . به عنوان مثال، ما ممکن است برای همه پست هایی که نظرات تایید نشده ای دارند، پرس و جو کنیم:

use App\Models\Post;
 
$posts = Post::whereRelation('comments', 'is_approved', false)->get();

البته، مانند فراخوانی‌های متد سازنده پرس و جو where ، می‌توانید یک اپراتور را نیز مشخص کنید:

$posts = Post::whereRelation(
'comments', 'created_at', '>=', now()->subHour()
)->get();

پرس و جو عدم وجود رابطه

هنگام بازیابی رکوردهای مدل، ممکن است بخواهید نتایج خود را بر اساس عدم وجود رابطه محدود کنید. به عنوان مثال، تصور کنید می خواهید تمام پست های وبلاگی را که هیچ نظری ندارند، بازیابی کنید. برای انجام این کار، می توانید نام رابطه را به doesntHave و orDoesntHave متدها ارسال کنید:

use App\Models\Post;
 
$posts = Post::doesntHave('comments')->get();

اگر حتی به قدرت بیشتری نیاز دارید، می‌توانید از روش‌های whereDoesntHave و orWhereDoesntHave برای افزودن محدودیت‌های پرس و جو اضافی به doesntHave درخواست‌های خود استفاده کنید، مانند بررسی محتوای یک نظر:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::whereDoesntHave('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();

می توانید از نماد "نقطه" برای اجرای یک پرس و جو در برابر یک رابطه تودرتو استفاده کنید. به عنوان مثال، پرس و جو زیر همه پست هایی را که نظر ندارند بازیابی می کند. با این حال، پست هایی که نظرات نویسندگانی دارند که ممنوع نشده اند در نتایج گنجانده می شوند:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::whereDoesntHave('comments.author', function (Builder $query) {
$query->where('banned', 0);
})->get();

پرس و جوی شکل به روابط

برای پرس و جو در مورد وجود روابط "morph to"، می توانید از روش whereHasMorph و استفاده کنید whereDoesntHaveMorph . این روش ها نام رابطه را به عنوان اولین استدلال خود می پذیرند. در مرحله بعد، متدها نام مدل‌های مرتبطی را که می‌خواهید در پرس و جو قرار دهید، می‌پذیرند. در نهایت، می‌توانید بسته‌ای را ارائه کنید که پرس و جوی رابطه را سفارشی می‌کند:

use App\Models\Comment;
use App\Models\Post;
use App\Models\Video;
use Illuminate\Database\Eloquent\Builder;
 
// Retrieve comments associated to posts or videos with a title like code%...
$comments = Comment::whereHasMorph(
'commentable',
[Post::class, Video::class],
function (Builder $query) {
$query->where('title', 'like', 'code%');
}
)->get();
 
// Retrieve comments associated to posts with a title not like code%...
$comments = Comment::whereDoesntHaveMorph(
'commentable',
Post::class,
function (Builder $query) {
$query->where('title', 'like', 'code%');
}
)->get();

ممکن است گاهی نیاز به افزودن محدودیت‌های پرس و جو بر اساس «نوع» مدل چندشکلی مرتبط داشته باشید. بسته شدن به whereHasMorph متد ممکن است یک $type مقدار را به عنوان آرگومان دوم خود دریافت کند. این آرگومان به شما امکان می دهد تا "نوع" پرس و جوی ساخته شده را بررسی کنید:

use Illuminate\Database\Eloquent\Builder;
 
$comments = Comment::whereHasMorph(
'commentable',
[Post::class, Video::class],
function (Builder $query, string $type) {
$column = $type === Post::class ? 'content' : 'title';
 
$query->where($column, 'like', 'code%');
}
)->get();

به جای ارسال آرایه‌ای از مدل‌های چند شکلی ممکن، می‌توانید * به عنوان یک مقدار عام ارائه دهید. این به لاراول دستور می دهد تا همه انواع چند شکلی ممکن را از پایگاه داده بازیابی کند. لاراول یک کوئری اضافی را برای انجام این عملیات اجرا می کند:

use Illuminate\Database\Eloquent\Builder;
 
$comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) {
$query->where('title', 'like', 'foo%');
})->get();

گاهی اوقات ممکن است بخواهید تعداد مدل های مرتبط را برای یک رابطه مشخص بدون بارگذاری واقعی مدل ها بشمارید. برای انجام این کار، می توانید از withCount روش استفاده کنید. این روش یک ویژگی را بر روی مدل های به دست آمده withCount قرار می دهد : {relation}_count

use App\Models\Post;
 
$posts = Post::withCount('comments')->get();
 
foreach ($posts as $post) {
echo $post->comments_count;
}

با ارسال یک آرایه به withCount متد، می‌توانید «counts» را برای چندین رابطه اضافه کنید و همچنین محدودیت‌های اضافی را به کوئری‌ها اضافه کنید:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::withCount(['votes', 'comments' => function (Builder $query) {
$query->where('content', 'like', 'code%');
}])->get();
 
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

همچنین می‌توانید با نام مستعار نتیجه شمارش رابطه، شمارش‌های متعدد در یک رابطه را مجاز کنید:

use Illuminate\Database\Eloquent\Builder;
 
$posts = Post::withCount([
'comments',
'comments as pending_comments_count' => function (Builder $query) {
$query->where('approved', false);
},
])->get();
 
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;

بارگیری تعداد معوق

با استفاده از این loadCount روش، می‌توانید یک تعداد رابطه را پس از بازیابی مدل والد بارگیری کنید:

$book = Book::first();
 
$book->loadCount('genres');

اگر نیاز به تعیین محدودیت‌های پرس و جو اضافی در پرس و جوی تعداد دارید، می‌توانید آرایه‌ای را ارسال کنید که توسط روابطی که می‌خواهید شمارش کنید، کلید می‌زنید. مقادیر آرایه باید بسته هایی باشند که نمونه سازنده query را دریافت می کنند:

$book->loadCount(['reviews' => function (Builder $query) {
$query->where('rating', 5);
}])

بیانیه های شمارش رابطه و انتخاب سفارشی

withCount اگر با یک دستور ترکیب می‌کنید ، مطمئن شوید که بعد از متد select فراخوانی می‌کنید : withCount select

$posts = Post::select(['title', 'body'])
->withCount('comments')
->get();

سایر توابع جمع

withCount Eloquent علاوه بر روش، روش های , , withMin , withMax و را ارائه می دهد . این روش ها یک ویژگی را در مدل های به دست آمده شما قرار می دهند : withAvg withSum withExists {relation}_{function}_{column}

use App\Models\Post;
 
$posts = Post::withSum('comments', 'votes')->get();
 
foreach ($posts as $post) {
echo $post->comments_sum_votes;
}

اگر می‌خواهید با استفاده از نام دیگری به نتیجه تابع مجموع دسترسی پیدا کنید، می‌توانید نام مستعار خود را مشخص کنید:

$posts = Post::withSum('comments as total_comments', 'votes')->get();
 
foreach ($posts as $post) {
echo $post->total_comments;
}

مانند loadCount روش، نسخه های معوق این روش ها نیز موجود است. این عملیات جمع اضافی ممکن است بر روی مدل های Eloquent که قبلاً بازیابی شده اند انجام شود:

$post = Post::first();
 
$post->loadSum('comments', 'votes');

اگر این متدهای انبوه را با یک دستور ترکیب می کنید select ، مطمئن شوید که متدهای انبوه را بعد از select متد فراخوانی می کنید:

$posts = Post::select(['title', 'body'])
->withExists('comments')
->get();

اگر می‌خواهید مشتاقانه یک رابطه «morph to» و همچنین تعداد مدل‌های مرتبط برای موجودیت‌های مختلفی را که ممکن است توسط آن رابطه برگردانده شوند بارگیری کنید، می‌توانید از این with روش در ترکیب با روش morphTo رابطه استفاده کنید morphWithCount .

در این مثال، فرض می کنیم که Photo و Post مدل ها ممکن است ActivityFeed مدل هایی ایجاد کنند. فرض می کنیم که ActivityFeed مدل یک رابطه "morph to" به نام تعریف می کند parentable که به ما امکان می دهد والد Photo یا Post مدل را برای یک ActivityFeed نمونه معین بازیابی کنیم. علاوه بر این، فرض کنیم که Photo مدل‌ها Tag مدل‌های «بسیار» دارند و Post مدل‌ها Comment مدل‌های «بسیار» دارند.

حال، بیایید تصور کنیم که می‌خواهیم ActivityFeed نمونه‌ها را بازیابی کنیم و مشتاقانه parentable مدل‌های والد را برای هر ActivityFeed نمونه بارگذاری کنیم. علاوه بر این، می‌خواهیم تعداد برچسب‌هایی را که با هر عکس والدین مرتبط است و تعداد نظرات مرتبط با هر پست والدین را بازیابی کنیم:

use Illuminate\Database\Eloquent\Relations\MorphTo;
 
$activities = ActivityFeed::with([
'parentable' => function (MorphTo $morphTo) {
$morphTo->morphWithCount([
Photo::class => ['tags'],
Post::class => ['comments'],
]);
}])->get();

بارگیری تعداد معوق

بیایید فرض کنیم قبلاً مجموعه‌ای از ActivityFeed مدل‌ها را بازیابی کرده‌ایم و اکنون می‌خواهیم تعداد روابط تودرتو را برای parentable مدل‌های مختلف مرتبط با فیدهای فعالیت بارگیری کنیم. loadMorphCount برای انجام این کار می توانید از روش زیر استفاده کنید :

$activities = ActivityFeed::with('parentable')->get();
 
$activities->loadMorphCount('parentable', [
Photo::class => ['tags'],
Post::class => ['comments'],
]);

مشتاق بارگیری

هنگام دسترسی به روابط Eloquent به عنوان ویژگی، مدل های مرتبط "بارگذاری تنبل" هستند. این بدان معنی است که تا زمانی که شما برای اولین بار به ویژگی دسترسی نداشته باشید، داده های رابطه در واقع بارگذاری نمی شوند. با این حال، Eloquent می‌تواند روابط را در زمانی که مدل والد را پرس و جو می‌کنید، «بارگیری کند». بارگیری مشتاق مشکل پرس و جو "N + 1" را کاهش می دهد. برای نشان دادن مشکل پرس و جو N + 1، Book مدلی را در نظر بگیرید که به یک Author مدل "متعلق دارد":

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author(): BelongsTo
{
return $this->belongsTo(Author::class);
}
}

اکنون بیایید همه کتاب ها و نویسندگان آنها را بازیابی کنیم:

use App\Models\Book;
 
$books = Book::all();
 
foreach ($books as $book) {
echo $book->author->name;
}

این حلقه یک پرس و جو را برای بازیابی همه کتاب های موجود در جدول پایگاه داده و سپس یک پرس و جو برای هر کتاب به منظور بازیابی نویسنده کتاب اجرا می کند. بنابراین، اگر 25 کتاب داشته باشیم، کد بالا 26 پرس و جو را اجرا می کند: یکی برای کتاب اصلی، و 25 درخواست اضافی برای بازیابی نویسنده هر کتاب.

خوشبختانه، ما می توانیم از بارگذاری مشتاق برای کاهش این عملیات به تنها دو پرس و جو استفاده کنیم. هنگام ساخت یک پرس و جو، می توانید مشخص کنید که کدام روابط باید با استفاده از روش بارگذاری شوند with :

$books = Book::with('author')->get();
 
foreach ($books as $book) {
echo $book->author->name;
}

برای این عملیات، فقط دو پرس و جو اجرا می شود - یک پرس و جو برای بازیابی همه کتاب ها و یک پرس و جو برای بازیابی همه نویسندگان برای همه کتاب ها:

select * from books
 
select * from authors where id in (1, 2, 3, 4, 5, ...)

مشتاق بارگیری روابط چندگانه

گاهی اوقات ممکن است نیاز داشته باشید که مشتاقانه چندین رابطه مختلف را بارگذاری کنید. برای انجام این کار، کافی است یک آرایه از روابط را به with متد ارسال کنید:

$books = Book::with(['author', 'publisher'])->get();

Nested Eager Loading

برای بارگذاری مشتاقانه روابط یک رابطه، می توانید از نحو "نقطه" استفاده کنید. برای مثال، بیایید مشتاقانه همه نویسندگان کتاب و همه مخاطبین شخصی نویسنده را بارگیری کنیم:

$books = Book::with('author.contacts')->get();

همچنین، می‌توانید با ارائه یک آرایه تودرتو به روش، روابط بارگذاری شده تودرتو را مشخص کنید with ، که می‌تواند هنگام بارگیری چندین رابطه تودرتو راحت باشد:

$books = Book::with([
'author' => [
'contacts',
'publisher',
],
])->get();

morphTo روابط بارگیری مشتاق تو در تو

اگر می‌خواهید مشتاقانه یک morphTo رابطه و همچنین روابط تودرتو در موجودیت‌های مختلفی را که ممکن است توسط آن رابطه برگردانده شوند بارگذاری کنید، می‌توانید از این with روش در ترکیب با روش morphTo رابطه استفاده کنید morphWith . برای کمک به توضیح این روش، بیایید مدل زیر را در نظر بگیریم:

<?php
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class ActivityFeed extends Model
{
/**
* Get the parent of the activity feed record.
*/
public function parentable(): MorphTo
{
return $this->morphTo();
}
}

در این مثال، فرض کنید Event ، Photo و Post مدل ها ممکن است ActivityFeed مدل هایی ایجاد کنند. علاوه بر این، فرض کنیم که Event مدل‌ها متعلق به یک Calendar مدل هستند، Photo مدل‌ها با مدل‌ها مرتبط هستند Tag ، و Post مدل‌ها متعلق به یک Author مدل هستند.

با استفاده از این تعاریف و روابط مدل، ممکن است نمونه‌های مدل را بازیابی کنیم ActivityFeed و مشتاقانه همه parentable مدل‌ها و روابط تودرتوی مربوطه را بارگیری کنیم:

use Illuminate\Database\Eloquent\Relations\MorphTo;
 
$activities = ActivityFeed::query()
->with(['parentable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
Event::class => ['calendar'],
Photo::class => ['tags'],
Post::class => ['author'],
]);
}])->get();

Eager Loading ستون های خاص

ممکن است همیشه به هر ستون از روابطی که بازیابی می کنید نیاز نداشته باشید. به همین دلیل، Eloquent به شما اجازه می دهد تا مشخص کنید کدام ستون از رابطه را می خواهید بازیابی کنید:

$books = Book::with('author:id,name,book_id')->get();

هنگام استفاده از این ویژگی، همیشه باید id ستون و هر ستون کلید خارجی مربوطه را در لیست ستون هایی که می خواهید بازیابی کنید، قرار دهید.

بارگیری مشتاق به صورت پیش فرض

گاهی اوقات ممکن است بخواهید همیشه برخی از روابط را هنگام بازیابی یک مدل بارگیری کنید. برای انجام این کار، می توانید یک $with ویژگی را در مدل تعریف کنید:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Book extends Model
{
/**
* The relationships that should always be loaded.
*
* @var array
*/
protected $with = ['author'];
 
/**
* Get the author that wrote the book.
*/
public function author(): BelongsTo
{
return $this->belongsTo(Author::class);
}
 
/**
* Get the genre of the book.
*/
public function genre(): BelongsTo
{
return $this->belongsTo(Genre::class);
}
}

اگر می خواهید $with برای یک پرس و جو، موردی را از ویژگی حذف کنید، می توانید از without روش زیر استفاده کنید:

$books = Book::without('author')->get();

اگر می‌خواهید همه آیتم‌های داخل $with ویژگی را برای یک پرس‌وجو لغو کنید، می‌توانید از withOnly روش زیر استفاده کنید:

$books = Book::withOnly('genre')->get();

محدود کردن بارهای مشتاق

گاهی اوقات ممکن است بخواهید مشتاقانه یک رابطه را بارگیری کنید، اما همچنین شرایط پرس و جو اضافی را برای درخواست بارگیری مشتاق مشخص کنید. شما می توانید این کار را با ارسال یک آرایه از روابط به with روشی انجام دهید که در آن کلید آرایه یک نام رابطه است و مقدار آرایه یک بسته است که محدودیت های اضافی را به درخواست بارگیری مشتاق اضافه می کند:

use App\Models\User;
use Illuminate\Contracts\Database\Eloquent\Builder;
 
$users = User::with(['posts' => function (Builder $query) {
$query->where('title', 'like', '%code%');
}])->get();

در این مثال، Eloquent فقط مشتاقانه پست‌هایی را بارگذاری می‌کند که ستون پست title حاوی کلمه باشد code . برای سفارشی کردن بیشتر عملیات بارگذاری مشتاق، می توانید سایر روش های سازنده پرس و جو را فراخوانی کنید:

$users = User::with(['posts' => function (Builder $query) {
$query->orderBy('created_at', 'desc');
}])->get();

بارگذاری مشتاق محدود morphTo روابط

اگر مشتاق بارگیری یک رابطه هستید morphTo ، Eloquent چندین پرس‌وجو را برای واکشی هر نوع مدل مرتبط اجرا می‌کند. می توانید با استفاده از روش MorphTo رابطه ، محدودیت های اضافی را به هر یک از این پرس و جوها اضافه کنید constrain :

use Illuminate\Database\Eloquent\Relations\MorphTo;
 
$comments = Comment::with(['commentable' => function (MorphTo $morphTo) {
$morphTo->constrain([
Post::class => function ($query) {
$query->whereNull('hidden_at');
},
Video::class => function ($query) {
$query->where('type', 'educational');
},
]);
}])->get();

در این مثال، Eloquent فقط مشتاقانه پست هایی را بارگذاری می کند که پنهان نشده اند و ویدیوهایی که دارای type ارزش "آموزشی" هستند.

محدود کردن بارهای مشتاق با وجود رابطه

ممکن است گاهی اوقات نیاز به بررسی وجود یک رابطه داشته باشید در حالی که به طور همزمان رابطه را بر اساس همان شرایط بارگذاری می کنید. برای مثال، ممکن است بخواهید فقط User مدل‌هایی را بازیابی کنید که دارای Post مدل‌های فرزندی هستند که با شرایط پرس‌وجو مطابقت دارند و در عین حال مشتاق بارگیری پست‌های منطبق هستند. شما می توانید این کار را با استفاده از withWhereHas روش زیر انجام دهید:

use App\Models\User;
 
$users = User::withWhereHas('posts', function ($query) {
$query->where('featured', true);
})->get();

تنبل مشتاق بارگیری

گاهی اوقات ممکن است نیاز داشته باشید که مشتاقانه یک رابطه را پس از بازیابی مدل والد بارگیری کنید. برای مثال، اگر بخواهید به صورت پویا تصمیم بگیرید که آیا مدل‌های مرتبط را بارگیری کنید، این ممکن است مفید باشد:

use App\Models\Book;
 
$books = Book::all();
 
if ($someCondition) {
$books->load('author', 'publisher');
}

اگر نیاز به تعیین محدودیت‌های پرس و جو اضافی در پرس‌وجوی بارگیری مشتاق دارید، می‌توانید آرایه‌ای را ارسال کنید که توسط روابطی که می‌خواهید بارگیری کنید کلید خورده است. مقادیر آرایه باید نمونه های بسته شدنی باشند که نمونه پرس و جو را دریافت می کنند:

$author->load(['books' => function (Builder $query) {
$query->orderBy('published_date', 'asc');
}]);

برای بارگیری یک رابطه فقط زمانی که قبلاً بارگذاری نشده باشد، از loadMissing روش استفاده کنید:

$book->loadMissing('author');

Nested Lazy Eager Loading و morphTo

اگر می‌خواهید مشتاقانه یک morphTo رابطه و همچنین روابط تودرتو در موجودیت‌های مختلفی که ممکن است توسط آن رابطه برگردانده شوند بارگذاری کنید، می‌توانید از این loadMorph روش استفاده کنید.

این روش نام morphTo رابطه را به عنوان اولین آرگومان خود و آرایه ای از جفت های مدل / رابطه را به عنوان آرگومان دوم می پذیرد. برای کمک به توضیح این روش، بیایید مدل زیر را در نظر بگیریم:

<?php
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
 
class ActivityFeed extends Model
{
/**
* Get the parent of the activity feed record.
*/
public function parentable(): MorphTo
{
return $this->morphTo();
}
}

در این مثال، فرض کنید Event ، Photo و Post مدل ها ممکن است ActivityFeed مدل هایی ایجاد کنند. علاوه بر این، فرض کنیم که Event مدل‌ها متعلق به یک Calendar مدل هستند، Photo مدل‌ها با مدل‌ها مرتبط هستند Tag ، و Post مدل‌ها متعلق به یک Author مدل هستند.

با استفاده از این تعاریف و روابط مدل، ممکن است نمونه‌های مدل را بازیابی کنیم ActivityFeed و مشتاقانه همه parentable مدل‌ها و روابط تودرتوی مربوطه را بارگیری کنیم:

$activities = ActivityFeed::with('parentable')
->get()
->loadMorph('parentable', [
Event::class => ['calendar'],
Photo::class => ['tags'],
Post::class => ['author'],
]);

جلوگیری از بارگذاری تنبل

همانطور که قبلاً بحث شد، روابط بارگذاری مشتاق اغلب می تواند مزایای عملکرد قابل توجهی را برای برنامه شما فراهم کند. بنابراین، در صورت تمایل، می توانید به لاراول دستور دهید که همیشه از بارگذاری تنبل روابط جلوگیری کند. برای انجام این کار، می توانید preventLazyLoading روش ارائه شده توسط کلاس مدل پایه Eloquent را فراخوانی کنید. به طور معمول، شما باید این متد را در boot متد کلاس برنامه خود فراخوانی کنید AppServiceProvider .

این preventLazyLoading روش یک آرگومان بولی اختیاری را می پذیرد که نشان می دهد آیا باید از بارگذاری تنبل جلوگیری شود. به عنوان مثال، ممکن است بخواهید فقط بارگذاری تنبل را در محیط های غیر تولیدی غیرفعال کنید تا محیط تولید شما به طور عادی به کار خود ادامه دهد، حتی اگر یک رابطه بارگذاری تنبل به طور تصادفی در کد تولید وجود داشته باشد:

use Illuminate\Database\Eloquent\Model;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}

Illuminate\Database\LazyLoadingViolationException پس از جلوگیری از بارگذاری تنبل، Eloquent زمانی که برنامه شما تلاش می کند هر رابطه Eloquent را تنبل بارگذاری کند، استثنا ایجاد می کند .

می‌توانید با استفاده از این روش، رفتار نقض بارگذاری تنبل را سفارشی کنید handleLazyLoadingViolationsUsing . برای مثال، با استفاده از این روش، می‌توانید به نقض‌های بارگذاری تنبل دستور دهید به جای قطع کردن اجرای برنامه با استثنائات، فقط ثبت شوند:

Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation) {
$class = $model::class;
 
info("Attempted to lazy load [{$relation}] on model [{$class}].");
});

روش save

Eloquent روش های مناسبی را برای افزودن مدل های جدید به روابط ارائه می دهد. به عنوان مثال، شاید لازم باشد یک نظر جدید به یک پست اضافه کنید. به جای تنظیم دستی post_id ویژگی روی Comment مدل، می‌توانید نظر را با استفاده از روش رابطه وارد کنید save :

use App\Models\Comment;
use App\Models\Post;
 
$comment = new Comment(['message' => 'A new comment.']);
 
$post = Post::find(1);
 
$post->comments()->save($comment);

comments توجه داشته باشید که ما به رابطه به عنوان یک ویژگی پویا دسترسی نداشتیم . در عوض، comments روش را برای به دست آوردن یک نمونه از رابطه فراخوانی کردیم . این save روش به طور خودکار مقدار مناسب را post_id به Comment مدل جدید اضافه می کند.

اگر نیاز به ذخیره چندین مدل مرتبط دارید، می توانید از saveMany روش زیر استفاده کنید:

$post = Post::find(1);
 
$post->comments()->saveMany([
new Comment(['message' => 'A new comment.']),
new Comment(['message' => 'Another new comment.']),
]);

متدهای save و saveMany نمونه‌های مدل داده‌شده را حفظ می‌کنند، اما مدل‌های تازه تداوم‌شده را به هیچ‌یک از روابط درون حافظه‌ای که قبلاً روی مدل والد بارگذاری شده‌اند اضافه نمی‌کنند. اگر قصد دارید پس از استفاده از روش save یا به رابطه دسترسی پیدا کنید saveMany ، ممکن است بخواهید از refresh روش برای بارگذاری مجدد مدل و روابط آن استفاده کنید:

$post->comments()->save($comment);
 
$post->refresh();
 
// All comments, including the newly saved comment...
$post->comments;

ذخیره بازگشتی مدل ها و روابط

اگر مایل به save مدل خود و همه روابط مرتبط با آن هستید، می توانید از push روش استفاده کنید. در این مثال، Post مدل و همچنین نظرات آن و نویسندگان نظر ذخیره می شود:

$post = Post::find(1);
 
$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';
 
$post->push();

این pushQuietly روش ممکن است برای ذخیره یک مدل و روابط مرتبط با آن بدون ایجاد هیچ رویدادی استفاده شود:

$post->pushQuietly();

روش create

علاوه بر متدهای save و saveMany ، می‌توانید از create روشی نیز استفاده کنید که آرایه‌ای از ویژگی‌ها را می‌پذیرد، یک مدل ایجاد می‌کند و آن را در پایگاه داده قرار می‌دهد. تفاوت بین save و create این است که save یک نمونه مدل کامل Eloquent را می پذیرد در حالی که create یک PHP ساده را می پذیرد array . مدل جدید ایجاد شده با create روش زیر برگردانده می شود:

use App\Models\Post;
 
$post = Post::find(1);
 
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);

می توانید از createMany روش برای ایجاد چندین مدل مرتبط استفاده کنید:

$post = Post::find(1);
 
$post->comments()->createMany([
['message' => 'A new comment.'],
['message' => 'Another new comment.'],
]);

متدهای createQuietly و createManyQuietly ممکن است برای ایجاد یک مدل(ها) بدون ارسال هیچ رویدادی استفاده شوند:

$user = User::find(1);
 
$user->posts()->createQuietly([
'title' => 'Post title.',
]);
 
$user->posts()->createManyQuietly([
['title' => 'First post.'],
['title' => 'Second post.'],
]);

همچنین می توانید از روش های findOrNew , firstOrNew , firstOrCreate , و updateOrCreate برای ایجاد و به روز رسانی مدل های روابط استفاده کنید .

قبل از استفاده از create روش، حتما اسناد انتساب انبوه را بررسی کنید .

متعلق به روابط است

اگر می خواهید یک مدل فرزند را به یک مدل والد جدید اختصاص دهید، می توانید از این associate روش استفاده کنید. در این مثال، مدل رابطه ای را با مدل User تعریف می کند . این روش کلید خارجی را در مدل فرزند تنظیم می کند: belongsTo Account associate

use App\Models\Account;
 
$account = Account::find(10);
 
$user->account()->associate($account);
 
$user->save();

برای حذف مدل والد از مدل فرزند، می‌توانید از این dissociate روش استفاده کنید. این روش کلید خارجی رابطه را به صورت زیر تنظیم می کند null :

$user->account()->dissociate();
 
$user->save();

روابط بسیار به بسیاری

پیوست کردن / جدا کردن

Eloquent همچنین روش‌هایی را برای راحت‌تر کردن کار با روابط چند به چند ارائه می‌کند. به عنوان مثال، بیایید تصور کنیم یک کاربر می تواند نقش های زیادی داشته باشد و یک نقش می تواند کاربران زیادی داشته باشد. می‌توانید از این attach روش برای پیوست کردن یک نقش به کاربر با درج یک رکورد در جدول میانی رابطه استفاده کنید:

use App\Models\User;
 
$user = User::find(1);
 
$user->roles()->attach($roleId);

هنگام پیوست کردن یک رابطه به یک مدل، می‌توانید آرایه‌ای از داده‌های اضافی را نیز برای درج در جدول میانی ارسال کنید:

$user->roles()->attach($roleId, ['expires' => $expires]);

گاهی اوقات ممکن است لازم باشد که یک نقش از یک کاربر حذف شود. برای حذف یک رکورد رابطه چند به چند، از detach روش استفاده کنید. این detach روش رکورد مناسب را از جدول میانی حذف می کند. با این حال، هر دو مدل در پایگاه داده باقی می مانند:

// Detach a single role from the user...
$user->roles()->detach($roleId);
 
// Detach all roles from the user...
$user->roles()->detach();

برای راحتی attach و detach همچنین پذیرش آرایه های شناسه به عنوان ورودی:

$user = User::find(1);
 
$user->roles()->detach([1, 2, 3]);
 
$user->roles()->attach([
1 => ['expires' => $expires],
2 => ['expires' => $expires],
]);

همگام سازی انجمن ها

همچنین می‌توانید از این sync روش برای ایجاد ارتباط چند به چند استفاده کنید. این sync روش آرایه ای از شناسه ها را برای قرار دادن در جدول میانی می پذیرد. هر شناسه ای که در آرایه داده شده نباشد از جدول میانی حذف می شود. بنابراین، پس از تکمیل این عملیات، تنها شناسه های موجود در آرایه داده شده در جدول میانی وجود خواهند داشت:

$user->roles()->sync([1, 2, 3]);

همچنین می توانید مقادیر جدول میانی اضافی را با شناسه ها ارسال کنید:

$user->roles()->sync([1 => ['expires' => true], 2, 3]);

اگر می‌خواهید همان مقادیر جدول میانی را با هر یک از شناسه‌های مدل همگام‌سازی شده درج کنید، می‌توانید از syncWithPivotValues روش زیر استفاده کنید:

$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);

اگر نمی‌خواهید شناسه‌های موجود را که در آرایه داده شده وجود ندارد جدا کنید، می‌توانید از syncWithoutDetaching روش زیر استفاده کنید:

$user->roles()->syncWithoutDetaching([1, 2, 3]);

تضعیف انجمن ها

رابطه چند به چند نیز toggle روشی را ارائه می‌کند که وضعیت پیوست شناسه‌های مدل مرتبط را تغییر می‌دهد. اگر شناسه داده شده در حال حاضر پیوست باشد، جدا می شود. به همین ترتیب، اگر در حال حاضر جدا شده باشد، پیوست خواهد شد:

$user->roles()->toggle([1, 2, 3]);

همچنین می توانید مقادیر جدول میانی اضافی را با شناسه ها ارسال کنید:

$user->roles()->toggle([
1 => ['expires' => true],
2 => ['expires' => true],
]);

به روز رسانی یک رکورد در جدول میانی

اگر نیاز دارید یک ردیف موجود در جدول میانی رابطه خود را به روز کنید، می توانید از این updateExistingPivot روش استفاده کنید. این روش کلید خارجی رکورد میانی و آرایه ای از ویژگی ها را برای به روز رسانی می پذیرد:

$user = User::find(1);
 
$user->roles()->updateExistingPivot($roleId, [
'active' => false,
]);

لمس کردن مهر زمانی والدین

هنگامی که یک مدل یک belongsTo یا belongsToMany رابطه ای را با مدل دیگری تعریف می کند، مانند مدلی Comment که متعلق به یک است Post ، گاهی اوقات هنگام به روز رسانی مدل فرزند، به روز رسانی مهر زمانی والدین مفید است.

به عنوان مثال، هنگامی که یک Comment مدل به روز می شود، ممکن است بخواهید به طور خودکار updated_at مهر زمانی مالکیت را "لمس" کنید Post تا روی تاریخ و زمان فعلی تنظیم شود. برای انجام این کار، می توانید یک touches ویژگی به مدل فرزند خود اضافه کنید که حاوی نام روابط است که باید updated_at زمانی که مدل فرزند به روز می شود، مهر زمانی آنها به روز شود:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Comment extends Model
{
/**
* All of the relationships to be touched.
*
* @var array
*/
protected $touches = ['post'];
 
/**
* Get the post that the comment belongs to.
*/
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}

مهر زمانی مدل والد تنها در صورتی به روز می شود که مدل فرزند با استفاده از روش Eloquent به روز شود save .