کانال بله, جهت پشتیبانی و اطلاع رسانی کانال بله, جهت پشتیبانی و اطلاع رسانی
عضویت

آموزش رابطه ها در Eloquent

رابطه ها (Relationships) در Eloquent

  1. مقدمه
  2. تعریف رابطه ها
    • رابطه ی یک به یک
    • رابطه ی یک به چند
    • رابطه ی چند به چند
    • رابطه ی چندتایی همراه با واسط (has many through)
    • رابطه های polymorphic
    • رابطه های polymorphic چند به چند
  3. کوئری گرفتن از رابطه ها
    • Eager loading (بارگذاری زود هنگام)
    • اعمال محدودیت بر eager load ها
    • بارگذاری زود هنگام به شرط نیاز (lazy eager loading)
  4. وارد کردن مدل های مربوطه (related models)
    • رابطه های چند به چند
    • بروز رسانی timestamp مدل پدر

مقدمه

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

  1. رابطه ی یک به یک (one to one)
  2. رابطه ی یک به چند (one to many)
  3. رابطه ی چند به چند (many to many)
  4. رابطه ی چند به چند با واسطه (Has Many Through)
  5. رابطه های polymorphic
  6. رابطه های polymorphic چند به چند

تعریف رابطه ها (relationships)

در Eloquent، رابطه ها به صورت توابعی داخل کلاس های مدل تعریف می شوند. از آنجایی که رابطه ها نیز همچون مدل های Eloquent، خود به عنوان یک query builder ایفای نقش می کنند، ویژگی تعریف رابطه ها به صورت توابع این امکان را فراهم می کند تا متدها را به صورت زنجیره ای صدا زده و قابلیت های پرس و جوی بیشتری را در کوئری گرفتن های خود لحاظ کنید. مثال:

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

در زیر به نحوه ی تعریف هر یک می پردازیم:

رابطه ی یک به یک

رابطه ی یک به یک ساده ترین نوع relationship است. به عنوان مثال یک مدل User ممکن است با تنها یک phone رابطه داشته باشد. برای تعریف این رابطه، یک متد phone در مدل User قرار می دهیم. متد phone بایستی خروجی متد hasOne را در کلاس base مدل Eloquentبرگرداند:

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

اولین آرگومان ارسالی به متد hasOne اسم مدل مربوطه هست. پس از اینکه رابطه تعریف شد، می توان رکورد مربوطه را با استفاده از امکانproperty های داینامیک Eloquent بازیابی کرد. property های داینامیک به شما اجازه می دهند به رابطه ها (که به صورت توابع تعریف می شوند) مانند property های تعریف شده در مدل دسترسی داشته باشید:

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

Eloquent اسم کلید خارجی (foreign key) رابطه را بر اساس اسم مدل مربوطه درنظر می گیرد. در این مثال، مدل Phone (با توجه به اسم مدل) باید دارای کلید خارجی به نام user_id باشد. برای شکستن و بازنویسی این قرارداد، می توانید اسم دلخواه برای کلید خارجی را به عنوان آرگومان دوم به متد پاس دهید:

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

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

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

تعریف عکس رابطه (طرف دیگر رابطه)

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

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

در نمونه ی بالا، Eloquent ستون user_id از مدل Phone را به id در مدل User متصل (match می کند). Eloquent اسم پیش فرض کلید خارجی را با در نظر گرفتن اسم متد (رابطه) و الصاق پسوند _id تعریف می کند. حال اگر می خواهید کلید خارجی در مدل Phone اسمی غیر ازuser_id داشته باشد، می توانید اسم دلخواه کلید خارجی را به عنوان آرگومان دوم ارسال کنید:

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

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

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

رابطه ی یک به چند (one to many)

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

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

یادآور می شویم که Eloquent خود ستون کلید خارجی را در مدل Comment تعیین می کند. بر اساس قراردادهای از پیش تعیین شده،Eloquent فرم snake case اسم مدل مالک (post) را انتخاب کرده و صرفا یک پسوند _id به آن اضافه می کند. در این مثال Eloquent فرض را بر این می گذارد که کلید خارجی در مدل Comment ستونی به نام post_id هست.
پس از تعریف رابطه ی مورد نیاز، می توان با دسترسی به پراپرتی comments به مجموعه دیدگاه های پست مربوطه دسترسی داشت. همان طور که پیش تر گفته شد، Eloquent از ویژگی dynamic properties پشتیبانی می کند. بنابراین می توان به رابطه ها (relationship function) همانند property های تعریف شده در مدل دسترسی داشت:

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

