إضافة روابط التصفح
قبل أن نفعل أي شيء، سنقوم بإضافة روابط create/edit/delete/back إلى صفحات المشاريع والمهام:
<!-- /resources/views/projects/index.blade.php -->
@extends('app')
@section('content')
<h2>Projects</h2>
@if ( !$projects->count() )
You have no projects
@else
<ul>
@foreach( $projects as $project )
<li>
{!! Form::open(array('class' => 'form-inline', 'method' => 'DELETE', 'route' => array('projects.destroy', $project->slug))) !!}
<a href="{{ route('projects.show', $project->slug) }}">{{ $project->name }}</a>
(
{!! link_to_route('projects.edit', 'Edit', array($project->slug), array('class' => 'btn btn-info')) !!},
{!! Form::submit('Delete', array('class' => 'btn btn-danger')) !!}
)
{!! Form::close() !!}
</li>
@endforeach
</ul>
@endif
<p>
{!! link_to_route('projects.create', 'Create Project') !!}
</p>
@endsection
<!-- /resources/views/projects/show.blade.php -->
@extends('app')
@section('content')
<h2>{{ $project->name }}</h2>
@if ( !$project->tasks->count() )
Your project has no tasks.
@else
<ul>
@foreach( $project->tasks as $task )
<li>
{!! Form::open(array('class' => 'form-inline', 'method' => 'DELETE', 'route' => array('projects.tasks.destroy', $project->slug, $task->slug))) !!}
<a href="{{ route('projects.tasks.show', [$project->slug, $task->slug]) }}">{{ $task->name }}</a>
(
{!! link_to_route('projects.tasks.edit', 'Edit', array($project->slug, $task->slug), array('class' => 'btn btn-info')) !!},
{!! Form::submit('Delete', array('class' => 'btn btn-danger')) !!}
)
{!! Form::close() !!}
</li>
@endforeach
</ul>
@endif
<p>
{!! link_to_route('projects.index', 'Back to Projects') !!} |
{!! link_to_route('projects.tasks.create', 'Create Task', $project->slug) !!}
</p>
@endsection
هذه الشفرة البرمجية مفهومة، الصعوبة تكمن في رابط الحذف، متحكِّمات الموارد تتطلب إرسال وظيفة HTTP DELETE، وهذا لا يمكن أن يتم مع رابط عادي، لذلك يتطلب نموذج يتم تقديمه إلى طريق route محدد. للمزيد من المعلومات قُم بزيارة إجراءات يتم التحكم بها عن طريق متحكِّمات الموارد.
إنشاء صفحات الإضافة والتعديل
بعد الانتهاء من صفحات عرض قائمة المشاريع، نحتاج إلى إمكانية إضافة وحذف المشاريع والمهام. استمارات الإضافة والحذف ستكون متشابهة كثيرا، لذلك بدلا من تكرارهم، سنقوم بوراثتهم من نموذج واحد.
سوف نبدأ مع عروض create/edit للمشروع:
<!-- /resources/views/projects/create.blade.php -->
@extends('app')
@section('content')
<h2>Create Project</h2>
{!! Form::model(new App\Project, ['route' => ['projects.store']]) !!}
@include('projects/partials/_form', ['submit_text' => 'Create Project'])
{!! Form::close() !!}
@endsection
<!-- /resources/views/projects/edit.blade.php -->
@extends('app')
@section('content')
<h2>Edit Project</h2>
{!! Form::model($project, ['method' => 'PATCH', 'route' => ['projects.update', $project->slug]]) !!}
@include('projects/partials/_form', ['submit_text' => 'Edit Project'])
{!! Form::close() !!}
@endsection
قُم بعمل نفس الشيء مع المهام لكن لا تنسَ تحديث الطرق (routes):
<!-- /resources/views/tasks/create.blade.php -->
@extends('app')
@section('content')
<h2>Create Task for Project "{{ $project->name }}"</h2>
{!! Form::model(new App\Task, ['route' => ['projects.tasks.store', $project->slug], 'class'=>'']) !!}
@include('tasks/partials/_form', ['submit_text' => 'Create Task'])
{!! Form::close() !!}
@endsection
<!-- /resources/views/tasks/edit.blade.php -->
@extends('app')
@section('content')
<h2>Edit Task "{{ $task->name }}"</h2>
{!! Form::model($task, ['method' => 'PATCH', 'route' => ['projects.tasks.update', $project->slug, $task->slug]]) !!}
@include('tasks/partials/_form', ['submit_text' => 'Edit Task'])
{!! Form::close() !!}
@endsection
الآن توجد عدة مفاهيم جديدة:
إضافة جزئية
الاستمارة تتطلب عدة وسوم، وبدلا من ذلك سنقوم بتقسيمها من جزئية _form ونضعها مباشرة في العرض، استمارة Add هي من نوع POST ترسل البيانات إلى طريق projects.store واستمارة Edit هي من نوع PATCH وترسل البيانات إلى projects.update. قد يبدوا هذا معقدا ومبهم لكن هذه هي الطريقة التي تعمل بها المتحكِّمات.
لاحظ أننا استخدمنا ()Form::model، وتسمى هذه نموذج الاستمارة الملزمة و ليس لدى هذه أهمية كبيرة الآن، فهي تُستخدم للتعبئة التلقائية وسوف نحتاجها في وقت لاحقا عندما نضيف الحقول
باستخدام ()Form::input.
الحماية من CSRF:
مساعدي الاستمارة (Form helpers) توفر العديد من الوظائف مجانا، فإذا ذهبت إلى projects/create/ ورأيت مصدر الصفحة ستجد شيئا مشابها لهذا:
<input name="_token" type="hidden" value="Y8uOo7SeD5tQZExezDf5a7UwiYR4P6qIHEUKJNxI">
</form>
هل ترى حقل token_ ؟ هذا هو الرمز المميز لـ CSRF مُنشئ تِلقائيا بواسطة استدعاء {{ ()Form::model }} الذي يمنع ثغرة CSRF. يكفي أن نقول أن هذا شيئ مفيد وأننا لم نقم بأي شيئ للحصول عليه.
إنشاء استمارة التعديل
نحن بحاجة إلى ترميز (markup) الاستمارة للمشاريع والمهام، وبفضل نموذج الاستمارة الملزمة يمكننا فقط استخدام مساعدي الاستمارة في
Laravel لإخراج كافة الحقول التي نحتاجها.
<!-- /resources/views/projects/partials/_form.blade.php -->
<div class="form-group">
{!! Form::label('name', 'Name:') !!}
{!! Form::text('name') !!}
</div>
<div class="form-group">
{!! Form::label('slug', 'Slug:') !!}
{!! Form::text('slug') !!}
</div>
<div class="form-group">
{!! Form::submit($submit_text, ['class'=>'btn primary']) !!}
</div>
<!-- /resources/views/tasks/partials/_form.blade.php -->
<div class="form-group">
{!! Form::label('name', 'Name:') !!}
{!! Form::text('name') !!}
</div>
<div class="form-group">
{!! Form::label('slug', 'Slug:') !!}
{!! Form::text('slug') !!}
</div>
<div class="form-group">
{!! Form::label('completed', 'Completed:') !!}
{!! Form::checkbox('completed') !!}
</div>
<div class="form-group">
{!! Form::label('description', 'Description:') !!}
{!! Form::textarea('description') !!}
</div>
<div class="form-group">
{!! Form::submit($submit_text) !!}
</div>
هذا كل شيئ، سوف تستطيع الأن التصفح في صفحات الإضافة والتعديل، الأمر كان سهلا للغاية.
جعل الاستمارات تعمل
لدينا استمارات الإضافة والتعديل للمشروع و المهام، بالإضافة إلى استمارة مُضللة لحذفهم، الآن يجب علينا أن نجعلهم يعملون لأداء مهامهم.
أولا، أضف هذا في أعلى المتحكِّمات:
use Input;
use Redirect;
والآن لوظائف store و update و destroy:
// ProjectsController
public function store()
{
$input = Input::all();
Project::create( $input );
return Redirect::route('projects.index')
->with('message', 'Project created');
}
public function update(Project $project)
{
$input = array_except(Input::all(), '_method');
$project->update($input);
return Redirect::route('projects.show', $project->slug)
->with('message', 'Project updated.');
}
public function destroy(Project $project)
{
$project->delete();
return Redirect::route('projects.index')->with('message', 'Project deleted.');
}
// TasksController
public function store(Project $project)
{
$input = Input::all();
$input['project_id'] = $project->id;
Task::create( $input );
return Redirect::route('projects.show', $project->slug)
->with('message', 'Task created.');
}
public function update(Project $project, Task $task)
{
$input = array_except(Input::all(), '_method');
$task->update($input);
return Redirect::route('projects.tasks.show', [$project->slug, $task->slug])
->with('message', 'Task updated.');
}
public function destroy(Project $project, Task $task)
{
$task->delete();
return Redirect::route('projects.show', $project->slug)->with('message', 'Task deleted.');
}عندما تقوم بتمرير مصفوفة بيانات إلى ()Model::create أو ()Model::update من المتحكِّمات بدل من وضع حقل واحد في نفس الوقت، ولإصلاح هذا، قم ببساطة بإضافة خاصية guarded إلى كل نموذج.
class Project extends Model {
protected $guarded = [];
class Task extends Model {
protected $guarded = [];
رسائل الفلاش
سوف تلاحظ من الشفرة السابقة أننا استخدمنا دالة ()with، وهذه الدالة تقوم بتمرير متغير فلاش (يستخدم لمرة واحدة) للجلسة session والتي يمكن قراءتها عند تحميل الصفحة التالية.
والآن نحتاج إلى التأكد من تلك الرسالة وعرضها على المستخدم. قُم بفتح resources/views/app.blade.php/ وأضف التالي:
...
<div class="content">
@if (Session::has('message'))
<div class="flash alert-info">
<p>{{ Session::get('message') }}</p>
</div>
@endif
@yield('content')
</div>
...
حاول
إنشاء مشروع، هذه الرسالة ستظهر مهما حدّثت الصفحة وستذهب بعد فترة.