مشخصات مقاله
-
1856
-
0.0
-
6764
-
0
-
0
آموزش نوشتن یک برنامه لیست نمایش کارها و وظایف با Laravel
نوشتن یک برنامه ی سطح متوسط لیست نمایش کارها و وظایف با Laravel
فهرست محتوا
- مقدمه
- نصب
-
آماده سازی پایگاه داده
- Migration پایگاه داده
- مدل های Eloquent
- رابطه های Eloquent (مدیریت رابطه بین جداول به وسیله ی Eloquent)
-
Routing (آدرس دهی)
- بازیابی و نمایش یک view
- Authentication (احراز هویت کاربر)
- کنترلر Task
-
ساخت Layout و View برنامه
- ایجاد Layout
- ایجاد یک View فرزند
-
اضافه کردن Task های جدید به لیست
- Validation (اعتبارسنجی)
- ایجاد Task
-
نمایش Task های جاری
- Dependency Injection
- نمایش دادن Task ها
-
حذف کردن Task های جاری
- اضافه کردن دکمه ی Delete به برنامه
- Route Model Binding (bind کردن مدل)
- Authorization (مجوزدهی)
- حذف کردن Task از لیست
مقدمه
در این آموزش نحوه ی نوشتن برنامه ی سطح متوسط به وسیله ی چارچوب نرم افزاری Laravel را به شما آموخته و نیز توضیحاتی درباره ی مباحث مطرحی همچون migration، ابزار Eloquent، Routing (مسیردهی)، authentication (احرازهویت)، authorization (مجوزدهی)،dependency injection، validation (اعتبارسنجی)، view ها و قالب های Blade برای شما ارائه می دهیم. در صورتی که از قبل با لاراول کار کرده یا به طور کلی با فریم ورک های PHP آشنایی دارید، این آموزش در مسیر برنامه نویسی در این چارچوب کاری کمک شایانی به شما خواهد کرد.
برای نوشتن برنامه ی سطح متوسط نمایش لیست وظایف از تمامی ابزار و امکانات ذکر شده ی لاراول بهره می گیریم. برنامه ای که در این آموزش به نوشتن آن می پردازیم علاوه بر نمایش و حذف وظایف در لیست، به کاربران اجاز می دهد حساب کاربری ایجاد کرده و عملیات احراز هویت انجام دهند. کد کامل این پروژه در GitHub برای دانلود در دسترس می باشد.
نصب
ابتدا باید یک نسخه ی جدید از Laravel را نصب نمایید. برای اجرای این framework می توانید از ماشین مجازی Homestead یا یک محیط توسعه محلی PHP به انتخاب خود استفاده کنید. پس از نصب و راه اندازی محیط توسعه محلی، می توانید چارچوب نرم افزاری لاراول را به وسیله یComposer نصب کنید:
composer create-project laravel/laravel quickstart --prefer-dist
می توانید کد برنامه را دانلود کرده و آن را بر روی ماشین محلی خود اجرا کنید. برای این منظور کافی است مخزن (repository) Git آن را کلون کرده وdependency های آن را نصب کنید:
git clone https://github.com/laravel/quickstart-intermediate quickstart cd quickstart composer install php artisan migrate
آماده سازی پایگاه داده
Migration پایگاه داده
در گام نخست با استفاده از یک migration جدولی در پایگاه داده تعریف می کنیم که تمامی task ها در آن ذخیره می شوند. قابلیت migration در لاراول روشی آسان و بهینه برای تعریف ساختار جدول و اعمال تغییرات به آن با استفاده از کدهای بهینه و کارآمد PHP فراهم می آورد. باmigration دیگر نیازی نیست که برنامه نویسان تیم ستون های مورد نظر را دستی به نسخه ی محلی خود از پایگاه داده اضافه کنند، بلکه کافی استmigration هایی را که مدیر پروژه در source control (سیستم کنترل تغییرات) قرار می دهد، اجرا کنند.
جدول users
همان طور که گفته شد در این برنامه کاربران می توانند حساب کاربری ایجاد کنند. بنابراین برای ذخیره ی اطلاعات مربوط به هر کاربر بایستی یک جدول به نام users در پایگاه داده ایجاد کنیم. خوشبختانه لاراول با migration ای که در اختیار ما قرار می دهد این امکان را فراهم می آورد تا یک جدول ساده ی users، بدون زحمت انجام آن به صورت دستی، ایجاد کنیم. migration پیش فرضی که برای ایجاد جدول نام برده از آن استفاده می شود، در پوشه یdatabase/migrations قرار دارد.
جدول tasks
اکنون جدول دیگری به نام tasks در دیتابیس ایجاد می کنیم که اطلاعات task ها در آن نگه داری می شوند. برای آسان تر کردن و سرعت بخشیدن به فرایند کدنویسی در حین ساخت پروژه، می توانید از Artisan CLI استفاده کنید که کلاس های مورد نیاز را برای شما ایجاد کرده و همان طور که گفته شد پروسه ی کدنویسی را تسهیل می بخشد. با استفاده از دستور make:migration یک migration برای جدول tasks ایجاد می کنیم:
php artisan make:migration create_tasks_table --create=tasks
migration ایجاد شده در پوشه ی database/migrations پروژه ی شما جایگذاری می شود. همان طور که می بینید، دستور مذکور یک ID خود افزاینده (auto-incrementing) به همراه Timestamp (برای مشخص کردن ترتیب اجرا) به فایل migration اضافه می کند. ابتدا به ویرایش این فایل و افزودن یک ستون از نوع string برای ذخیره ی اسم task و نیز یک ستون به نام user id که دو جدول tasks و users را به هم متصل می کند (رابطه ی بین این دو ایجاد می کند)، می پردازیم.
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTasksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->index();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('tasks');
}
}
برای اجرای migration ها، از دستور آرتیزان migrate کمک می گیریم. در صورتی که محیط توسعه ی مورد استفاده ی شما Homestead باشد، لازم است این دستور را از درون ماشین مجازی صدا بزنید. دلیل انجام این کار، عدم داشتن دسترسی مسقیم ماشین میزبان به پایگاه داده می باشد.
php artisan migrate
این دستورجداول پایگاه داده را ایجاد می کند. اگر از طریقdatabase client دلخواه خود برای مشاهده و بررسی جداول استفاده کنید، در آن صورت جداول تازه ایجاد شده ی tasks و users را مشاهده خواهید کرد که دربردارنده ی ستون های تعریف شده در migration هستند. در زیر به ایجاد مدل های Eloquent اپلیکیشن خود خواهیم پرداخت.
مدل های Eloquent
Eloquent ابزار ORM پیش فرض Laravel می باشد. ORM ذکر شده فرآیند ذخیره اطلاعات در پایگاه داده و واکشی آن ها را به وسیله ی مدل های کارآمد فراهم آورده که متعاقبا تعامل با پایگاه داده آسان تر می کند. هر مدل مسقیما با یک جدول در پایگاه داده متناظر می باشد.
مدل User
ابتدا می بایست یک مدل ایجاد کنیم که به جدول users در پایگاه داده منطبق باشد (تعلق داشته باشد). اگر در پوشه ی app پروژه ی خود پیمایش کنید، می بینید که لاراول یک مدل پیش فرض به نام User برای استفاده ی شما در نظر گرفته است، از اینرو نیازی نیست خود یک مدل به صورت دستی ایجاد کنید.
مدل Task
حال یک مدل به نام Task ایجاد می کنیم که با جدول tasks در پایگاه داده منطبق می باشد. این بار نیز می توان با بهره گیری از یک دستور صریح آرتیزان برای ایجاد مدل اقدام کرد. دستور مورد نیاز برای ایجاد مدل عبارت است از: make:model. این دستور را به صورت زیر فراخوانی می کنیم:
php artisan make:model Task
مدل تازه ایجاد شده داخل پوشه ی app برنامه ی کاربردی شما جای گذاری می شود. به طور پیش فرض، کلاس مدل تهی و فاقد هیچ گونه فیلد و متد می باشد. لزومی ندارد که صریحا به مدل Eloquent اعلان کنیم دقیقا با کدام کلاس متناظر می باشد (تعلق دارد) زیرا خود مدل فرض می گیرد جدول پایگاه داده در واقع همان نسخه ی جمع اسم مدل می باشد. پس نتیجه می گیریم که مدل Task با جدول tasks در پایگاه داده که همان نسخه ی جمع اسم مدل (Task) است، متناظر می باشد.
در اینجا چند آیتم به مدل مزبور اضافه می کنیم. ابتدا در کد ذکر می کنیم که خصیصه ی (attribute) name این مدل بایستی mass-assignable باشد (قابلیت تخصیص کلی).
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
}
رابطه های Eloquent (مدیریت رابطه بین جداول به وسیله ی Eloquent)
حالا که مدل های برنامه را ایجاد کردیم، بایستی بین آن ها ارتباط برقرار کنیم. برای مثال مدل User ما می تواند چندین نمونه از مدل Task داشته باشد (رابطه ی یک به چند با tasks داشته باشد)، این در حالی است که یک Task تنها به یک User انتساب داده می شود. تعریف یک ارتباط (relationship) بین مدل ها به ما اجازه می دهد با آسانی هر چه تمام تر در رابطه ی مدل ها به شکل زیر بچرخیم (با حلقه تک تک task های user را نمایش دهیم):
$user = App\User::find(1);
foreach ($user->tasks as $task) {
echo $task->name;
}
رابطه ی tasks
حال به ایجاد رابطه ی جدول tasks در مدل User می پردازیم. رابط های Eloquent داخل مدل در قالب متدهایی تعریف می شوند. Eloquent از انواع رابطه بین جداول پشتیبانی می کند. برای این مثال، داخل مدل User یک تابع به نام tasks تعریف می کنیم که متد hasMany (از توابعEloquent) را صدا می زند:
<?php
namespace App;
// Namespace Imports...
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
// Other Eloquent Properties...
/**
* Get all of the tasks for the user.
*/
public function tasks()
{
return $this->hasMany(Task::class);
}
}
تعریف رابطه ی user
در گام بعدی، یک رابطه ی user در مدل Task ایجاد می کنیم. همان طور که گفته شد رابطه ی مورد نظر را بایستی داخل مدل به صورت یک متد تعریف نمود. در این مثال برای نیل به هدف متد belongsTo از توابع Eloquent را بکار می بریم:
<?php
namespace App;
use App\User;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
/**
* Get the user that owns the task.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
پس از تعریف رابطه ها، می توان نسبت به ایجاد کنترلرهای اپلیکیشن اقدام نمود.
Routing (مسیردهی)
در نسخه ی ساده ی اپلیکیشن لیست نمایش وظایف (که در مبحث پیشین نوشتیم)، تمام منطق برنامه را با استفاده از Closure ها در فایلroutes.php پیاده سازی می کردیم. در بیشتر قسمت های این برنامه برای سازماندهی route ها از کنترلر استفاده می کنیم. کنترلرها به ما این امکان را می دهند تا منطق مدیریت درخواست HTTP را برای سازماندهی بهتر در چندین فایل پیاده سازی کنیم.
بازیابی و نمایش یک View
در این بخش یک route بیشتر نداریم که از یک Closure استفاده می کند و آن (route) ' / 'می باشد. این Closure صرفا یک صفحه برای کاربران میهمان اپلیکیشن نمایش می دهد. حال به پر کردن این routeمی پردازیم. از این route، یک قالب HTML رندر می کنیم (اجرا کرده و نمایش می دهیم) که صفحه ی "welcome" را شامل می شود:
در فرییم ورک لاراول تمامی قالب های HTML در پوشه ی resources/views نگهداری می شوند. برای بازیابی یکی از این قالب ها از route، تابع کمکی view را بکار می بریم:
Route::get('/', function () {
return view('welcome');
});
البته هنوز view را ایجاد نکرده ایم. در بخش بعدی به آن خواهیم پرداخت.
Authentication (احراز هویت)
یادآور می شویم که کاربران بایستی در این برنامه بتوانید حساب کاربری ایجاد کرده و به اکانت خود از طریق لاگین دسترسی داشته باشند. ایجاد یک لایه ی کامل authentication برای برنامه ی تحت وب می تواند پروسه ای بسیار ملال آور و کسل کننده باشد. اما از آنجایی که لایه ی احراز هویت یک نیاز اساسی برای تقریبا تمامی برنامه ی های تحت وب محسوب می شود، Laravel سعی می کند این فرآیند را برای برنامه نویس کاملا بی زحمت و آسان سازد.
اولین چیزی که متوجه آن می شوید، وجود کنترلر app/Http/Controllers/Auth/AuthController در اپلیکیشن Laravel می باشد. این کنترلر از یک ویژگی (trait) خاص به نام AuthenticatesAndRegistersUsers که حاوی کل منطق لازم برای ایجاد و احراز هویت کاربران می باشد، بهره می گیرد.
Route های Authentication
به نظر شما دیگر چه کاری مانده که باید انجام دهیم؟ همان طور که حدس می زنید قالب های ثب نام (registration) و ثبت ورود (login) را هنوز ایجاد نکرده و همچنین route هایی که به کنترلر authentication ارجاع دهند را تعریف نکرده ایم. بنابراین، ابتدا بایستی route های مورد نیاز را به فایلapp/Http/routes.php اضافه کنیم:
// Authentication Routes...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');
// Registration Routes...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');
View های Authentication
برای تعریف مکانیزم احراز هویت در برنامه ی تحت وب می بایست فایل های login.blade.php و register.blade.php را در پوشه یresources/views/auth ایجاد کنیم. طراحی و ظاهر (استایل دهی) این view ها اهمیت چندانی ندارد، با این حال وجود تعدادی فیلد ساده در آن ضروری است.
فایل register.blade.php بایستی یک فرم حاوی فیلدهای name، email، password، password_confirmation داشته باشد و یک درخواست POST به روت/auth/register ارسال کند.
فایل login.blade.php می بایست یک فرم حاوی فیلدهای email و password داشته باشد و نیز یک درخواست POST به روت /auth/loginارسال کند.
کنترلر Task
برای اینکه مقادیر task ها را بازیابی و ذخیره کنیم، لازم است یک کنترلر به نام TaskController با استفاده از Artisan CLI ایجاد کنیم. Artisan CLI کنترلر جدید را در پوشه ی app/Http/Controllers قرار می دهد:
php artisan make:controller TaskController --plain
پس از اینکه کنترلر ایجاد شد، تعدادی route در فایل app/Http/routes.php اضافه (stub) می کنیم که به کنترلر ارجاع می دهند:
Route::get('/tasks', 'TaskController@index');
Route::post('/task', 'TaskController@store');
Route::delete('/task/{task}', 'TaskController@destroy');
احراز هویت تمامی Route های Task
در این برنامه می خواهیم که تمامی task route ها کاربران را برای دسترسی ملزوم به احراز هویت کنند. به عبارتی دیگر کاربر می بایست از طریق لاگین وارد برنامه شود تا بتواند یک task جدید ایجاد کند. از این رو باید دسترسی به task route ها را به کاربرانی که کاملا تصدیق هویت شده اند محدود کنیم. لاراول با بهره گیری از middleware این پروسه را به معنای حقیقی آسان کرده است.
(در کنترلر) برای اینکه کاربران برای انجام تمامی عملیات ملزوم به احراز هویت شوند، می توانیم پارامتر 'auth' را به متد middleware ارسال کرده و سپس متد نام برده را از سازنده (constructor) کنترلر صدا بزنیم.
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TaskController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}
ساخت Layout و View برنامه
این برنامه تنها یک ویو دارد که این ویو حاوی یک فرم ویژه ی افزودن task جدید به برنامه و نیز یک لیست برای نمایش task های جاری به کاربر می باشد. برای اینکه بهتر بتوانید ویو را تجسم کنید، در زیر تصویری از نسخه ی تکمیل شده ی برنامه را برای شما نمایش داده ایم:
ایجاد Layout (طرح کلی برنامه)
تقریبا تمامی برنامه های تحت وب از یک layout برای تمامی صفحات خود استفاده می کنند. برای مثال برنامه ای که تصویر آن را در بالا مشاهده می کنید، یک نوار پیمایش در بالای صفحه دارد که بین تمامی صفحات آن به اشتراک گذاشته می شود (البته اگر این برنامه چندین صفحه داشت). لاراول با ارائه ی layout های Blade این امکان را به شما می دهد تا به راحتی این دست ویژگی ها را بین تمامی صفحات اپلیکیشن به اشتراک بگذارید.
همان طور که پیش تر هم ذکر شد، تمامی ویوهای لاراول در پوشه ی resources/views نگهداری می شوند. لازم به گفتن نیست که باید یکlayout view جدید در فایل resources/views/layouts/app.blade.php ایجاد کنیم. پسوند .blade.php به فریم ورک لاراول اعلان می کند که باید با استفاده از Blade templating engine ویو مورد نظر را رندر (اجراکرده و نمایش دهد). البته در صورت تمایل می توانید از قالب های ساده ی PHP استفاده کنید. اما از آنجایی که لاراول میانبرهایی برای نوشتن و طراحی قالب های کارآمد در اختیار شما قرار می دهد، اکیدا توصیه می کنیم از Blade استفاده نمایید.
کد فایل ویو app.blade.php می بایست مشابه نمونه ی زیر باشد:
// resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<title>Laravel Quickstart - Intermediate</title>
<!-- CSS And JavaScript -->
</head>
<body>
<div class="container">
<nav class="navbar navbar-default">
<!-- Navbar Contents -->
</nav>
</div>
@yield('content')
</body>
</html>
توجه خود را به دستور @yield('content') در layout حاضر جلب کنید. این تکه کد در واقع یک دستور (directive) از موتور قالب ساز Bladeمی باشد که تعیین می کند صفحات فرزندی که از layout جاری ارث بری می کنند (آن را extend می کنند)، در کجا می توانند محتویات خود را تزریق (inject) کنند. در بخش بعدی view فرزند را تعریف می کنیم که از این layout استفاده (ارث بری) کرده و محتویات اولیه آن را فراهم می کند (محتوای خود را در آن تزریق می کند).
تعریف View فرزند
در این بخش یک view تعریف می کنیم که علاوه بر جدولی که لیست کارهای جاری را نمایش می دهد، حاوی یک فرم برای ایجاد task جدید می باشد.این View را در مسیر resources/views/tasks/index.blade.php تعریف می کنیم که با متد index در کنترلر TaskController مرتبط و منطبق می باشد.
در زیر کدهای Bootstrap CSS را نادیده گرفته و به شرح کدهای لاراول می پردازیم.
به یاد داشته باشید که منبع کامل این پروژه را می توانید از GitHub دانلود کنید:
// resources/views/tasks/index.blade.php
@extends('layouts.app')
@section('content')
<!-- Bootstrap Boilerplate... -->
<div class="panel-body">
<!-- Display Validation Errors -->
@include('common.errors')
<!-- New Task Form -->
<form action="/task" method="POST" class="form-horizontal">
{{ csrf_field() }}
<!-- Task Name -->
<div class="form-group">
<label for="task-name" class="col-sm-3 control-label">Task</label>
<div class="col-sm-6">
<input type="text" name="name" id="task-name" class="form-control">
</div>
</div>
<!-- Add Task Button -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-default">
<i class="fa fa-plus"></i> Add Task
</button>
</div>
</div>
</form>
</div>
<!-- TODO: Current Tasks -->
@endsection
چند نکته ی آموزشی
پیش از پرداختن به ادامه ی مبحث، توضیح مختصری درباره ی این template ارائه می دهیم.
دستور (directive) @extends به Blade اعلان می کند که layout مورد استفاده ی برنامه ی ما، از قبل در resources/views/layouts/app.blade.php تعریف شده است. تمامی کدهایی بین دو بخش @section('content') و@endsection مشاهده می کنید، در مکان قرارگیری دستور @yield('content') ، داخل طرح کلی (layout) app.blade.php تزریق می شود.
Layout و view مورد نیاز برنامه ی خود را ایجاد کردیم. اکنون زمان آن فرارسیده که این view را از متد index کنترلر TaskController بازیابی کنیم:
/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index');
}
حال می توانیم کد مورد نظر را به روت متد کنترلر POST /task برای مدیریت input فرم و افزودن یک task جدید به پایگاه داده اضافه کنیم.
دستور @include('common.errors') قالب مقیم در مسیر resources/views/common/errors.blade.php را بارگذاری می کند. البته این template را هنوز ایجاد نکردیم و به زودی نسبت به ایجاد آن را اقدام خواهیم کرد.
اضافه کردن Task های جدید به لیست
Validation (اعتبارسنجی)
پس از ایجاد فرم در فایل ویو، باید کدی را به متد TaskController@store اضافه کنیم که ورودی فرم را تصدیق اعتبار کرده و سپس یک taskجدید ایجاد کند. اول ورودی را اعتبارسنجی می کنیم.
در این فرم، فیلد name را با required مقداردهی می کنیم تا وارد کردن مقدار در این فیلد توسط کاربر الزامی گردد و سپس حداکثر تعداد کاراکترهای مجاز آن را با تنظیم مقدار مشخصه ی max بر روی 255 طوری پیکربندی می کنیم که کاربر اجازه ی وارد کردن بیش از 255 کاراکتر را نداشته باشد. در صورتی که عملیات اعتبارسنجی با شکست مواجه شد، کاربر به آدرس URL " / " (بازگشت داده) redirect می شود و همچنین ورودی قبلی به همراه خطاها در session افزوده و برای نمایش در دسترس قرار می گیرند (flash می شوند). اضافه کردن و نمایش دادن (flash) ورودی در session این امکان را برای ما فراهم می کند تا ورودی کاربر را حتی در شرایطی که خطای اعتبارسنجی رخ می دهد، نگه داشته و رصد کنیم:
/**
* Create a new task.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
]);
// Create The Task...
}
اگر مبحث قبلی را مطالع کرده باشید، متوجه می شوید که کد اعتبارسنجی آن با نمونه ی حاضر در این مبحث کاملا متفاوت است. از آنجایی که داخل کنترلر هستیم، می توانیم به راحتی از خصیصه ی ValidatesRequests استفاده کنیم. خصیصه ی نام برده در کنترلر پایه ی فریم ورک لاراول تعبیه شده است. این خصیصه یک متد ساده ی validate را فراهم می کند که یک request (درخواست) به همراه آرایه ای از validation rule ها (قوانین/معیار اعتبارسنجی) را به عنوان آرگومان ورودی می پذیرد.
حتی لازم نیست به صورت دستی بررسی کنیم آیا اعتبارسنجی با شکست مواجه شده یا redirection (بازگشت کاربر به صفحه ی قبلی) را به صورت دستی انجام دهید. اگر validation با توجه به قوانین و معیار مشخص شده ی اعتبارسنجی با شکست مواجه شد، کاربر به صورت خودکار به همان جایی از آن آمده بازگشت داده (redirect) می شود و خطاهای اعتبارسنجی هم به صورت اتوماتیک داخل session قرار داده می شوند.
متغیر $errors
یادآور می شویم که برای نمایش (render) دادن خطاهای اعتبارسنجی فرم، در view از دستور@include('common.errors') استفاده می کنیم. پارامتر ورودی common.errors به ما این اجازه را می دهد تا به راحتی خطاهای اعتبارسنجی را با فرمتی یکسان در تمامی صفحات برنامه ی خود نمایش دهیم. حال به تعریف محتویات این view می پردازیم:
// resources/views/common/errors.blade.php
@if (count($errors) > 0)
<!-- Form Error List -->
<div class="alert alert-danger">
<strong>Whoops! Something went wrong!</strong>
<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
متغیر$errors در تمامی view های لاراول در دسترس می باشد. در صورتی که هیچ خطای اعتبارسنجی (validation error) وجود نداشته باشد، این متغیر تنها یک نمونه ی تهی از ViewErrorBag خواهد بود.
ایجاد Task
پس از اجرا و اداره ی اعتبارسنجی ورودی، با پر کردن route یک task جدید ایجاد می کنیم. بعد از اینکه task را ایجاد کردیم، کاربر را به آدرسURL " / " بازگشت می دهیم (redirect می کنیم). برای تعریف task جدید در لیست، از قابلیت قدرتمند رابطه ها در Eloquent بهره می گیریم.
بیشتر رابطه ها در لاراول متد create را در دسترس قرار می دهند، این متد آرایه ای از attribute ها را به عنوان پارامتر ورودی می گیرد و مقدار کلید خارجی (foreign key) را در مدل مربوطه، پیش از ذخیره شدن کردن آن در پایگاه داده، تنظیم می کند. در این مثال، متد create به صورت خودکار پراپرتی (ستون) user_id مربوط به task موردنظر را با ID کاربر تصدیق هویت شده جاری تنظیم و مقداردهی می کند. ما از طریق$request->user() به ID کاربر احراز هویت شده دست پیدا می کنیم:
/**
* Create a new task.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
]);
$request->user()->tasks()->create([
'name' => $request->name,
]);
return redirect('/tasks');
}
اکنون می توانیم با موفقیت تمام، task جدید ایجاد می کنیم. در بخش بعدی با افزودن یک لیست برای نمایش task ها، ویو خود را بسط می دهیم.
نمایش Task های جاری
ابتدا به ویرایش متد TaskController@index می پردازیم تا از این طریق بتوانیم تمامی task های جاری را به ویو ارسال و در آن به نمایش بگذاریم. تابع view دو آرگومان می پذیرد که آرگومان دوم آن آرایه ای از داده ها است. آرایه ی ارسالی به تابع نام برده در اختیار بخش view برنامه قرار می گیرد و هر کلید موجود در آن داخل ویو به یک متغیر تبدیل می شود. برای مثال می توانیم عملیات زیر را انجام دهیم:
/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
$tasks = Task::where('user_id', $request->user()->id)->get();
return view('tasks.index', [
'tasks' => $tasks,
]);
}
اکنون با بهره گیری از قابلیت های dependency injection لاراول، یک TaskRepository در TaskController تزریق کرده که بعده ها برای لایه ی data access (دسترسی به داده) از آن استفاده می کنیم.
Dependency Injection
service container لاراول یکی از قدرتمندترین امکانات کل فریم ورک تلقی می شود. پس از مطالعه ی این آموزش، حتما مستندات container را به طور کامل بخوانید.
ایجاد Repository
همان طور که قبلا گفتیم، می خواهیم یک TaskRepository تعریف کنیم که دربردارنده ی منطق data access مدل Task باشد. این کار به خصوص زمانی کمک قابل توجهی محسوب می شود که اپلیکیشن نسبت به قبل توسعه پیدا کرده و شما بایستی برخی از کوئری های Eloquent را در سراسر برنامه به اشتراک بگذارید.
بنابراین یک پوشه ی اصلی app/Repositories ایجاد کرده و سپس یک کلاس TaskRepository به آن اضافه می کنیم. به خاطر داشته باشید که تمامی پوشه های app لاراول به وسیله ی استاندارد PSR-4 auto-loading خودکار بارگذاری می شوند. از اینرو شما آزادید هر تعداد پوشه که لازم دارید ایجاد نمایید:
<?php
namespace App\Repositories;
use App\User;
use App\Task;
class TaskRepository
{
/**
* Get all of the tasks for a given user.
*
* @param User $user
* @return Collection
*/
public function forUser(User $user)
{
return Task::where('user_id', $user->id)
->orderBy('created_at', 'asc')
->get();
}
}
تزریق Repository
پس از تعریف repository، می توانیم به نوع داده ای آن در constructor کنترلر TaskController اشاره کرده و آن را در روت index خود بکار ببریم (repository را در متد سازنده ی کنترلر TaskController، type-hint کرده و سپس در روت index بکار ببریم. type-hint در پی اچ پی عبارت است از مشخص کردن نوع داده ای مورد انتظار آرگومان در تعریف تابع). از آنجایی که لاراول از container برای تجزیه (resolve) کردن تمامی کنترلرها بهره می گیرد، تمامی Dependency ها به صورت خودکار در نمونه ی کنترلر تزریق می شوند.
namespace App\Http\Controllers;
use App\Task;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Repositories\TaskRepository;
class TaskController extends Controller
{
/**
* The task repository instance.
*
* @var TaskRepository
*/
protected $tasks;
/**
* Create a new controller instance.
*
* @param TaskRepository $tasks
* @return void
*/
public function __construct(TaskRepository $tasks)
{
$this->middleware('auth');
$this->tasks = $tasks;
}
/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index', [
'tasks' => $this->tasks->forUser($request->user()),
]);
}
}
نمایش دادن Taskها
پس از ارسال داده ها می توانیم به راحتی در task های خود داخل فایل ویو tasks/index.blade.php حلقه زده و آن ها را در جدول فهرست کنیم. دستور@foreach از موتور Blade به ما این امکان را می دهد تا حلقه های با کد مختصر نوشته که با سرعت بی نظیر به کد ساده و در عین حال بهینه یPHP ترجمه (compile) می شود:
@extends('layouts.app')
@section('content')
<!-- Create Task Form... -->
<!-- Current Tasks -->
@if (count($tasks) > 0)
<div class="panel panel-default">
<div class="panel-heading">
Current Tasks
</div>
<div class="panel-body">
<table class="table table-striped task-table">
<!-- Table Headings -->
<thead>
<th>Task</th>
<th> </th>
</thead>
<!-- Table Body -->
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- Task Name -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<td>
<!-- TODO: Delete Button -->
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@endsection
با کدهایی که تا همین جا نوشته ایم برنامه ی ما ظاهرا کامل به نظر می رسد. اما برای حذف task های جاری که به انجام رسیده اند از لیست هیچ سازوکاری وجود ندارد. در بخش بعدی این مکانیزم را به برنامه اضافه می کنیم.
حذف Task ها
افزودن دکمه ی Delete
یک متن به نام "TODO" در کد برنامه ی خود قرار دادیم که کد دکمه ی Delete بایستی در آنجا به برنامه اضافه شود. در این بخش به ازای هر سطر در لیست task یک دکمه ی Delete در فایل ویوtasks/index.blade.php اضافه می کنیم. سپس یک فرم با تنها یک دکمه به ازای هر task در لیست ایجاد می کنیم. با کلیک بر روی دکمه ی Delete، درخواست DELETE /task به اپلیکیشن ارسال می شود که متدTaskController@destroy را فعال کرده و باعث اجرای آن می شود:
<tr>
<!-- Task Name -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<!-- Delete Button -->
<td>
<form action="/task/{{ $task->id }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button>Delete Task</button>
</form>
</td>
</tr>
نکته ای در خصوص method spoofing (جعل متدهای ارسال فرم)
همان طور که مشاهده می کنید خصیصه ی method در کد بالا با Post مقداردهی شده است، با این حال ما در برنامه ی خود از طریقRoute::delete به درخواست ارسالی پاسخ می دهیم. در فرم های HTML تنها از روش ارسال داده ی Post و Get پشتیبانی می شود، از این رو می بایست از یک طریقی درخواست DELETE از فرم را جعل (spoof) کنیم.
برای جعل کردن درخواست DELETE می بایست نتایج تابع method_field('DELETE') را در فرم جاری خود قرار دهیم. این تابع یک ورودی که نوعش hidden می باشد، ایجاد می کند. لاراول این خروجی را شناسایی کرده و سپس با استفاده از آن، متد درخواست HTTP را بازنویسی می کند. فیلد ایجاد شده بدین شکل می باشد:
Bind کردن مدل
اکنون می توانیم متد destroy را در TaskController تعریف کنیم. اما قبلش بهتر است تعریف Route را کد بازبینی کنیم:
Route::delete('/task/{task}', 'TaskController@destroy');
بدون افزودن هیچ کد اضافه بر سازمانی، لاراول ID (شناسه) Task مربوطه را در متد TaskController@destroy تزریق می کند:
/**
* Destroy the given task.
*
* @param Request $request
* @param string $taskId
* @return Response
*/
public function destroy(Request $request, $taskId)
{
//
}
با این وجود اولین چیزی که می بایست در این متد انجام دهیم، بازیابی نمونه ی Task از پایگاه داده با استفاده از ID مربوطه می باشد. بهتر نبود لاراول نمونه ی Task ای که با ID مورد نظر منطبق است را خود تزریق کند؟ اکنون اقدام به نوشتن آن می کنیم.
در متد boot فایل app/Providers/RouteServiceProvider.php، کد زیر را درج می کنیم:
$router->model('task', 'App\Task');
این یک خط کد به لاراول اعلان می کند که هر زمان {task} را در تعریف Route مشاهده کرد، بایستی آن مدل Task که با ID مورد نظر منطبق و متناظر هست را بازیابی کند. حال می توانیم متد destroy را به صورت زیر تعریف کنیم:
/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
//
}
Authorization (مجوزدهی)
حالا یک نمونه Task داریم که به درون متد destroy تزریق شده است. با این وجود هیچ تضمینی نیست که کاربر تصدیق هویت شده مالک واقعی taskمورد نظر باشد. به عنوان مثال، ممکن است با ارسال یک task ID تصادفی به آدرس URL /tasks/{task} ، یک درخواست مخرب و ساختگی ایجاد و به منظور حذف کردن task متعلق به کاربر دیگر مورد استفاده قرار گیرد. بنابراین بایستی از قابلیت های تخصیص مجوز لاراول بهره گرفته و اطمینان حاصل نمایید کاربر تصدیق هویت شده مالک واقعی آن نمونه ی Task است که در route تزریق شده.
ایجاد یک Policy (سیاست تخصیص مجوز)
لاراول با بهره گیری از "policy ها (سیاست های تخصیص مجوز)" منطق authorization را در قالب کلاس های ساده و کوچک سازماندهی می کنند. به طور کلی، هر policyمتعلق و مربوط به یک مدل می باشد. در اینجا با بهره گیری از Artisan CLI یک TaskPolicy ایجاد می کنیم. این فایل در مسیر app/Policies/TaskPolicy.php جایگذاری می شود:
php artisan make:policy TaskPolicy
در گام بعدی یک متد destroy به policy اضافه می کنیم. این متد یک نمونه ی User و نیز یک نمونه ی Task را به عنوان آرگومان ورودی می پذیرد. متد نام برده فقط بایستی بررسی کند آیا ID کاربر با user_id در task منطبق است یا خیر. در واقع تمامی متدهای policy به عنوان خروجی یا مقدارtrue را برمی گرداند و یا مقدار false را.
<?php
namespace App\Policies;
use App\User;
use App\Task;
use Illuminate\Auth\Access\HandlesAuthorization;
class TaskPolicy
{
use HandlesAuthorization;
/**
* Determine if the given user can delete the given task.
*
* @param User $user
* @param Task $task
* @return bool
*/
public function destroy(User $user, Task $task)
{
return $user->id === $task->user_id;
}
}
در پایان باید مدل Task را با TaskPolicy متصل و وابسته کنیم. این کار را با افزودن یک خط کد در پراپرتی $policies داخل فایلapp/Providers/AuthServiceProvider.php انجام می دهیم. این خط کد به لاراول اعلان می کند هر زمان که ما سعی بر تخصیص مجوز برای انجام عملیات خاص در نمونه ی Task داریم، کدام Policy بایستی مورد استفاده قرار گیرد:
/** * The policy mappings for the application. * * @var array */ protected $policies = [ Task::class => TaskPolicy::class, ];
تخصیص مجوز به عملیات
حال که Policy را نوشته و تنظیم کردیم، می خواهیم آن را متد destroy خود بکار ببریم. تمامی کنترلرهای فریم ورک لاراول می توانند متدauthorize را فراخوانی کنند. این متد به واسطه ی خصیصه (trait) AuthorizesRequest در دسترس ما قرار داشته می شود:
/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
$this->authorize('destroy', $task);
// Delete The Task...
}
ابتدا به شرح کد فراخوانی این متد می پردازیم. اولین آرگومان ارسالی به متد authorize، اسم متد policy است که می خواهیم صدا بزنیم. آرگومان دوم نمونه ی مدل مورد نظر می باشد. اگر به خاطر داشته باشیم، به لاراول اعلان کردیم که مدل Task به TaskPolicy وابسته و مرتبط می باشد. از این رو فریم ورک نام برده دقیقا اطلاع دارد که متد destroy را بر روی کدام policy می بایست فراخوانی و اجرا کند. لازم به ذکر است که user جاری خود به صورت اتوماتیک به متد Policy فرستاده می شود، بنابراین نیازی نیست user را به صورت دستی به اینجا ارسال کنیم.
چنانچه عملیات مورد نظر با موفقیت مجوزدهی شد، کد با رویه ی عادی به اجرا ادامه می دهد. اما اگر به انجام عملیات مجوز داده نشد (متد destroy ازPolicy مقدار false را به عنوان خروجی بازگرداند)، یک خطا (exception) با کد 403 رخ می دهد و در پی آن صفحه ی خطا برای کاربر به نمایش درمی آید.
حذف کردن Task
در پایان منطقی به متد destroy اضافه می کنیم که task مورد نظر را حذف کند. برای نیل به این هدف متد delete از توابع Eloquent را بکار می بریم که نمونه ی مدل را به کلی از پایگاه داده حذف می کند. پس از اینکه سطر مورد نظر پاک شد، کاربر را به آدرس URL /tasks بازگشت می دهیم (redirect می کنیم).
/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
$this->authorize('destroy', $task);
$task->delete();
return redirect('/tasks');
}