در این مقاله قصد داریم ایجاد سیستم نظرات پیشرفته تو در تو در لاراول را به شما آموزش دهیم. در قسمت نظردهی برخی سایت ها، شما می توانید نظرات یک کاربر را reply کرده یا پاسخ بدهید و سپس کاربر دیگری نظرات کاربر دیگری را reply کرده و به این ترتیب.
به این ترتیب ایجاد نظرات تو در تو یکی از موارد خیلی مهم در طراحی برنامه های تحت وب است، که ما در مثال از پایه به آموزش این مطلب می پردازیم.
در این مثال از رابطه Polymorphic استفاده می کنیم.
1-نصب و پیکربندی لاراول
2-ایجاد یک مدل و migration
3-تعریف رابطه Polymorphic
4- تعریف View، کنترلر، و روت ها
5- ذخیره و نمایش پست ها
6- ایجاد یک فرم برای افزودن نظر
7- نمایش نظرات
8- ایجاد یک فرم reply (پاسخ) و ذخیره replyها
9- کدهای گیت هاپ
laravel new comments # or composer create-project laravel/laravel comments --prefer-dist
سپس به پروژه بروید
cd comments
برنامه را در یک ویرایشگر باز کنید.
code .
پایگاه داده MySql را در فایل .env پیکربندی و سپس یک auth را با دستور زیر ایجاد کنید.
php artisan make:auth
سپس پایگاه داده را با دستور زیر migrate کنید.
php artisan migrate
یک مدل post به همراه migration آن را ایجاد کنید.
php artisan make:model Post -m
سپس Schema آن را در فایل Migration مربوط به post قرار دهید.
// create_posts_table public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->text('body'); $table->timestamps(); }); }
همچنین باید مدل Comment را به همراه migration آن تولید کنید:
php artisan make:model Comment -m
خب، حالا از رابطه Polymorphic برای ارتباط بین مدل ها استفاده می کنیم. پس باید Schema را با این روش ایجاد کنیم:
// create_comments_table public function up() { Schema::create('comments', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->integer('parent_id')->unsigned(); $table->text('body'); $table->integer('commentable_id')->unsigned(); $table->string('commentable_type'); $table->timestamps(); }); }
سپس پایگاه داده را با دستور زیر migrate کنید.
php artisan migrat
در این مرحله باید رابطه Polymorphic را برای ارتباط بین مدل های برنامه تعریف کنید، پس دستورات زیر را در فایل app > Post.php ایجاد کنید.
<?php // Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function comments() { return $this->morphMany(Comment::class, 'commentable')->whereNull('parent_id'); } }
در اینجا ما همه نظراتی را که مقدار parent_idشان برابر null است، می نویسیم. اینکار به این دلیل است که ما می خواهیم نظرات سطح والد را نمایش دهیم و همچنین این نظرات را ذخیره کنیم. به این ترتیب ما باید بین نظرات و پاسخ ها (reply) تفاوت قائل شویم.
همچنین هر پست متعلق به یک کاربر است. که می توانیم رابطه belongsTo را روی آن تعریف کنیم.
<?php // Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function user() { return $this->belongsTo(User::class); } public function comments() { return $this->morphMany(Comment::class, 'commentable')->whereNull('parent_id'); } }
برای تعریف ارتباط بین نظر با پست ها کدهای زیر را در فایل Comment.php قرار دهید.
<?php // Comment.php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { public function user() { return $this->belongsTo(User::class); } }
یک کنترلر به نام PostController.php با استفاده از دستور زیر ایجاد کنید
php artisan make:controller PostController
در قدم بعدی باید برای ویوها (Views) مسیر (route) تعریف و پست ها را در پایگاه داده ذخیره کنیم. برای اینکار کدهای زیر را در فایل routes > web.php بنویسید.
<?php // web.php Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); Route::get('/post/create', 'PostController@create')->name('post.create'); Route::post('/post/store', 'PostController@store')->name('post.store');
سپس کدهای زیر را در فایل PostController.php قرار دهید.
<?php // PostController.php namespace App\Http\Controllers; use Illuminate\Http\Request; class PostController extends Controller { public function __construct() { return $this->middleware('auth'); } public function create() { return view('post'); } public function store(Request $request) { // store code } }
حالا، یک فرم برای ایجاد پست درست می کنیم. برای اینکار یک فایل با نام post.blade.php در فولدر resource > views ایجاد کرده و کدهای زیر را در فایل post.blade.php قرار دهید.
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Create Post</div> <div class="card-body"> <form method="post" action="{{ route('post.store') }}"> <div class="form-group"> @csrf <label class="label">Post Title: </label> <input type="text" name="title" class="form-control" required/> </div> <div class="form-group"> <label class="label">Post Body: </label> <textarea name="body" rows="10" cols="30" class="form-control" required></textarea> </div> <div class="form-group"> <input type="submit" class="btn btn-success" /> </div> </form> </div> </div> </div> </div> </div> @endsection
حالا فایل resource > views > layouts > app.blade.php را باز و یک لینک برای ایجاد پست ایجاد کنید.
ما باید یک لینک به قسمت @else موجود در navigation bar اختصاص دهیم. چون می خواهیم اگر کاربری با موفقیت به برنامه لاگین کرد بتواند یک پست ایجاد کند و در غیر اینصورت نتواند.
@else <li class="nav-item"> <a class="nav-link" href="{{ route('post.create') }}">Create Post</a> </li>
سپس به آدرس http://localhost:8000/register بروید و یک کاربر را ثبت کنید. بعد از اینکه با آن کاربر لاگین کردید می توانید لینک Create Post را در قسمت Navigation Bar ببینید.
روی این لینک کلیک کنید تا به مسیر http://localhost:8000/post/create منتقل شوید. همان طور که می بینید در این صفحه یک فرم به همراه فیلدهای عنوان (title) و محتوای پست (body)، داریم.
در این مرحله برای ذخیره سازی پست ها را در پایگاه داده، کدهای زیر را متد store در فایل PostController.php قرار دهید.
<?php // PostController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; class PostController extends Controller { public function __construct() { return $this->middleware('auth'); } public function create() { return view('post'); } public function store(Request $request) { $post = new Post; $post->title = $request->get('title'); $post->body = $request->get('body'); $post->save(); return redirect('posts'); } }
بعد از اینکه پست ذخیره شد، ما به صفحه ای که لیست پستها را نمایش می دهد منتقل می شویم. کدهای زیر را در فایل web.php قرار دهید.
// web.php Route::get('/posts', 'PostController@index')->name('posts');
همچنین باید متد index زیر را در فایل PostController.php ایجاد کنیم.
// PostController.php public function index() { $posts = Post::all(); return view('index', compact('posts')); }
فایل index.blade.php را درون فولدر views ایجاد و کدهای زیر را درون این فایل قرار دهید:
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <table class="table table-striped"> <thead> <th>ID</th> <th>Title</th> <th>Action</th> </thead> <tbody> @foreach($posts as $post) <tr> <td>{{ $post->id }}</td> <td>{{ $post->title }}</td> <td> <a href="{{ route('post.show', $post->id) }}" class="btn btn-primary">Show Post</a> </td> </tr> @endforeach </tbody> </table> </div> </div> </div> @endsection
مسیر (route) مربوط به نمایش پست ها را در فایل web.php قرار بنویسید:
// web.php Route::get('/post/show/{id}', 'PostController@show')->name('post.show');
سپس متد show() را درون فایل PostController.php تعریف کنید
// PostController.php public function show($id) { $post = Post::find($id); return view('show', compact('post')); }
فایل show.blade.php را درون فولدر views ایجاد و کدهای زیر را در آن قرار دهید:
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-body"> <p>{{ $post->title }}</p> <p> {{ $post->body }} </p> </div> </div> </div> </div> </div> @endsection
خب، به این ترتیب می توانید هر پست را به تفکیک ببینید. در مرحله بعد باید نظرات را در پست هایمان نمایش دهیم.
ابتدا با دستور زیر یک فایل به نام CommentController.php ایجاد کنید:
php artisan make:controller CommentController
سپس یک فرم درون فایل show.blade.php ایجاد کنید تا توسط آن بتوانیم برای هر پست کامنت بگذاریم.
کدهای زیر را در فایل show.blade.php قرار دهید.
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-body"> <p><b>{{ $post->title }}</b></p> <p> {{ $post->body }} </p> <hr /> <h4>Add comment</h4> <form method="post" action="{{ route('comment.add') }}"> @csrf <div class="form-group"> <input type="text" name="comment_body" class="form-control" /> <input type="hidden" name="post_id" value="{{ $post->id }}" /> </div> <div class="form-group"> <input type="submit" class="btn btn-warning" value="Add Comment" /> </div> </form> </div> </div> </div> </div> </div> @endsection
خب حالا یک فرم اضافه کردیم که می توانیم با آن نظر خود را ارسال کنیم. در نتیجه باید یک مسیر (route) برای ذخیره نظر تعریف کنیم.
// web.php Route::post('/comment/store', 'CommentController@store')->name('comment.add');
خب حالا یک متد Store() می نویسیم و نظرات خود را توسط رابطه morphMany() ذخیره می کنیم.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Comment; use App\Post; class CommentController extends Controller { public function store(Request $request) { $comment = new Comment; $comment->body = $request->get('comment_body'); $comment->user()->associate($request->user()); $post = Post::find($request->get('post_id')); $post->comments()->save($comment); return back(); } }
در صورتی که همه کارها را به درستی انجام داده باشید، اکنون کاربر می تواند نظرات خود را اضافه کند. دقت کنید که فعلا نمی توانیم نظرات را نمایش دهیم. با تکمیل ذخیره نظر مقدار parent_id برابر null است.
خب ما رابطه بین نظر و پست ها را ایجاد کردیم، و اکنون می توانیم همه نظرات مربوط به یک پست را نمایش بدهیم. برای اینکار کدهای زیر را در فایل show.blade.php قرار دهید .دقت داشته باشید که همه این نظرات ، نظر والد (Parent) هستند. سپس یک دکمه reply (پاسخ) اضافه می کنیم و در قدم بعد همه نظرات پاسخ داده شده را نمایش می دهیم.
<!-- show.blade.php --> @extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-body"> <p><b>{{ $post->title }}</b></p> <p> {{ $post->body }} </p> <hr /> <h4>Display Comments</h4> @foreach($post->comments as $comment) <div class="display-comment"> <strong>{{ $comment->user->name }}</strong> <p>{{ $comment->body }}</p> </div> @endforeach <hr /> <h4>Add comment</h4> <form method="post" action="{{ route('comment.add') }}"> @csrf <div class="form-group"> <input type="text" name="comment_body" class="form-control" /> <input type="hidden" name="post_id" value="{{ $post->id }}" /> </div> <div class="form-group"> <input type="submit" class="btn btn-warning" value="Add Comment" /> </div> </form> </div> </div> </div> </div> </div> @endsection
حالا، نظر خود را اضافه کنید، می بینید که نظرات را در همان url نمایش می دهد.
یک متد با نام replies() درون مدل Comment.php ایجاد کنید.
<?php // Comment.php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { public function user() { return $this->belongsTo(User::class); } public function replies() { return $this->hasMany(Comment::class, 'parent_id'); } }
در متد replies یک کلید اصلی به نام parent_id ایجاد می کنیم. چون می خواهیم یک reply (پاسخ) مربوط به شناسه نظر والد آن را برگردانیم.
حالا کد مربوط به نمایش همه کامنت ها به همراه پاسخ های آنها را در یک فایل partial می نویسیم.
دلیل اینکار اینست که ما میخواهیم نظرات تو در تو ایجاد کنیم و تعداد سطح تو در تو بستگی به تعامل کاربرمان دارد و از طرفی از قبل نمی دانیم که تا چند سطح می خواهیم نظرات را تو در تو کنیم. برای اینکه انعطاف پذیری بیشتری به این قسمت بدهیم، می خواهیم این قسمت را در یک فایل partial قرار دهیم و برای نمایش هر نظر تو در تو از این فایل partial استفاده می کنیم.
پس یک فولدر به نام partial در مسیر reaource > views قرار دهید و داخل این فولدر یک فایل به نام comment_replies.blade.php ایجاد کنید.
سپس کدهای زیر را در این فایل قرار دهید:
<!-- _comment_replies.blade.php --> @foreach($comments as $comment) <div class="display-comment"> <strong>{{ $comment->user->name }}</strong> <p>{{ $comment->body }}</p> <a href="" id="reply"></a> <form method="post" action="{{ route('reply.add') }}"> @csrf <div class="form-group"> <input type="text" name="comment_body" class="form-control" /> <input type="hidden" name="post_id" value="{{ $post_id }}" /> <input type="hidden" name="comment_id" value="{{ $comment->id }}" /> </div> <div class="form-group"> <input type="submit" class="btn btn-warning" value="Reply" /> </div> </form> @include('partials._comment_replies', ['comments' => $comment->replies]) </div> @endforeach
در اینجا من همه پاسخ ها را با یک textbox نمایش دادم.
حالا این partial نیاز به دریافت دو پارامتر دارد.
موقعی که ما این partial را در فایل show.blade.php قرار می دهیم، باید این دو پارامتر ها را به آن پاس دهیم.
همچنین برای ذخیره پاسخ ها باید مسیر (route) مربوط به آن را ایجاد کنیم.
کدهای زیر را درون فایل routes > web.php قرار دهید.
// web.php Route::post('/reply/store', 'CommentController@replyStore')->name('reply.add');
و در نهایت فایل web.php ما باید مطابق زیر باشد:
<?php // web.php Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); Route::get('/post/create', 'PostController@create')->name('post.create'); Route::post('/post/store', 'PostController@store')->name('post.store'); Route::get('/posts', 'PostController@index')->name('posts'); Route::get('/post/show/{id}', 'PostController@show')->name('post.show'); Route::post('/comment/store', 'CommentController@store')->name('comment.add'); Route::post('/reply/store', 'CommentController@replyStore')->name('reply.add');
سپس باید متد replyStore() را داخل فایل CmmentCotroller.php تعریف کنیم:
<?php // CommentController.php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Comment; use App\Post; class CommentController extends Controller { public function store(Request $request) { $comment = new Comment; $comment->body = $request->get('comment_body'); $comment->user()->associate($request->user()); $post = Post::find($request->get('post_id')); $post->comments()->save($comment); return back(); } public function replyStore(Request $request) { $reply = new Comment(); $reply->body = $request->get('comment_body'); $reply->user()->associate($request->user()); $reply->parent_id = $request->get('comment_id'); $post = Post::find($request->get('post_id')); $post->comments()->save($reply); return back(); } }
خب در اینجا متدهای store و replyStore تقریبا شبیه هم هستند. ما نظرات والد و reply (پاسخ) مربوط به هر کدام را درون یک جدول یکسان نگهداری میکنیم. اما موقعی که نظر والد را ذخیره میکنیم، مقدار فیلد parent_id برابر null است و وقتی که reply (پاسخ) را ذخیره کنیم، مقدار این فیلد برابر Comment_id است.این تفاوت آن است.
در انتها فایل show.blade.php مانند زیر است:
<!-- show.blade.php --> @extends('layouts.app') <style> .display-comment .display-comment { margin-left: 40px } </style> @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-body"> <p><b>{{ $post->title }}</b></p> <p> {{ $post->body }} </p> <hr /> <h4>Display Comments</h4> @include('partials._comment_replies', ['comments' => $post->comments, 'post_id' => $post->id]) <hr /> <h4>Add comment</h4> <form method="post" action="{{ route('comment.add') }}"> @csrf <div class="form-group"> <input type="text" name="comment_body" class="form-control" /> <input type="hidden" name="post_id" value="{{ $post->id }}" /> </div> <div class="form-group"> <input type="submit" class="btn btn-warning" value="Add Comment" /> </div> </form> </div> </div> </div> </div> </div> @endsection
در اینجا من باید استایل های css را برای نمایش درست نظرات تو در تو ایجاد کنم. سپس partial به همراه دو پارامتر زیر را به آن اضافه می کنم
ما می توانیم نظرات والد را از اینجا اضافه کنیم و نظرات reply را از partial اضافه کنیم. و در انتها، نظرات والد به همراه پاسخ های آنها را مانند زیر به پایگاه داده اضافه کردم:
همچنین خروجی نهایی مانند زیر است:
خب آموزش این قسمت به پایان رسید. از این لینک می توانید کدهای مربوط به این آموزش را دانلود کنید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.