و با توجه به اینکه رابطه ها نقش query builder را نیز ایفا می کنند، می توانید دیدگاه هایی که در خروجی واکشی می شوند را با اعمال constraintها محدود نمایید. برای این منظور متد comments را صدا زده و شرط ها را به صورت زنجیره ای به کوئری الحاق کنید:

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

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

return $this->hasMany('App\Comment', 'foreign_key');
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');

تعریف طرف دیگر رابطه

پس از تعریف رابطه ی لازم برای دسترسی به تمامی دیدگاه های پست، رابطه ای تعریف می کنیم که اجازه ی دسترسی دیدگاه به پست مربوطه (پست پدر) را فراهم می کند. برای تعریف طرف دیگر رابطه ی hasMany، یک تابع relationship در مدل فرزند اعلان می کنیم که متد belongsTo را صدا می زند:

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

پس از تعریف رابطه، می توان با دسترسی به property های داینامیک post مدل Post مربوط به یک Comment را واکشی نمود:

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

در مثال بالا، Eloquent ستون post_id از مدل Comment را به ستون id در مدل Post مچ می کند. همان طور که قبلا هم به آن اشاره شد،Eloquent اسم کلید خارجی را با درنظر گرفتن اسم رابطه و افزودن پسوند _id به اسم متد تعیین می کند. حال اگر اسم ستون کلید خارجی در مدلComment، post_id نباشد در آن صورت یک اسم سفارشی به عنوان آرگومان دوم به متد belongsTo پاس می دهیم:

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

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

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

رابطه ی چند به چند (many to many)

relation های چند به چند کمی پیچیده تر از رابطه های hasOne و hasMany هستند. به عنوان مثال می توان به رابطه ای اشاره کرد که در آن یک کاربر می تواند چندین نقش داشته باشد و نقش ها هم می توانند به کاربرهای متعددی داده شوند.
برای مثال کاربران متعددی می توانند نقش Admin را داشته باشند. برای تعریف این رابطه به سه جدول نیاز داریم: 1. users 2. roles 3.role_user. اسم جدولrole_user بر اساس ترتیب حروف الفبا از کنار هم قرار گرفتن نام دو مدل مرتبط گرفته شده است. این جدول حاوی دو ستون به نام های user_id و role_id می باشد.
برای تعریف رابطه ی چند به چند یک متد تعریف می کنیم که خود متد belongsToMany را در کلاس پایه Eloquent صدا می زند. در مثال زیر متدroles را در مدل User فراخوانی می کنیم:

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

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

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

می توان متد roles را فراخوانی نموده و محدودیت هایی را جهت واکشی نتایج مد نظر در ادامه ی آن متد به صورت زنجیره ای به کوئری الحاق کرد:

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

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

                    return $this->belongsToMany('App\Role', 'role_user');

همچنین می توانید اسم ستون های کلید خارجی جدول را توسط آرگومان سوم و چهارم سفارشی تنظیم نمایید. آرگومان سوم اسم کلید خارجی مدلی است که رابطه را در آن تعریف می کنید و آرگومان چهارم اسم کلید خارجی مدلی است که به آن وصل می شوید:

                    return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');

تعریف طرف دیگر رابطه

برای تعریف طرف مقابل رابطه، متد belongsToMany را در مدل مربوطه صدا می زنیم. در ادامه ی مثال قبلی، یک متد users در مدل Roleتعریف می کنیم که متد belongsToMany را صدا می زند:

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

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

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

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

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

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

                    return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

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

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

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

می توانید نتایج واکشی شده توسط belongsToMany را با استفاده از متدهای wherePivot و wherePivotIn در تعریف رابطه فیلتر و محدود نمایید:

return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
return $this->belongsToMany('App\Role')->wherePivotIn('approved', [1, 2]);
return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
return $this->belongsToMany('App\Role')->wherePivotIn('approved', [1, 2]);

رابطه ی چندتایی با واسطه (has many through)

رابطه ی has many through (چندتایی با واسطه) یک میانبر برای دسترسی به رابطه های دور (مدل هایی که در عمق یک رابطه هستند) از طریق یک رابطه ی واسط فراهم می آورد. به عنوان مثال، مدل Country ممکن است به واسطه ی یک مدل واسط User دارای مدل های Post متعددی باشد. در این مثال، می توان به راحتی تمامی پست های وبلاگ مربوط به یک کشور را بازیابی نمود. برای تعریف این رابطه به سه جدول زیر نیاز داریم:

