ایجاد رابطه‌ی many to many در لاراول به چه شکل است؟

برای ایجاد رابطه‌ی چند به چند یا many to many در لاراول نیاز به یک جدول واسط یا pivot داریم.

فرض من بر این است که قصد دارم بین دو جدول users و categories ارتباط چند به چند برقرار کنم. یعنی یک کاربر می‌تواند عضو چند دسته باشد و هر دسته می‌تواند چندین و چند کاربر داشته باشد. پس همانطور که قبل‌تر اشاره کردم علاوه بر دو جدول یک جدول واسط یا همان pivot table نیاز است که به صورت زیر جداول را تعریف می‌کنم:

users
categories
category_user

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

جدول category_user نیز شامل سه ستون خواهد بود که طبیعتاً باید با ستون‌های متناظر خود در جداول دیگر همخوانی داشته باشند:

id
user_id
category_id

به سراغ مدل‌های مربوطه می‌روم تا بتوانم ارتباط را برقرار کنم که به طور خلاصه در هر مدل، ارتباط با مدل دیگر به صورت زیر برقرار خواهد شد:

User.php

public function categories()
{
	return $this->belongsToMany(Category::class,'category_user','user_id','category_id');
}

Category.php

public function users()
{
	return $this->belongsToMany(User::class,'category_user','category_id','user_id');
}

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

در سمت کنترلر هم چیزی مشابه زیر داریم که من برای موارد store و update نمونه‌ای را ایجاد کرده‌ام و مشخص است که چطور از attach و sync برای این موارد استفاده شده:

public function store(UserCreateRequest $request)
{

if ($request->hasFile('profile_image')) {

	$imageName = time() . '.' . $request->profile_image->extension();

	$user = User::create([
		'name' => $request['name'],
		'email' => $request['email'],
		'profile_image' => $imageName,
	]);

	$user->categories()->attach(request('category_id'));

	$request->profile_image->move(public_path('images'), $imageName);
	
} else {

	$user = User::create([
		'name' => $request['name'],
		'profile_image' => 'nopic.jpg',
		'email' => $request['email'],
	]);

	$user->categories()->attach(request('category_id'));
}

return redirect(route('users.create'))->with('success', 'عملیات افزودن با موفقیت انجام پذیرفت');
}
public function update(User $user, UserUpdateRequest $request)
{

	if ($request->hasFile('profile_image')) {

		$imageName = time() . '.' . $request->profile_image->extension();

		$user->update([
			'name' => $request['name'],
			'email' => $request['email'],
			'profile_image' => $imageName,
		]);

		$user->categories()->sync(request('category_id'));

		$request->profile_image->move(public_path('images'), $imageName);
	} else {

		$user->update([
			'name' => $request['name'],
			'email' => $request['email'],
		]);

		$user->categories()->sync(request('category_id'));
	}

	return redirect(route('users.index'))->with('success', 'به روز رسانی با موفقیت انجام پذیرفت');
}

برای featch کردن از دیتابیس در کنترل روش دسترسی به صورت زیر است:

$users = User::whereHas('categories', function ($query){
   $query->where('categories.user_id', 3);
})->get();

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

@foreach($user->categories as $category)
	{{ $category->title }}
@endforeach

به طور بسیار ساده و خلاصه این روال ایجاد و استفاده از روابط چند به چند در لاراول بود.

یک نکته‌ی پیشرفته و کاربردی:

مثلاً فرض بگیریم یک رابطه‌ی چند به چند بین جداول posts و categories داریم و تمام موارد در مدل‌های Post و Category به درستی پیاده شده باشد.

حال اگر سناریوی ما این باشد که با زدن آدرسی مشابه زیر که در route تعریف شده به تمام پست‌های یک دسته دسترسی یابیم

Route::get('/category/{slug}', 'IndexController@category');

کافی است به صورت زیر در کنترلر عمل کنیم:

public function category($slug)
{
    $post = Post::whereHas('categories', function ($query) use ($slug){
        $query->where('slug', $slug);
    })->where('status', '1')->orderBy('id', 'DESC')->get()->take(3);
}

من چند مورد در کد بالا پیاده کرده‌ام. اول مقدار slug برابر با دسته‌ی من است و با در کد اعلام می‌کنم که پُست‌هایی که دارای دسته‌بندی‌اند و آن دسته‌بندی دارای slug مورد نظرم است را انتخاب و سه تای آخر را به من بده.

نکته‌ی مهم این است که برای اینکه بتوانم slug که به من پاس داده شده را در تابع کوئری خود استفاده کنم باید از کلمه‌ی کلیدی use استفاده کنم و با این کار به راحتی می‌توانم مقدار slug دریافتی را در کوئری خود به کار ببرم.

درباره نویسنده: احسان

مطالب زیر را حتما بخوانید