درود
فرض کنید من میخوام پست های وبلاگم رو به همراه نویسندش نمایش بدم.یک جدول پست دارم ویک فیلد داره به نام user_id که با جدول user ریلیشن میشن.
پرسش من اینجاست که چجوری پستها رو با نام نویسنده پست چاپ کنم؟
اینجوری نمیشه
Post::find(1)->user
چون شاید کاربر ۲۰ پست ۱ رو ساخته باشه.
ممنون میشم راهنماییم کنید؟
دقیقاً به فارسی بگین چی میخواین تا کد معادلش رو بگیم:
- میخواین تمام پستها + نویسنده اونها رو نشون بدین؟
- پستهای یک کاربر خاص رو نشون بدین؟
- کاربری که یک پست خاص رو گذاشته نشون بدین؟
خدایی فارسی نبود؟
پست های وبلاگم رو به همراه نویسندش
foreach(Post::all() as $post) {
echo $post->user->name;
}
باید از inner join استفاده کنید
$posts = DB::table('users')
->join('posts', 'users.id', '=', 'posts.user_id')
->select('users.name', 'posts.*')
->get();
کد بالا معادل کوئری زیر هست :
select `users`.`name`, `posts`.* from `users` inner join `posts` on `users`.`id` = `posts`.`user_id`
تو ویو هم میتونید هم به تمام ستون های پست و ستون نام کاربر نویسنده دسترسی داشته باشید
@foreach($posts as $post)
<p>title : {{ $post->title }}</p>
<p>body : {{ $post->body }}</p>
<p>author: {{ $post->name }}</p>
<hr>
@endforeach
مگه وقتی ریلیشن زدیم خودش ساب کوئری نمیزنه؟
دستوری که در مستندات هست برای حالتی هست که قراره تمام پست های یک کاربر با آیدی مورنظرو در بیاریم ولی برای این حالت که تمام پست ها قراره بازیابی بشه باید join بزنند مشابه اون در eloquent هم به این صورته :
$posts = AppPost::join('users', function($join){
$join->on('users.id', '=', 'posts.user_id');
})->select('users.name', 'posts.*')->get();
خوب این که زیاد جالب نیست. برای مثال، توی Yii وقتی میگیم ریلیشن تعریف کردیم و گفتیم مثلاً ارتباط belongsTo داره با جدول User ازطریق فیلد user_id ، وقتی میگیم post->user$ خودش میره یه کوئری میزنه به جدول User و رکوردی که id اون با user_id اینطرف هست رو پیدا میکنه و اگه با with صدا بزنیم و together رو true کنیم، همون موقع Join میزنه. دیگه لازم نیست دستی جوین بزنیم. اگه قرار بود جوین بزنیم پس ریلیشن رو واسه چی تعریف کردیم؟
بطور مشابه، وقتی توی مدل User میایم یه ریلیشن hasMany با جدول Post برقرار میکنیم، وقتی میگیم user->posts$ خودش میره کوئری میزنه و تمام پستهای اون کاربر رو استخراج میکنه. مطمئناً لاراول هم رفتار مشابهی داره وگرنه Relationها عملاً بلا استفاده میشن.
قطعا در Eloquent هم می توان به مانند ActiveRecord در Yii عمل کرد. چون ایشون کدی اینجا قرار ندادن و هیچ اشاره ای به تعریف relation در مدل هاشون نکردن من این روش رو پیشنهاد دادم تا فورا نتیجه بگیرند.
اگر از hasMany و belongsTo در مدل هاشون استفاده کردن به این طریق می توانند به تمام پست ها به همراه نویسنده هاشون دسترسی داشته باشند :
$posts = Post::with('User')->get();
نحوه دسترسی در ویو :
@foreach($posts as $post)
<h2>{{ $post->title }}</h2>
<p>{{ $post->body }}</p>
<p>{{ $post->user->name }}</p>
<hr>
@endforeach
راستی یادم رفت مهندس روشی که شما هم ذکر کردین Post::all() هم کار میکنه اما باعث ایجاد مشکل N+1 در لاراول میشه و Eager loading که من در بالا مثالشو زدم راه حلشه
راستش من زیاد لاراول کار نکردم و این مثال رو هم از توی مستندات خودش پیدا کردم. این مشکل N+1 توی Yii هم هست و راه حل اونجا هم همین Eager Loading هست که البته با کمک فعال کردن together میشه فریمورک رو وادار کرد حتماً Join بزنه و از SubQuery استفاده نکنه. یادمه قبلاً توی لاراول خودش Join نمیزد و باید دستی Join میزدیم. منظورم اینه که با کمک with بازم SubQuery میزنه. حقیقتش اگه ابزاری که باهاش کار میکنیم رو به خوبی بشناسیم، خیلی بهتر میشه ازش استفاده کنیم. به استارتر شدیداً مطالعه مستندات رو توصیه میکنم. با توجه به علاقه زیادشون به لاراول و ازطرفی روحیه ای که ازشون میشناسم، اگه نتونن به خوبی از امکانات این فریمورک قدرتمند استفاده کنن، ممکنه ازش دلسرد بشن.