countries
   id - integer
   name - string
 
users
   id - integer
   country_id - integer
   name – string
 
posts
   id - integer
   user_id - integer
   title - string

اگرچه جدول posts ستونی به نام country_id ندارد، با این وجود رابطه ی hasManyThrough امکان دسترسی به پست های یک کشور (پست هایی که کاربر برای Country نوشته) را از طریق $country->posts فراهم می آورد. برای اجرای این کوئری، Eloquent ستون country_id را در جدول واسطه ی users بررسی می کند. پس از یافتن ID های کاربری منطبق، Eloquent از آن ها برای کوئری گرفتن از جدول posts استفاده می کند.
حال که ساختار جداول مورد نیاز برای این رابطه را بررسی کردیم، آن را در مدل Country تعریف می کنیم:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
   /**
    * Get all of the posts for the country.
    */
   public function posts()
   {
       return $this->hasManyThrough('App\Post', 'App\User');
   }
}

اولین آرگومان ارسالی به متد hasManyThrough اسم مدلی است که می خواهید به آن دسترسی داشته باشید و دومین آرگومان اسم مدل واسطه می باشد. می توان با ارسال آرگومان سوم و چهارم به متد مزبور، کلیدهای رابطه را خودتان به صورت اختصاصی مشخص نمایید. آرگومان سوم اسم کلید خارجی در جدول واسطه می باشد، در حالی که آرگومان چهارم اسم کلید خارجی در مدل نهایی می باشد. آرگومان پنجم نیز local key (ستون محلی برای رابطه) را مشخص می کند:

class Country extends Model
{
   public function posts()
   {
       return $this->hasManyThrough(
           'App\Post', 'App\User',
           'country_id', 'user_id', 'id'
       );
   }
}

رابطه های polymorphic

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

رابطه های Polymorphic به یک مدل اجازه می دهند در یک رابطه همزمان به چندین مدل دیگر تعلق داشته باشد (امکان رابطه ی یک مدل با چندین مدل دیگر از طریق یک رابطه را فراهم می آورد). به عنوان مثال، فرض کنید کاربران اپلیکیشن شما می خواهند هم پست ها و هم دیدگاه ها را بپسندد (like کنند). با بهره گیری از رابطه های polymorphic می توان به راحتی یک جدول واحد likes برای هر دو این سناریو بکار برد. در گام نخست ساختار جداولی که برای این رابطه لازم است را مورد بررسی قرار می دهیم:

posts
   id - integer
   title - string
   body - text
 
comments
   id - integer
   post_id - integer
   body - text
 
likes
   id - integer
   likeable_id - integer
   likeable_type - string

دو ستون مورد توجه در این مثال، ستون های likeable_id و likeable_type در جدول likes هستند. ستون likeable_id حاوی مقدار ID پست یا دیدگاه خواهد بود، در حالی که ستون likeable_type اسم کلاس مدل مالک (پدر) را نگه خواهد داشت. Eloquent در واقع به واسطه ی ستونlikeable_type تشخیص می دهد در زمان دسترسی به رابطه ی likeable کدام مدل را بایستی برگرداند.

ساختار مدل

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

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Like extends Model
{
   /**
    * Get all of the owning likeable models.
    */
   public function likeable()
   {
       return $this->morphTo();
   }
}
class Post extends Model
{
   /**
    * Get all of the post's likes.
    */
   public function likes()
   {
       return $this->morphMany('App\Like', 'likeable');
   }
}
class Comment extends Model
{
   /**
    * Get all of the comment's likes.
    */
   public function likes()
   {
       return $this->morphMany('App\Like', 'likeable');
   }
}

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

پس از تعریف جداول و مدل های مورد نیاز، می توانید از طریق مدل ها به رابطه ها دسترسی داشته باشید.برای مثال، جهت دسترسی به like های یک پست، می توان از property داینامیک likes استفاده کرد:

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

می توان مالک یک رابطه ی polymorphic را از مدل polymorphic بازیابی کرد. برای این منظور بایستی به اسم متدی که متد morphTo را صدا می زند، دسترسی داشته داشت. در مثال ما، این همان متد likeable در مدل Like هست. حال کافی است به آن متد همانند یک dynamic property دسترسی داشته باشید:

$like = App\Like::find(1);
$likeable = $like->likeable;

فراخوانی (متد) رابطه ی likeable در مدل Like، بسته به مدلی که مالک آن like می باشد یک نمونه از Post یا Comment را در خروجی برمی گرداند.

