Tìm hiểu Laravel từ số 0 (P7)
Bài đăng này đã không được cập nhật trong 3 năm
Tiếp nối phần 6 đã nói về Form validation, Formrequest, Error message, Scope và View Partial, trong phần 7 này tôi sẽ tiếp tục đề cập một số vấn đề sau :
- Helper
- Flash message
- Named route
- Restful resource controller
Helper
Lần này tôi sẽ thêm vào chức năng xoá article và thông qua đó cũng sẽ có nói luôn về Helper.
Cái đầu tiên là cần thêm vào những route sau với phương thức delete
trog routes.php
Route::delete('articles/{id}', 'ArticlesController@destroy');
Kế đến sẽ đi thêm vào Controller phương thức destroy
với nội dung nhận vào tham số $id
vừa khai báo trong route.
// app/Http/Controllers/ArticlesController.php
public function destroy($id) {
$article = Article::findOrFail($id);
$article->delete();
return redirect('articles');
}
Để gọi được phương thức xoá ta cần đặt nút xoá trên View, tôi ở đây sẽ dùng phương thức DELETE của Form.
// resoruces/views/articles/show.blade.php
@extends('layout')
@section('content')
// Giản lược code ở đây
{!! Form::open(['method' => 'DELETE', 'url' => ['articles', $article->id]]) !!}
{!! Form::submit('Delete', ['class' => 'btn btn-danger']) !!}
{!! Form::close() !!}
@stop
Đến đây tôi muốn đề cập đến khái niệm HELPER, mục đích dùng nó là sẽ refactor chia lại phần tạo Form mà dùng phương thức DELETE ở trên.
Tôi sẽ thêm file helper.php
với nội dung chưa một hàm là delete_form
sinh ra thẻ Form rồi trả về.
<?php
// app/Http/helper.php
function delete_form($urlParams, $label = 'Delete')
{
$form = Form::open(['method' => 'DELETE', 'url' => $urlParams]);
$form .= Form::submit($label, ['class' => 'btn btn-danger']);
$form .= Form::close();
return $form;
}
Để có hiệu lực tôi sẽ cần chỉnh file composer.json
để Laravel thực hiện Autoload helper.php
.
{
...
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App": "app/"
},
"files": ["app/Http/helper.php"] <-- thêm đoạn này
},
...
}
Nhưng sau đó phải thực hiện command dưới đây thì việc autoload mới thực sự được thiết lập :
composer dump-autoload
Lại tiếp tục chỉnh show.blade.php
. Thay vì đoạn code tạo Form được nhét vào Blade thì ta chỉ cần 1 dòng ngắn ngọn là code đã sáng sủa hơn rất nhiều. Ngoài ra thì bằng việc đưa ra Helper, bất kì chỗ nào cần Delete thì bạn đều đã có thể tái sử dụng được rồi. Cool !
// resoruces/views/articles/show.blade.php
@extends('layout')
@section('content')
// Giản lược code ở đây
{!! delete_form(['articles', $article->id]) !!}
@stop
Flash message
Xong phần xoá, bạn hãy thử chạy xem. Kết quả bạn sẽ được chuyển về trang list nhưng chẳng hề có thông báo nào được hiển thị ra, như thế đối với code normal thì bạn thấy thiếu thiếu mất messageđể cho user biết xử lý là có thành công hay không. Giờ tiếp theo tôi sẽ đi đến dùng Flash message để làm việc này.
Đầu tiên sẽ phải sửa phương thức destroy
của ArticlesController.php
một chút :
// app/Http/Controllers/ArticlesController.php
...
public function destroy($id) {
$article = Article::findOrFail($id);
$article->delete();
\Session::flash('flash_message', 'Deleted article successfully.'); // dòng thêm vào
return redirect('articles');
}
...
Ở đây tôi dùng đến facade là \Session
để access vào session và bằng phương thức flash
tôi thêm vào nội dung báo ra màn hình. Khái niệm Flash mang ý nghĩa nó chỉ có hiệu lực một thời điểm nhất thời và chỉ có hiệu lực với request tiếp sau.
Đương nhiên cần đến View thì dòng thông báo đó mới có thể ra đến màn hình cho user, tôi sẽ đi sửa Layout. Đơn giản chỉ cần thêm vào code check xem trong Session có key nào tên là flash_messgae
thì sẽ hiển thị nó ra. Dùng thêm class của Boostrap sẽ làm cho message có sắc hơn
// resources/views/layout.blade.php
...
<body>
<div class="container">
{{-- Hiển thị Flash message --}}
@if (Session::has('flash_message'))
<div class="alert alert-success">{{ Session::get('flash_message') }}</div>
@endif
// Giản lược code
</div>
...
</body>
</html>
Named route
Cái tên đã nói lên ý nghĩa của nó - route đã được đặt tên. Từ trước tới giờ khi mà chỉ định URL ở trên Controller hay View thì tôi viết cả path vào source code. Nhưng mà nếu làm như này suốt sẽ không ổn bởi vì một ngày đẹp trời path đó thay đổi bạn sẽ phải thay đổi cho rất nhiều nơi dùng nó. Do đó một cách đơn giản hiệu quả là đặt tên cho nó, nếu có sửa thì cũng chỉ cần thay đổi nội dung của path là xong.
Cần phải sửa file routes.php
giống như sau :
// app/Http/routes.php
...
// Đây là cách viết Route cũ
//Route::get('articles', 'ArticlesController@index');
//Route::get('articles/create', 'ArticlesController@create');
//Route::get('articles/{id}', 'ArticlesController@show');
//Route::post('articles', 'ArticlesController@store');
//Route::get('articles/{id}/edit', 'ArticlesController@edit');
//Route::patch('articles/{id}', 'ArticlesController@update');
//Route::delete('articles/{id}', 'ArticlesController@destroy');
// Còn đây tôi đã chuyển sang đặt tên cho các Route
Route::get('articles', ['as' => 'articles.index', 'uses' => 'ArticlesController@index']);
Route::get('articles/create', ['as' => 'articles.create', 'uses' => 'ArticlesController@create']);
Route::get('articles/{id}', ['as' => 'articles.show', 'uses' => 'ArticlesController@show']);
Route::post('articles', ['as' => 'articles.store', 'uses' => 'ArticlesController@store']);
Route::get('articles/{id}/edit', ['as' => 'articles.edit', 'uses' => 'ArticlesController@edit']);
Route::patch('articles/{id}', ['as' => 'articles.update', 'uses' => 'ArticlesController@update']);
Route::delete('articles/{id}', ['as' => 'articles.destroy', 'uses' => 'ArticlesController@destroy']);
Bạn thấy code ở đây dài hơn nhưng thực sự nó rất tiện ích. Bằng việc dùng mảng rồi chỉ định khoá as
là bạn sẽ đặt tên cho Route của mình, còn uses
sẽ là phần chỉ ra action (gồm Controller và phương thức cần gọi). Cách sử dụng như dưới :
// app/Http/Controllers/ArticlesController.php
...
public function store(ArticleRequest $request) {
Article::create($request->all());
Session::flash('flash_message', 'Add article successfully.');
//cũ : return redirect('articles');
return redirect()->route('articles.index');
}
...
public function update($id, ArticleRequest $request) {
$article = Article::findOrFail($id);
$article->update($request->all());
Session::flash('flash_message', 'Update article successfully.');
// cũ : return redirect(url('articles', [$article->id]));
return redirect()->route('articles.show', [$article->id]);
}
// resources/views/articles/create.blade.php
@extends('layout')
@section('content')
// Giản lược code
{{-- Form::open(['url' => 'articles']) --}}
{!! Form::open(['route' => 'articles.store']) !!}
@include('articles.form', ['published_at' => date('Y-m-d'), 'submitButton' => 'Add Article'])
{!! Form::close() !!}
@stop
Restful Resource Controller
Cho đến bài này thì tất cả những cái cơ bản CRUD tức tạo mới - Create, đọc ra - Read, cập nhật - Update và xoá - Delete đã được thực hiện. Thực tế là tôi đã định nghĩa Route hay Controller dựa vào REST khi mà thực hiện CRUD. Phần cuối bài này tôi sẽ nói về quan hệ của Route hay Controller trong Laravel với REST.
Vậy REST là gì ?
Tôi cũng không tự tin lắm để giải thích chi tiết được nhưng theo tôi hiểu nó là một khái niệm thiết kế của API chỉ rõ thao tác và đối tượng thao tác đến ứng với URI và phương thức Request của HTTP. Và người ta gọi thứ mà được thiết kế dựa trên khái niệm REST là RESTful.
Trong Laravel nhìn chung nếu mà hiểu với pattern dưới chắc sẽ không có vấn đề gì.
Trường hợp đối tượng thao tác đến là bài viết (Article) :
METHOD | URI | ACTION | Nội dung thao tác |
---|---|---|---|
GET | /articles | ArticlesController@index | list bài viết |
GET | /articles/create | ArticlesController@create | Tạo mới |
POST | /articles | ArticlesController@store | Lưu mới |
GET | /articles/{article} | ArticlesController@show | Hiển thị |
GET | /articles/{article}/edit | ArticlesController@edit | Chỉnh sửa |
PUT/PATCH | /articles/{article} | ArticlesController@update | Cập nhật |
DELETE | /articles/{article} | ArticlesController@destroy | Xoá |
RESTFUL CONTROLLER
Trong Laravel mặc định nếu mà tạo Controller thì phương thức sẽ được tạo. Những phương thức này sẽ theo pattern bên trên để được đặt tên. Nếu như tạo raPostsController.php
php artisan make:controller PostsController
thì mặc định bên trong nó sẽ có đủ các phương thức nên gọi là Restful Controller.
<?php // app/Http/Controllers/PostsController.php
namespace AppHttpControllers;
use AppHttpRequests;
use AppHttpControllersController;
use IlluminateHttpRequest;
class posts extends Controller {
public function index()
{
//
}
public function create()
{
//
}
public function store()
{
//
}
public function show($id)
{
//
}
public function edit($id)
{
//
}
public function update($id)
{
//
}
public function destroy($id)
{
//
}
}
RESTFUL ROUTES
Và tương tự thế, để định nghĩa được tất cả Route đối với PostsController ta chỉ cần 1 dòng code sử dụng Route::resource
- gọi là Restful Routes :
// app/Http/routes.php
Route::resource('posts', 'PostsController');
Để xác nhận điều đó hãy chạy command sau :
php artisan route:list
Domain | Method | URI | Name | Action | Middleware |
---|---|---|---|---|---|
GET/HEAD | posts | posts.index | App\Http\Controllers\PostsController@index | ||
GET/HEAD | posts/create | posts.create | App\Http\Controllers\PostsController@create | ||
POST | posts | posts.store | App\Http\Controllers\PostsController@store | ||
GET/HEAD | posts/{posts} | posts.show | App\Http\Controllers\PostsController@show | ||
GET/HEAD | posts/{posts}/edit | posts.edit | App\Http\Controllers\PostsController@edit | ||
PUT | posts/{posts} | posts.update | App\Http\Controllers\PostsController@update | ||
PATCH | posts/{posts} | App\Http\Controllers\PostsController@update | |||
DELETE | posts/{posts} | posts.destroy | App\Http\Controllers\PostsController@destroy |
Bạn để ý sẽ thấy cột Name có định nghĩa cả giá trị cho Named Route luôn. Cool ! Không chỉ thế, mà tuỳ vào từng trường hợp bạn hoàn toàn customize Route như bên dưới :
Route::resource('posts', 'PostsController', ['only' =>['index', 'show']]);
Route::resource('posts', 'PostsController', ['except' => ['create', 'store', 'update', 'destroy']]);
Đối với trường hợp Article thì thay vì phải định nghĩa tất cả Routes như đã làm tới 7 dòng code :
// app/Http/routes.php
//Route::get('articles', ['as' => 'articles.index', 'uses' => 'ArticlesController@index']);
//Route::get('articles/create', ['as' => 'articles.create', 'uses' => 'ArticlesController@create']);
//Route::get('articles/{id}', ['as' => 'articles.show', 'uses' => 'ArticlesController@show']);
//Route::post('articles', ['as' => 'articles.store', 'uses' => 'ArticlesController@store']);
//Route::get('articles/{id}/edit', ['as' => 'articles.edit', 'uses' => 'ArticlesController@edit']);
//Route::patch('articles/{id}', ['as' => 'articles.update', 'uses' => 'ArticlesController@update']);
//Route::delete('articles/{id}', ['as' => 'articles.destroy', 'uses' => 'ArticlesController@destroy']);
Thì chỉ cần 1 dòng là đủ !
Route::resource('articles', 'ArticlesController');
Bạn hãy chạy thử php artisan route:list
xem có đúng là như vậy không !?
All rights reserved