Eloquent نوشتن این روابط و کار با آنها را بسیار آسانتر کرده است. مخصوصا که از انواع ارتباط به خوبی پشتیبانی میکند. برای آموزش رابطهها در لاراول باید تمام انواع رابطهها را مطالعه نمایید:
- One To One رابطه یک به یک
- One To Many رابطه یک به چند
- Many To Many رابطه چند به چند
- Has One Through رابطه یک به یک با واسطه
- Has Many Through رابطه یک به چند با واسطه
- One To One (Polymorphic)
- One To Many (Polymorphic)
- Many To Many (Polymorphic)
آموزش رابطه ها در لاراول
رابطههای Eloquent با نوشتن متدها در داخل Modelهای Eloquent تعریف میشوند. به عبارتی برای تعریف هر رابطه شما باید در داخل کلاس مدلهایتان، اقدام به نوشتن متد یا تابع یا فانکشن نمایید. دقیقا همانند مدلهای Eloquent، رابطهها نیز خود به عنوان یک query builder عمل میکنند، تعریف رابطهها به صورت توابع یا همان متدها این امکان را فراهم میکند تا اطلاعات را به صورت زنجیرهای از پایگاه داده صدا زده و قابلیتهای پرس و جوی بیشتری را در جستجوهای خود به کار بگیرید. مثلا :
$user->posts()->where('active', 1)->get();
در مثال بالا مشاهده میکنید که توانستیم جستجوی پیشرفتهای در بین پستهای یک کاربر انجام دهیم. به عبارتی باید گفت که میتوانیم از متدهای Eloquent در روابط لاراول بهره برده و پرس و جوی دقیقتری انجام دهیم.
آموزش رابطهها در لاراول را با مطالعه روابط ساده شروع میکنیم :
رابطه یک به یک یا One To One در لاراول
سادهترین و اساسیترین رابطه در لاراول، رابطه یک به یک میباشد. مثلا هر کاربری فقط یک شماره موبایل دارد و هر شماره موبایلی میتواند فقط متعلق به یک کاربر باشد نه بیشتر.
پیاده سازی
اگر جدول users یا کاربران به این صورت باشد :
$table->bigIncrements('id'); $table->string('name');
جدول mobiles نیز باید اینگونه باشد :
$table->bigIncrements('id'); $table->string('number'); $table->unsignedBigInteger('user_id')->index(); $table->foreign('user_id')->references('id')->on('users') ->onDelete('cascade')->onUpdate('cascade');
در سطر سوم از دستورات بالا، به عبارت user_id کلید خارجی یا foreign key میگویند. مقدار این عبارت برابر خواهد بود با id کاربر. با استفاده از مقدار کلید خارجی مشخص میشود که یک شماره تلفن متعلق به کدام کاربر است.
در مدل User :
public function phone() { return $this->hasOne('App\Http\Models\Phone'); }
در مدل Phone :
public function user() { return $this->belongsTo('App\User'); }
بعد از انجام مراحل بالا کافیست جدول را نهایی کنیم :
php artisan migrate
حال برای فراخوانی شماره موبایل یک کاربر میتوانیم دستور زیر را بکار ببریم :
return User::first()->phone;
و یا :
$user = User::find(3); return $user->phone;
و همچنین برای فراخوانی کاربر مربوط به یک شماره تلفن :
return Phone::first()->user;
و یا :
$phone = Phone::find(5); return $phone->user;
نکته :
کلید خارجی یا foreign key را میتوانید چیزی به جز user_id نیز قرار دهید اما باید در متد phone() در مدل User و نیز در متد user() در مدل Phone به این مطلب اشاره کنید. فرض کنید ما از عبارت person_id استفاده کردهایم :
در مدل User :
public function phone() { return $this->hasOne('App\Http\Models\Phone', 'person_id'); }
و در مدل Phone :
public function user() { return $this->belongsTo('App\User', 'person_id'); }
جهت آموزش کامل و کاربردی رابطه یک به یک، به این آدرس مراجعه بفرمایید.
رابطه یک به چند یا One To Many در لاراول
رابطه یک به چند زمانی تعریف میشود که یک مقدار از یک مدل Model دارای چند مقدار از مدلهای دیگر است. به طور مثال در یک سایت خبری، هر پست یا خبر، چندین و چندین comment یا دیدگاه دارد و در عین حال دیدگاه، فقط متعلق به یک خبر است.
پیاده سازی :
اگر جدول posts یا مطالب به این صورت باشد :
$table->bigIncrements('id'); $table->string('title');
جدول comments یا دیدگاهها نیز باید اینگونه باشد :
$table->bigIncrements('id'); $table->string('body'); $table->unsignedBigInteger('post_id')->index(); $table->foreign(' post _id')->references('id')->on('posts') ->onDelete('cascade')->onUpdate('cascade');
در سطر سوم از دستورات بالا، به عبارت post_id کلید خارجی یا foreign key میگویند. مقدار این عبارت برابر خواهد بود با id مطلب یا post مربوطه. با استفاده از مقدار کلید خارجی مشخص میشود که یک دیدگاه یا کامنت متعلق به کدام پست است.
در مدل Post :
public function comments() { return $this->hasMany('App\Comment); }
در مدل Comment :
public function post() { return $this->belongsTo('App\Post'); }
بعد از انجام مراحل بالا کافیست جدول را نهایی کنیم :
php artisan migrate
حال برای فراخوانی کامنتها یا دیدگاههای یک پست یا مطلب میتوانیم دستور زیر را بکار ببریم :
$comments = Post::first()->comments ;
و یا :
$comments = Post::find(3)->comments ;
و همچنین برای فراخوانی مطلب مربوط به یک دیدگاه :
return Comment::first()->post;
و یا :
$comment = Comment::find(5); return $comment ->post;
نکته اول :
دقت کنید که در مدل Post نام متد را comments به صورت اسم جمع دادیم، زیرا هر پست یا مطلب میتواند بیش از یک دیدگاه یا کامنت داشته باشد.
نکته دوم :
کلید خارجی یا foreign key را میتوانید چیزی به جز post_id نیز قرار دهید اما باید در متد comments() در مدل Post و نیز در متدpost() در مدل Comment به این مطلب اشاره کنید. فرض کنید ما از عبارت relation_id استفاده کردهایم :
در مدل Post :
public function comments() { return $this->hasMany('App\Comment ', 'relation_id'); }
و در Comment.php :
public function post() { return $this->belongsTo('App\Post', ' relation_id'); }
جهت آموزش کامل و کاربردی رابطه یک به چند، به این آدرس مراجعه بفرمایید.
Many To Many رابطه چند به چند در لاراول
فرض کنید ما جدولی داریم به نام tags که بالطبع نام مدل آن Tag است و جدولی به نام posts که اسم مدل آن Post میباشد. حال فرض کنید یک post نوشتهایم به نام “روابط در لاراول” که میتواند این تگها را به خود اختصاص دهد : php , laravel , برنامه نویسی , …
حال هر یک از تگهای بالا میتوانند در پستهای زیادی باشند. مثلا پستی با عنوان “سیستم احراز هویت لاراول” هم میتواند تگ php یا laravel را بگیرد. پس هر post میتواند چندین tag داشته باشد و هر tag میتواند متعلق به چندین post باشد.
بدین ترتیب دو جدول posts و tags میتوانند رابطه many to many داشته باشند. در لینک زیر پاورپوینت آماده فریم ورک لاراول قرار داده شده که میخواهیم شما را با سرعت توسعه لاراول و موضوعات مرتبط دیگر آشنا کنیم. این فایل در قالبی آکادمیک طراحی شده و مناسب ارائه شما عزیزان میباشد.
پیاده سازی
در رابطه Many To Many به غیر از جداول posts و tags ، به جدول واسطه دیگری نیازمندیم که به آن pivot table نیز گفته میشود. در واقع در هیچ یک از جداول posts و tags نیاز به تعریف کلید خارجی نداریم و این کار را تنها در جدول واسطه انجام خواهیم داد. ابتدا جداول posts و tags را تعریف میکنیم :
جدول posts :
$table->bigIncrements('id'); $table->string('title'); $table->longText('body'); $table->timestamps();
جدول tags :
$table->bigIncrements('id'); $table->string('name'); $table->timestamps();
نوشتن جداول واسطه مهمترین بخش از آموزش رابطهها در لاراول میباشد.حال نوبت به طراحی و تعریف جدول واسطه یا pivot table میرسد.
- اولین نکته در طراحی این جدول نام آن است. نام جدول واسطه باید از ترکیب نام دو جدول که رابطه many to many دارند ساخته شود.
- نام جدول واسطه باید ترکیب اسم مفرد جدول اول و اسم مفرد جدول دوم با یک آندرلاین (خط زیرین یا underscores) در میان آنها باشد. مثلا post_tag . درواقع اسم posts_tags برای نام pivot table صحیح نیست.
- در انتخاب نام باید ترتیب الفبایی رعایت شود. بدین صورت که اسم جدولی در ابتدا نوشته میشود که حرف اول آن نسبت به حرف اول اسم جدول دیگر اولویت داشته باشد. مثلا اگر اسم جدول اول post باشد و اسم جدول دوم tags باشد با مقایسه دو حرف p و t به این نتیجه میرسیم که حرف p نسبت به حرف t مقدم است. پس ابتدا باید post نوشته شود و سپس علامت _ و بعد tag، پس میشود : post_tag
مثال دیگر : نام جدول واسطه بین دو جدول users و roles خواهد شد :
role_user
مقادیر این جدول تنها id های مقادیر دو جدول مربوط به هم و کلیدهای خارجی خواهند بود :
$table->bigInteger('post_id')->unsigned()->index(); $table->bigInteger('tag_id')->unsigned()->index(); $table->foreign('post_id')->references('id')->on('posts') ->onDelete('cascade')->onUpdate('cascade'); $table->foreign('tag_id')->references('id')->on('tags') ->onDelete('cascade')->onUpdate('cascade');
در مدل Post :
public function tags() { return $this->belongsToMany('App\ Tag'); }
در مدل Tag :
public function posts() { return $this->belongsToMany('App\ Post'); }
مجددا به جمع بودن اسم هر دو متد و عبارت belongsTo در هر دوی آنها دقت نمایید .
جهت فراخوانی تگهای پست با id = 3 میتوان نوشت :
$tags = Post::find(3)->tags;
و برای فراخوانی پستهای تگ با id=4 داریم :
$posts =Tag::find(3)->posts;
Attach ، Dettach و Sync در روابط many to many
الوکوئنت Eloquent ، متدها و helperهایی در اختیارمان گذاشته تا کار با رابطه many to many آسانتر شود. یکی از این متدها attach() میباشد. Attach در لغت به معنای جسباندن و الصاق است و در لاراول جهت افزودن مدار به رابطه یک مقدار دیگر به کار میرود . مثلا :
$post = Post::first(); $post->tags()->attach($tagId);
مشاهده میفرمایید که ورودی متد attach() باید id یا آرایهای از idها باشد. این به خاطر این است که در pivot table تنها id میتوان ذخیره کرد. در مثال دیگری میخواهیم چند tag را به تگهای یک post اضافه نماییم :
$post->tags()->attach([1,2,3,]);
و یا مثلا در تکه کد زیر :
$tags = $request->tags; $post->tags()->attach($tags);
متد detach() نیز درست نقطه مقابل attach() و برعکس آن عمل میکند. مثلا برای حذف یک یا چند مقدار از روابط یک مقدار دیگر. مثلا برای حذف tagهایی با idهای ۱و۲ از یک post معین میتوانیم بنویسیم :
$post->tags()->attach([1,2]);
چنانچه از متد sync() استفاده نماییم، ابتدا تمام مقادیر حذف شده و سپس مقادیر ورودی متد sync() اضافه خواهد شد. مثلا فرض کنید post با id=3 تگها با idهای ۱و۳ را دارد. چنانچه از دستور زیر استفاده کنیم:
$post ->tags() -> sync(2);
تنها تگ با id=2 جزو تگهای این پست خواهد بود و تگهای ۱و۳ از جدول واسطه حذف خواهند شد. این helper میتواند یک آرایه را نیز به عنوان ورودی بپذیرد.
کی اط قسمتهای مهم آموزش روابط در لاراول روابط با واسطه است :
Has One Through رابطه یک به یک با واسطه در لاراول
رابطه hasOneThrough مدلها را از طریق یک واسطه پیوند میدهد به عبارتی ایجاد ارتباط بین دو جدول یا دو مدل از طریق جدول یا مدل دیگری میتواند برقرار شود. مثلا فرض کنید در یک آژانس تلفنی جدولی به نام drivers یا رانندهها داریم. جدول دیگری هم داریم به نام cars یا ماشینها. جدول سومی نیز وجود دارد به نام car_infos که مشخصات ماشینها از قبیل شماره پلاک، رنگ و … را ثبت میکند و نام مدل مربوط به آن CarInfo میباشد. ارتباط بین دو جدول drivers و cars از نوع One To One است. و ارتباط بین دو جدول cars و car_infos نیز One To One است.
یعنی هر رانندهای یک ماشین دارد و هر ماشینی یک اطلاعاتی و بالعکس هر اطلاعاتی متعلق به یک ماشین است و هر ماشینی متعلق به یک راننده. اما ارتباطی بین drivers و car_infos وجود ندارد. برای ارتباط بین این دو جدول، در نسخههای قبل از ۵.۸ بایستی تغییراتی در هر دو جدول میدادیم و از کلید خارجی استفاده میکردیم اما از نسخه ۵.۸ به بعد این کار لازم نیست و ما میتوانیم با جدولهای موجود ارتباط برقرار کنیم :
پیاده سازی
جدول drivers :
$table->bigIncrements('id'); $table->string('name');
جدول cars :
$table->bigIncrements('id'); $table->unsignedBigInteger('driver_id')->index(); $table->foreign('driver_id')->references('id')->on('drivers') ->onDelete('cascade')->onUpdate('cascade');
جدول car_infos :
$table->bigIncrements('id'); $table->string('color'); $table->integer('car_number'); $table->unsignedBigInteger('car_id')->index(); $table->foreign('car_id')->references('id')->on('cars') ->onDelete('cascade')->onUpdate('cascade');
مشاهده میکنید که ارتباطی بین drivers و car_infos وجود ندارد .
برای ایجاد ارتباط بین این دو کافیست در مدل Driver :
public function info() { return $this->hasOneThrough('App\CarInfo', 'App\Car'); }
این دستور میگوید: جدول drivers با جدول car_infos ارتباط One To One دارد اما از طریق جدول cars .
برای فراخوانی شماره پلاک مربوط به راننده اول داریم :
$driver = \App\Driver::first(); $info = $driver->info;
ولی تا تاریخ تحریر این مقاله، لاراول برای این رابطه، رابطه برعکسی نگذاشته است به عبارتی نمیتوان در این رابطه از روی اطلاعات یک ماشین، راننده آن را پیدا کرد. اما میتوان در مدل CarInfo این کار را انجام داد :
public function driver() { return $this->car->driver(); }
و برای فراخوانی راننده یک شماره ماشین :
$info = \App\CarInfo::where('number', '47693')->first(); $driver = $info->driver;
Has Many Through رابطه یک به چند با واسطه در لاراول
رابطه hasManyThrough مدلها را از طریق یک مدل واسطه ارتباط میدهد به عبارتی ایجاد ارتباط بین دو جدول یا دو مدل از طریق جدول یا مدل دیگری اتفاق میافتد. فرض کنید یک جدول داریم به نام countries یا کشور ها با مدل Country . جدول دیگری داریم به نام users یا کاربران با مدل User. جدول سوم هم متعلق به posts یا نوشتههاست که نام مدلش Post میباشد.
ارتباط بین Country و User به صورت One To Many است یعنی هر کشوری چندین کاربر دارد ولی هر کاربر تنها متعلق به یک کشور است. ارتباط بین User و Post نیز چنین است. هر کاربر چندین پست دارد ولی هر پست تنها متعلق به یک کاربر است.
رابطه has many through میگوید میتوانیم بین دو مدل Country و Post ارتباط برقرار کرده و همهpostهای یک country را فراخوانی نمود. تنها کافیست در مدل Country :
public function posts() { return $this->hasManyThrough('App\Post', 'App\User'); }
این دستور میگوید: جدول countries با جدول posts ارتباطی از نوع One To Many دارد اما از طریق جدول users .
حال برای فراخوانی همه postهای یک کشور مشخص چنین عمل میکنیم:
$country = \App\Country::first(); $posts = $country->posts;
ولی برای فراخوانی عکس این رابطه فعلا راهی جز نوشتن متد دستی وجود ندارد. مطابق مثال بالا میتوان در مدل Post نوشت:
public function country() { return $this->user->country(); }
در این قسمت از مقاله آموزش روابط در لاراول نوبت به بررسی روابط Polymorphic میرسد :
(One To One Polymorphic) رابطه پلی مورفیک یک به یک در لاراول
آموزش روابط در لاراول مستلزم درک صحیحی از روابط پلی مورفیک است. رابطه پلی مورفیک یک به یک مشابه رابطه ساده یک به یک است که در ابتدای مقاله بحث شد با این تفاوت که مدل هدف میتواند متعلق به چند مدل باشد و یا با چند مدل رابطه یک به یک داشته باشد.
فرض کنید مدلی داریم به نام Post با این شرط که به دلیل محدودیت دیتابیس، در هر پست تنها قادر خواهیم بود از یک عکس استفاده کنیم. مدل دیگری نیز به نام User داریم با این شرط که هر کاربر مجاز است تنها یک عکس داشته باشد. پس نیاز به مدل دیگری داریم به نام Image.
به عبارتی Image هم با Post رابطه یک به یک دارد هم با User. بنابراین میتوانیم برای این سه مدل رابطه One To One از نوع Polymorphic بنویسیم :
پیاده سازی
جدول users :
$table->bigIncrements('id'); $table->string('name');
جدول posts :
$table->bigIncrements('id'); $table->string('title');
جدول images :
$table->bigIncrements('id'); $table->string('path'); $table->rememberToken(); $table->timestamps();
در مدل User :
public function image() { return $this->morphOne('App\Image', 'imageable'); }
در مدل Post :
public function image() { return $this->morphOne('App\Image', 'imageable'); }
در مدل Image :
public function imageable() { return $this->morphTo(); }
برای فراخوانی عکس یک کاربر :
$user = User::first(); $image = $user->image;
یا برای فراخوانی پست مربوط به یک عکس :
$image = Image::first(); $user = $image->imageable;
(One To Many Polymorphic) رابطه یک به چند پلی مورفیک
این رابطه شبیه رابطه One To Many یک به چند ساده است با این تفاوت که مدل هدف، میتواند با چند مدل ارتباط یک به چند برقرار کند.
فرض کنید در یک سایت هم تعدادی post به صورت متن و تکست داریم و هم تعدادی video که کاربران میتوانند برای هر دوی اینها دیدگاه یا کامنت comment بگذارند. به قولی دیگر مدل Comment با هر دو مدل Post و Video ارتباط one to many برقرار میکند.
به جای نوشتن چندین متد و کلید خارجی میتوان از رابطه یک به چند پلی مورفیک استفاده کرده و از شلوغی در مدلها و جدولها جلوگیری کرد. در لینک زیر فیلم آموزشی ساخت دستورات artisan در لاراول ۷ قرار داده شده که پیشنهاد می کنیم کلیک کنید.
پیاده سازی :
جدول comments :
$table->bigIncrements('id'); $table->longText('body'); $table->integer('commentable_id'); $table->string('commentable_type');
جدول posts :
$table->bigIncrements('id'); $table->string('title'); $table->longText('body');
جدول videos :
$table->bigIncrements('id'); $table->string('title'); $table->string('path');
مدل Comment :
public function commentable() { return $this->morphTo(); }
مدل Post :
public function comments() { return $this->morphMany('App\ Comment', 'commentable'); }
مدل Video :
public function comments() { return $this->morphMany('App\ Comment', 'commentable'); }
حال برای فراخوانی دیدگاههای یک پست :
$post = App\Post::find(1); foreach ($post->comments as $comment) { // }
و یا برای فراخوانی ویدیوی مربوط به یک دیدگاه :
$comment = App\Comment::find(1); $commentable = $comment->commentable;
(Many To Many Polymorphic) رابطه چند به چند پلی مورفیک
رابطه چند به چند پلی مورفیک اندکی پیچیدهتر از بقیه رابطههاست. از این رابطه زمانی استفاده میکنیم که یک مدل با چند مدل ارتباط چند به چند داشته باشد. مثلا مدل Tag با هر دو مدل Post و Video رابطه چند به چند دارد. به عبارتی هر ویدیو و پست تکست میتواند چندین تگ داشته باشد و در عین حال هر تگ به صورت همزمان میتواند متعلق به چندین پست و ویدیو باشد.
اگر از رابطه Many To Many Polymorphic استفاده نکنیم جدولها و مدلهایمان کثیف و شلوغ خواهند شد. برای این رابطه علاوه بر سه جدول posts و videos و tags بایستی جدول دیگری به نام taggables نیز بنویسیم. این جدول چیزی مشابه جدول واسطه است.
پیاده سازی
جدول posts :
$table->bigIncrements('id'); $table->string('title'); $table->longText('body');
جدول videos :
$table->bigIncrements('id'); $table->string('title'); $table->string('path');
جدول tags :
$table->bigIncrements('id'); $table->string('name');
و اما جدول taggables :
$table->integer('tag_id'); $table->integer('tagable_id'); $table->string('tagable_type');
در مدل Tag متدهای زیر را می نویسیم :
public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } public function videos() { return $this->morphedByMany('App\Video', 'taggable'); }
در هر دو مدل Video و Post متد زیر را مینویسیم :
public function tags() { return $this->morphToMany('App\ Tag', 'taggable'); }
برای فراخوانی تگهای یک پست :
$post = Post::first(); $tags = $post->tags;
یا جهت فراخوانی تگ های یک ویدیو :
$video = \App\Video::first(); $tags = $video->tags;
ولی برای فراخوانی ویدیوهای مربوط به یک تگ :
$tag = Tag::first(); $videos = $tag->videos;
آموزش رابطهها در لاراول به صورت دقیق و مستحکم مستلزم مشاهده ویدیوهای آموزشی و تمرین و تکرار است. جهت آموزش کلی به آموزش صفر تا صد لاراول در همین سایت مراجعه فرمایید.
مراجع
https://laravel.com/docs/6.x/eloquent-relationships
https://medium.com/swlh/a-guide-on-laravel-relationships-1febfac430f6
https://appdividend.com/2018/05/16/laravel-model-relationships-example/
https://www.w3resource.com/laravel/eloquent-relationships.php
2 پاسخ
اقا انصافا دمتون گرم ایول