نوع های اختصاصی polymorphic

به صورت پیش فرض، Laravel اسم کامل کلاس را برای ذخیره ی نوع (اسم) مدل مربوطه بکار می برد. به عنوان نمونه، در ادامه ی مثال فوق که یکLike ممکن بود به یک Post یا Comment تعلق داشته باشد، likeable_type پیش فرضApp\Post یا App\Comment خواهد بود.
شما می توانید این قرار داد را شکسته و پایگاه داده را از ساختار داخلی اپلیکیشن جدا نمایید. این کار را با تعریف یک relationship "morph map" انجام می دهیم. morph map به Eloquent اعلان می کند که بجای اسم کلاس، اسم جدول متناظر با آن مدل را بکار ببرد.

use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
   App\Post::class,
   App\Comment::class,
]);

یا می توانید یک رشته ی سفارشی تعریف کنید که به عنوان اسم آن مدل انتخاب شود (به آن مدل مرتبط می شود):

use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
   'posts' => App\Post::class,
   'likes' => App\Like::class,
]);

می توانید morphMap را در تابع boot از AppServiceProvider معرفی کنید یا در صورت تمایل یک service provider مجزا تعریف کنید.

رابطه های polymorphic چند به چند

ساختار جدول

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

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 را خواهند داشت. متد tags متدmorphToMany را در کلاس پایه ی Eloquent فراخوانی می کند

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

تعریف طرف دیگر رابطه

سپس در مدل Tag می بایست یک متد برای هر یک از مدل های مربوطه تعریف نمایید. در این مثال، یک متد Posts و یک videos تعریف می کنیم:

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

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

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

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

می توانید مالک یک رابطه ی polymorphic را از مدل polymorphic بازیابی کنید. کافی است به اسم متدی که تابع morphedByMany را صدا می زند، دسترسی داشته باشید. در این مثال منظور دو متد posts یا videos در مدل Tag هست. بنابراین می بایست به متدهای نام برده همانندproperty های داینامیک دسترسی پیدا کنید:

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

پرس و جو و گرفتن کوئری از رابطه ها

همان طور که می دانید رابطه های Eloquent به صورت توابعی تعریف می شوند. شما می توانید با فراخوانی این توابع بدون اینکه لازم باشد خود کوئری relationship را واقعا اجرا کنید، نمونه ای از آن رابطه را دریافت نمایید. بعلاوه همان طور که قبلا نیز گفته شد، تمامی رابطه های Eloquentخود مانند query builder عمل می کنند و به شما اجازه می دهند شرط ها و قیودی را برای محدود نمودن نتیجه به صورت زنجیره ای به کوئریrelationship (پیش از اجرای نهایی کوئری بر روی پایگاه داده) اعمال کنید:
به عنوان مثال، یک سیستم وبلاگ را در نظر بگیرید که در آن مدل User تعداد زیادی مدل Post مرتبط دارد:

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

می توانید از رابطه ی posts کوئری گرفته و محدودیت های بیشتری را مانند زیر به رابطه اعمال کنید:

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

می توانید تمامی متدهای query builder را بر روی رابطه بکار ببرید.

متدهای relationship در برابر property های Dynamic

اگر نیازی به اعمال قیود (constraint) اضافی بر سازمان بر روی کوئری relationship نیست، می توانید به راحتی به آن رابطه مانند یک propertyدسترسی داشته باشید. در ادامه ی مثال قبلی (مدل های User و Post)، می توان به تمامی پست های کاربر مانند زیر دسترسی داشت:

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

property های dynamic بار گذاری را با تاخیر انجام می دهند (lazy loading). بدین معنی که داده های relationship خود را تنها زمانی بارگذاری می کنند که شما به آن ها دسترسی پیدا می کنید. به همین خاطر توسعه دهندگان اغلب از eager loading استفاده کرده و بدین وسیله رابطه هایی که می دانند پس از بارگذاری مدل مورد دسترسی قرار می گیرند را از قبل لود می کنند. eager loading تعداد کوئری های SQL که باید برای بارگذاری رابطه های یک مدل اجرا شوند را به طور قابل توجهی کاهش می دهد.

محدود کردن نتایج یک کوئری بر اساس وجود یا عدم وجود یک رابطه

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

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

می توانید یک عملگر به همراه تعداد دیدگاه ها (تعداد comment هایی که پست ها باید داشته باشد تا در خروجی لحاظ شوند) برای تنظیم بیشتر کوئری مطابق نیاز تعریف کنید:

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

