در قسمت اول آموزش ساخت یک برنامه مشابه توییتر با لاراول و Vuejs به مقدمات ساخت برنامه پرداختیم. و تا حدودی تنظیمات مربوط به لاراول و Vuejs را به سمتی بردیم که بتوانیم یک فرم توییت ایجاد کرده و آن را به کاربر موردنظر مرتبط کنیم. از طرفی با استفاده از کامپوننت Axios داده ها را به سمت سرور ارسال و خروجی دریافت شده را در رابط کاربری نمایش دادیم.
در این بخش به ادامه آموزش می پردازیم.
حال دومین کامپوننت vue مان را با نام timelineComponent.vue را در فولدر resource > asset > js >component ایجاد کرده و کدهای زیر را در آن قرار می دهیم.
// TimelineComponent.vue <template> <div class="col-md-8 posts"> <p v-if="!posts.length">No posts</p> <div class="media" v-for="post in posts" :key="post.id"> <img class="mr-3" /> <div class="media-body"> <div class="mt-3"> <a href="#">{{ post.user.name }}</a> </div> <p>{{ post.body }}</p> </div> </div> </div> </template> <script> import Event from '../event.js'; export default { data() { return { posts: [], post: {} } }, mounted() { Event.$on('added_tweet', (post) => { this.posts.unshift(post); }); } } </script>
حال این کامپوننت را در فایل app.js ثبت می کنیم.
// app.js Vue.component('timeline-component', require('./components/TimelineComponent.vue'));
هر وقت این رویداد اجرا شد، یک داده جدید به آرایه posts اضافه و برای نمایش توییت های مربوط به هر کاربر از این آرایه استفاده می کنیم.
حال این کامپوننت را در فایل home.blade.php بکار می گیریم.
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<form-component></form-component>
<timeline-component></timeline-component>
</div>
</div>
@endsection
فایل را ذخیره کرده و به آدرس localhost:8000/home بروید و یک توییت اضافه کنید.
برای نمایش زمان، باید یک پروپرتی داخل مدل post.php ایجاد و سپس از این پروپرتی برای افزودن زمان به توییت ها استفاده می کنیم.
// Post.php <?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['user_id', 'body']; protected $appends = ['createdDate']; public function user() { return $this->belongsTo(User::class); } public function getCreatedDateAttribute() { return $this->created_at->diffForHumans(); } }
حال می توانیم از داخل فایل timelineComponent.vue به پروپرتی createdDate دسترسی داشته باشیم.
<div class="mt-3"> <a href="#"> {{ post.user.name }} </a> | {{ post.createdDate }} </div>
در نهایت صفحه را رفرش کرده و یک توییت جدید اضافه کنید. با این کار در قسمت زمان عبارت یک ثانیه قبل (1 second ago) یا یک چیز شبیه این را می بینید.
ما از Route Model Binding برای نمایش پروفایل کاربران استفاده و برای هر کاربر (username) یک پروفایل ایجاد می کنیم.
معمولاً از username برای اینکار استفاده می شود چون به این طریق می توانیم یک url منحصر به فرد برای پروفایل هر کاربر داشته باشیم.
در هر صورت در این مثال من از نام کاربری (username) برای تولید url منحصر به فرد استفاده می کنم.
همچنین باید یک پروپرتی منحصر به فرد داخل فایل RegistryController.php بوجود بیاوریم و به این ترتیب می توانیم یک لینک منحصر به فرد برای پروفایل کاربران ایجاد کنیم.
// RegisterController.php protected function validator(array $data) { return Validator::make($data, [ 'name' => 'required|string|max:255|unique:users', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); }
حال یک کنترلر به نام UserController تولید می کنیم. دستورات زیر را در ترمینال وارد کنید.
php artisan make:controller UserController
سپس تابع زیر را در فایل UserController.php اضافه کنید.
<?php // UserController.php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\User; class UserController extends Controller { public function show(User $user) { return view('user', compact('user')); } }
در اینجا من از Route Model Binding استفاده کردم، اما ما می خواهیم از نام کاربری (username) روی روت استفاده کنیم. سپس توسط کلید name می توان به هر کدام از کاربران دسترسی داشت.
توجه: چنانچه مفاهیم ساخت میکروبلاگ مشابه توییتر برای شما قابل درک نیست لطفا ابتدا دوره آموزشی صفر تا صد لاراول را یاد بگیرید و سپس با این فریم ورک که جادوگری بی نظیر است راحت تر کار کنید. برای دریافت این دوره آموزشی روی لینک زیر کلیک نمایید:
برای اینکار یک کلید مسیریابی داخل فایل user.php تعریف می کنیم.
// User.php public function getRouteKeyName() { return 'name'; }
یک فایل view به نام user.blade.php داخل فولدر views ایجاد کنید.
<!-- user.blade.php --> @extends('layouts.app') @section('content') <div class="container"> {{ $user->name }} </div> @endsection
همچنین باید یک روت داخل فایل web.php ایجاد کنید.
// web.php <?php Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); Route::post('tweet/save', 'PostController@store'); Route::get('users/{user}', 'UserController@show')->name('user.show');
من یک کاربر به نام krunal را ثبت نام کردم و برای دسترسی به پروفایل این کاربر باید به آدرس http://localhost:8000/users/krunal برویم.
با ورود به این آدرس می توانید پروفایل این کاربر را ببینید.
معمولا هنگامی که ما از داده ای که از داده دیگر در همان جدول ایجاد شده، نیاز داشته باشیم، نباید آن داده را دوباره ذخیره کنیم بلکه باید هنگام بازیابی داده ها، رکورد داده های مرتبط را به هم الحاق (join) کنیم.
همان طور که می بینید آدرس پروفایل مبتنی بر username است، پس می توانیم یک پروپرتی جدید را به url متصل کنیم و این آدرس را به مدل user.php الحاق کنیم.
حال یک accessor برای profilelink داخل مدل user.php ایجاد می کنیم.
<?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; protected $appends = ['profileLink']; public function posts() { return $this->hasMany(Post::class); } public function getRouteKeyName() { return 'name'; } public function getProfileLinkAttribute() { return route('user.show', $this); } }
حال ما این url را داخل فایل timelineComponent.vue نمایش می دهیم.
// TimelineComponent.vue <template> <div class="col-md-8 posts"> <p v-if="!posts.length">No posts</p> <div class="media" v-for="post in posts" :key="post.id"> <img class="mr-3" /> <div class="media-body"> <div class="mt-3"> <a :href="post.user.profileLink">{{ post.user.name }}</a> | {{ post.createdDate }} </div> <p>{{ post.body }}</p> </div> </div> </div> </template> <script> import Event from '../event.js'; export default { data() { return { posts: [], post: {} } }, mounted() { Event.$on('added_tweet', (post) => { this.posts.unshift(post); }); } } </script>
یک فایل migration برای جدول followers ایجاد می کنیم. برای اینکار دستورات زیر را در ترمینال اجرا کنید.
php artisan make:model Follower -m
حال کدهای زیر را در فایل create_followes_table.php قرار دهید.
// create_users_table public function up() { Schema::create('followers', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->integer('follower_id')->unsigned(); $table->nullableTimestamps(); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('follower_id')->references('id')->on('users')->onDelete('cascade'); }); }
جدول را migrate کنید.
php artisan migrate
حال باید یک ارتباط با مدل user ایجاد کنیم.
// User.php public function following() { return $this->belongsToMany(User::class, followers, user_id, follower_id); }
در قدم بعد باید چند شرط به شرح زیر را به آن اضافه کنیم:
هر شرط را داخل یک تابع اختصاصی در فایل user.php تعریف می کنیم.
من تمام این سه تابع را داخل مدل user.php ایجاد می کنیم.
<?php // User.php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; protected $appends = ['profileLink']; public function posts() { return $this->hasMany(Post::class); } public function getRouteKeyName() { return 'name'; } public function getProfileLinkAttribute() { return route('user.show', $this); } public function following() { return $this->belongsToMany(User::class, 'followers', 'user_id', 'follower_id'); } public function isNot($user) { return $this->id !== $user->id; } public function isFollowing($user) { return (bool) $this->following->where('id', $user->id)->count(); } public function canFollow($user) { if(!$this->isNot($user)) { return false; } return !$this->isFollowing($user); } }
تمام این شرط ها را در تابع مخصوص خودش نوشتیم. حال فایل user.blade.php را باز کرده و لینک های follow و unfollow را پیاده سازی می کنیم.
@extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-12"> <h3>{{ $user->name }}</h3> @if(auth()->user()->isNot($user)) @if(auth()->user()->isFollowing($user)) <a href="#" class="btn btn-danger">No Follow</a> @else <a href="#" class="btn btn-success">Follow</a> @endif @endif </div> </div> </div> @endsection
خب حال اگر شما به پروفایل تان مراجعه کنید لینک های follow و unfollow را نمی بینید. در صورتی که با نام کاربری دیگر به سایت وارد (login) شوید می توانید لینک های follow و unfollow را مشاهده کنید.
روت users/{user}/follow را در فایل web.php تعریف کنید.
<?php // web.php Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); Route::post('tweet/save', 'PostController@store'); Route::get('users/{user}', 'UserController@show')->name('user.show'); Route::get('users/{user}/follow', 'UserController@follow')->name('user.follow');
حال می توانیم متد follow() را در فایل userController.php بنویسیم.
// UserController.php public function follow(Request $request, User $user) { if($request->user()->canFollow($user)) { $request->user()->following()->attach($user); } return redirect()->back(); }
همچنین باید فایل user.blade.php را بروزرسانی کنیم.
@extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-12"> <h3>{{ $user->name }}</h3> @if(auth()->user()->isNot($user)) @if(auth()->user()->isFollowing($user)) <a href="#" class="btn btn-danger">No Follow</a> @else <a href="{{ route('user.follow', $user) }}" class="btn btn-success">Follow</a> @endif @endif </div> </div> </div> @endsection
حال وقتی که با نام کاربری دیگری وارد سایت شوید و آدرس پروفایل خودتان را وارد کنید، می توانید آن را follow کنید.
وقتی که روی لینک follow کلیک کنید می بینید که لینک unfollow ظاهر می شود و می توانید آن را unfollow کنید.
همچنین می بینید که جدول followers یک ورودی دارد.
ما می توانیم یک کاربری را که هم اکنون دنبال می کنیم را unfollow کنیم. برای اینکار تابع زیر را در فایل user.php تعریف کنید.
// User.php public function canUnFollow($user) { return $this->isFollowing($user); }
همچنین یک روت برای unfollow کردن داخل فایل web.php تعریف می کنیم.
// web.php Route::get('users/{user}/unfollow', 'UserController@unfollow')->name('user.unfollow');
حال تابع unfollow() را داخل فایل userController.php می نویسیم.
// UserController.php public function unFollow(Request $request, User $user) { if($request->user()->canUnFollow($user)) { $request->user()->following()->detach($user); } return redirect()->back(); }
همچنین باید فایل user.blade.php را بروزرسانی کنیم.
@if(auth()->user()->isNot($user)) @if(auth()->user()->isFollowing($user)) <a href="{{ route('user.unfollow', $user) }}" class="btn btn-danger">No Follow</a> @else <a href="{{ route('user.follow', $user) }}" class="btn btn-success">Follow</a> @endif @endif
به این ترتیب توانستیم قابلیت follow و unfollow را پیاده کنیم.
در این مرحله باید توییت های تمام کاربرانی که آنها را دنبال میکنیم را بازیابی کنیم، یعنی وقتی که ما کاربری را دنبال میکنیم باید بتوانیم توییت هایشان را ببینیم.
برای این کار ما متد index() را داخل فایل postController.php تعریف میکنیم
<?php // PostController.php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Post; class PostController extends Controller { public function index(Request $request, Post $post) { $posts = $post->whereIn('user_id', $request->user()->following() ->pluck('users.id') ->push($request->user()->id)) ->with('user') ->orderBy('created_at', 'desc') ->take($request->get('limit', 10)) ->get(); return response()->json($posts); } public function store(Request $request, Post $post) { $newPost = $request->user()->posts()->create([ 'body' => $request->get('body') ]); return response()->json($post->with('user')->find($newPost->id)); } }
ما تمام پست های کاربرانی که آنها را دنبال میکنیم را بازیابی کردیم. تعداد کاربران ممکن است بیش از یک مورد باشد.
در کد بالا من گفتم که کاربر جاری که وارد سیستم شده است می تواند هم پست های خودش و هم پست های کاربرانی که آنها را دنبال می کند را ببیند.
در مرحله بعد باید همه این پست ها را نمایش دهیم.
یک روت برای این تابع index() تعریف میکنیم.
// web.php Route::get('posts', 'PostController@index')->name('posts.index');
سپس باید یک درخواست از سمت کاربر به سرور را با کتابخانه Axios ارسال کنیم که این کار را در فایل timelineComponent.vue انجام می دهیم.
// TimelineComponent.vue <template> <div class="col-md-8 posts"> <p v-if="!posts.length">No posts</p> <div class="media" v-for="post in posts" :key="post.id"> <img class="mr-3" /> <div class="media-body"> <div class="mt-3"> <a :href="post.user.profileLink">{{ post.user.name }}</a> | {{ post.createdDate }} </div> <p>{{ post.body }}</p> </div> </div> </div> </template> <script> import Event from '../event.js'; export default { data() { return { posts: [], post: {} } }, mounted() { axios.get('/posts').then((resp => { this.posts = resp.data; })); Event.$on('added_tweet', (post) => { this.posts.unshift(post); }); } } </script>
حالا می توانید هم پست های کاربرانی که آنها را دنبال می کنید و هم پست های خودتان را مشاهده کنید.
به این ترتیب آموزش ما به اتمام رسید. در این آموزش لاراول و Vuejs از مفاهیم زیادی استفاده کردیم. امیدوارم بتوانید از این آموزش در پروژه های خودتان بهره ببرید. این دقیقاً مانند برنامه توییتر نیست، اما سعی کردم مفاهیم زیادی از قبیل نحوه کار با رویدادها در vue ، ارتباط بین مدل ها در لاراول و کوئری های پیچیده را به شما آموزش بدهم. تمام کدهای این پروژه را در سایت گیت هاب آپلود کردم که در صورت نیاز می توانید آن را دریافت کنید.
برای استفاده از کدهایی که در گیت هاب قرار دادم مراحل زیر را دنبال کنید:
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.