با استفاده از عملگر نقطه می توانید دستورات has تودرتو بسازید. در نمونه ی زیر تمامی پست هایی که حداقل یک comment و vote دارند را واکشی می کنیم:

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

اگر می خواهید نتایج دقیق تری را در خروجی داشته باشید، می توانید متدهای whereHas و orWhereHas را برای اعمال شرط های where بر روی کوئری های has فراخوانی کنید. این متدها به شما امکان می دهند قیود اختصاصی (constraint) خود را به constraint های رابطه اعمال کنید، مانند بررسی محتویات یک دیدگاه (comment). در زیر تمامی پست هایی که حداقل یک دیدگاه مرتبط دارند و علاوه بر آن دربردارنده ی کلماتی همچون foo می باشد را استخراج می کنیم:

// Retrieve all posts with at least one comment containing words like foo%
$posts = Post::whereHas('comments', function ($query) {
   $query->where('content', 'like', 'foo%');
})->get();

شمردن تعداد نتایج حاصل از اجرای یک رابطه

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

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

می توانید تعداد نتایج چندین رابطه را بازیابی کرده و در این میان محدودیت هایی را نیز به کوئری ها اعمال کنید:

$posts = Post::withCount(['votes', 'comments' => function ($query) {
   $query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

Eager loading (بارگذاری زود هنگام)

زمانی که به (توابع) رابطه های Eloquent مانند property دسترسی پیدا می کنید، داده های رابطه در واقع با تاخیر بارگذاری می شوند (به اصطلاحlazy load می شوند). بدین معنی که داده های رابطه تنها زمانی به معنای واقعی لود می شوند که شما (برای اولین بار) به property دسترسی پیدا کنید. Eloquent می تواند رابطه ها را در زمانی که از مدل پدر کوئری می گیرید، به صورت زود هنگام بارگذاری (eager load) کند. Eager loading در حقیقت برای برطرف ساختن مشکل کوئری N + 1 در نظر گرفته شد. برای فهم بهتر این مشکل یک مدل Book را در نظر بگیرید که با مدل Author مرتبط است:

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

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

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

این حلقه یک کوئری برای واکشی تمامی کتاب های داخل جدول اجرا کرده سپس یک کوئری دیگر به ازای هر کتاب تا نویسنده ی مربوطه ی آن نیز واکشی شود. بنابراین اگر 25 کتاب داشته باشیم، حلقه ی مزبور در کل 26 کوئری اجرا می کند: 1 کوئری برای کتاب اصلی و 25 کوئری جهت استخراج نویسندگان مربوط به هر کتاب.
با بهره گیری از امکان eager loading می توان این عملیات و تعداد کوئری را به تنها دو کوئری کاهش داد. به هنگام کوئری گرفتن، می توان مشخص کرد کدام رابطه ها بایستی زود هنگام بارگذاری شوند. این کار با فراخوانی متد with امکان پذیر خواهد بود:

$books = App\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 = App\Book::with('author', 'publisher')->get();

بارگذاری زود هنگام رابطه های تودرتو (nested eager loading)

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

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

اعمال محدودیت بر روی بارگذاری زود هنگام

گاهی لازم می شود رابطه ای را به صورت زود هنگام بارگذاری کرده و این میان محدودیت هایی نیز را بر روی کوئری اعمال کنید. مثال:

$users = App\User::with(['posts' => function ($query) {
   $query->where('title', 'like', '%first%');
}])->get();

در این مثال، Eloquent تنها پست هایی را بارگذاری می کند که عنوان (مقدار ستون title) آن ها دربردارنده ی کلمه ی first باشد. البته می توانید دیگر متدهای query builder را برای تنظیم دقیق تر عملیات eager loading بکار ببرید:

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

بارگذاری زود هنگام به شرط نیاز

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

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

چنانچه لازم است محدودیت های بیشتری را بر روی کوئری اعمال کنید، می توانید تابع Closure را به متد load به عنوان آرگومان ارسال کنید (آن شرط ها قیود را در قالب تابع کلوژر به متد پاس دهید):

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

درج مدل های مربوطه (که با جدول دیگری رابطه دارند)

متد save

Eloquent متدهای فراوانی برای افزودن مدل های جدید به رابطه ها فراهم می کند. برای مثال، ممکن است لازم شود یک مدل جدید Commentبرای مدل Post درج کنید. بجای اینکه attribute(ستون) post-id را در مدل Comment به صورت دستی مقداردهی کنید، می توانید این مدل را به صورت مستقیم از متد save رابطه وارد نمایید:

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

همان طور که در مثال بالا می بینید به رابطه ی comments به صورت یک dynamic property دسترسی پیدا نکردیم. بلکه صرفا متدcomments را برای بدست آوردن نمونه ای از رابطه فراخوانی نمودیم. متد save خود به صورت اتوماتیک مقدار post_id را به مدل جدیدComment اضافه می کند (فیلد مزبور را در مدل Comment مقداردهی می کند).
برای ذخیره ی چندین مدل مرتبط، کافی است متد saveMany را بکار ببرید:

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

متد Save و رابطه های چند به چند

در رابطه های چند به چند، متد save آرایه ای از attribute های اضافی جدول واسطه را به عنوان آرگومان دوم می پذیرد:

                    App\User::find(1)->roles()->save($role, ['expires' => $expires]);

متد Create

علاوه بر متدهای save و saveMany، می توانید متد create را فراخوانی کنید. این متد آرایه ای از attribute ها را به عنوان آرگومان دریافت کرده، یک مدل جدید ایجاد می کند و سپس آن مدل را داخل پایگاه داده درج می نماید. تفاوت بین دو متد save و create در این است که save یک نمونه ی کامل از مدل Eloquent را به عنوان آرگومان می پذیرد، در حالی که create صرفا یک آرایه ی ساده و متعارف PHP را به عنوان پارامتر ورودی دریافت می کند:

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

بروز رسانی رابطه های "Belongs To" (مرتبط کردن مدل ها با متد associate)

به هنگام بروز رسانی رابطه ی belongsTo، می توانید متد associate را فراخوانی کنید. این متد کلید خارجی (foreign key) را در مدل فرزند مقداردهی می کند:

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

و در زمان حذف یک رابطه ی belongsTo متد dissociate را بکار ببرید. این متد علاوه بر بازگردانی کلید خارجی، رابطه ی موجود در مدل فرزند راreset می کند:

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

رابطه های چند به چند

درج (attach) / حذف (detach)

به هنگام کار با رابطه های چند به چند، Eloquent تعداد زیادی متد کمکی (helper method) ارائه می کند که کار با مدل های مرتبط را به مراتب آسان ساخته. سناریویی را در نظر بگیرید که در آن یک کاربر می تواند نقش های متعددی داشته باشد و یک نقش نیز در مقابل به کاربران زیادی تعلق داشته باشد. برای اینکه بتوان یک نقش جدید را با درج یک رکورد در جدول واسطه (که مدل ها را به هم وصل می کند) به کاربر مورد نظر اضافه کرد، بایستی متد attach را بکار برد (می توان با درج یک رکورد در جدول واسطه که مدل ها را به هم پیوند می دهد، یک نقش جدید به کاربر مورد نظر اضافه کرد. برای این منظور کافی است متد attach را فراخوانی کنید):

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

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

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

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

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

هر دو متدهای attach و detach آرایه ای از شناسه ها را به عنوان ورودی می گیرند:

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

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

برای بروز رسانی سطر موجود در جدول pivot، متد updateExistingPivot را بکار ببرید:

$user = App\User::find(1);
$user->roles()->updateExistingPivot($roleId, $attributes);

متد sync

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

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

می توانید مقادیر اضافی جدول واسطه را همراه با ID های ورودی به متد sync پاس دهید تا در جدول درج گردد:

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

بروز رسانی Timestamp مدل پدر

زمانی که مدلی به یک (belongsTo) یا چند (belongToMany) مدل دیگر تعلق دارد، مانند مدل Comment که به یک مدل Post تعلق دارد، بروز رسانی timestamp مدل پدر پس از بروز رسانی مدل فرزند می تواند مفید واقع شود. برای مثال زمانی که یک مدل Comment بروز رسانی می شود، ممکن است بخواهید تایم استمپ updated_at مدل مالک (Post) نیز بروز آوری شود. Eloquent این کار را برای شما آسان کرده است. کافی است پراپرتی touches که با اسم رابطه ها مقداردهی شده را به مدل فرزند اضافه نمایید:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
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()
   {
       return $this->belongsTo('App\Post');
   }
}

حال زمانی که شما یک Comment را بروز رسانی می کنید، مقدار ستون های updated_at در مدل پدر مدل پدر (Post) نیز بروز آوری می شود:

$comment = App\Comment::find(1);
$comment->text = 'Edit to this comment!';
$comment->save();
1395/03/31 9982 1668
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...