Tìm hiểu Laravel từ số 0 (P5)
Bài đăng này đã không được cập nhật trong 3 năm
Trong phần 4 chúng ta đã đi đến nội dung về Mutators, ở phần này tôi sẽ tiếp tục với các nội dung mới sau đây :
- Seeding
- MVC
- Install Bootstrap 3
- Create Form
Seeding
Với Laravel bạn có thể dùng command artisan db:seed
để có thể sinh ra những dữ liệu khởi tạo vào DB. Việc này sẽ giúp cho công việc tạo dữ liệu sample sử dụng cho development được nhanh hơn. Tôi sẽ tạo ra dữ liệu ví dụ vào bảng articles
trong bài này.
DATABASESEEDER File
Để mà làm được việc đó thì ta sẽ cần thay đổi file bên dưới :
database
└── seeds
└── DatabaseSeeder.php
<?php // database/seeds/DatabaseSeeder.php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class DatabaseSeeder extends Seeder
{
public function run()
{
Model::unguard(); // ①
// $this->call('UserTableSeeder');
Model::reguard(); // ②
}
}
①unguard() là phương thức sẽ thiết lập tắt đi mass assignment của Eloquent. ②reguard() là phương thức sẽ bật lại chức năng đó.
Ví dụ
<?php // database/seeds/DatabaseSeeder.php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Faker\Factory as Faker;
use Carbon\Carbon;
use App\Article;
class DatabaseSeeder extends Seeder
{
public function run() // ①
{
Model::unguard();
$this->call('ArticlesTableSeeder'); // ②
Model::reguard();
}
}
class ArticlesTableSeeder extends Seeder // ③
{
public function run()
{
DB::table('articles')->delete(); // ④
$faker = Faker::create('en_US'); // ⑤
for ($i = 0; $i < 10; $i++) { // ⑥
Article::create([
'title' => $faker->sentence(),
'body' => $faker->paragraph(),
'published_at' => Carbon::today()
]);
}
}
}
Việc thực hiện cụ thể có ý nghĩa như dưới :
① Nếu thực hiện php artisan db:seed
thì phương thức run() của lớp DatabaseSeeder sẽ được chạy.
② Từ trog phương thức đó sẽ gọi đến ArticlesTableSeeder.
③ Nếu mà với mỗi bảng ta tạo ra một lớp thì việc quản lý sẽ dễ dàng hơn.
④ Sử dụng Query Builder để xoá hết bản ghi (nếu có) trong bảng Articles.
⑤ Dùng Faker sẽ giúp ta tạo ra dummy data.
⑥ Và vòng lặp sẽ tạo ra số bản ghi mong muốn.
Chạy SEED
$ php artisan db:seed
Seeded: ArticlesTableSeeder
Để xác nhận kết quả lệnh trên ta có thể dùng tinker
:
$ php artisan tinker
>>> App\Article::all()->toArray();
=> [
[
"id" => "1",
"title" => "Voluptates repellendus libero quia provident officiis laudantium nesciunt.",
"body" => "Ut ab non dolor et nulla mollitia illo. Est quidem saepe adipisci magni unde pariatur animi. Porro vel laboriosam excepturi excepturi dolores. Non doloremque sapiente aut id.",
"created_at" => "2015-02-24 17:35:48",
"updated_at" => "2015-02-24 17:35:48",
"published_at" => "2015-02-24 17:35:48"
],
[
"id" => "2",
"title" => "Accusamus ut repellendus reprehenderit explicabo eos qui qui nobis.",
"body" => "Veritatis fugiat qui ut sint velit. Perspiciatis recusandae laudantium quo voluptatum aliquid distinctio rerum. Assumenda consequatur voluptas aut eos recusandae. Suscipit deleniti aliquid quaerat sapiente numquam consectetur nisi aliquam.",
"created_at" => "2015-02-24 17:35:48",
"updated_at" => "2015-02-24 17:35:48",
"published_at" => "2015-02-24 17:35:48"
],
...
Rất đơn giản ! Bạn đã có một lượng dữ liệu theo ý muốn.
MVC
Ở các bài trước tôi chỉ nói đến phần hiển thị View từ Controller nhưng lần này tôi sẽ thông qua Model để lấy dữ liệu từ DB và truyền chúng đến View để hiện thị trên màn hình, hoàn thiện đủ flow mô hình MVC.
ROUTING
Tôi sẽ thêm vào file routes.php
2 dòng như dưới đây :
// app/Http/routes.php
Route::get('articles', 'ArticlesController@index');
Route::get('articles/{id}', 'ArticlesController@show');
Phần {id}
ở đây là chỉ việc sẽ nhận vào tham số id
trong phương thức show()
.
HIển thị danh sách bài viết
CONTROLLER
Chúng ta sẽ cần tạo ArticlesController
và trong nó sẽ có 2 phương thức đã routing như ở trên.
php artisan make:controller ArticlesController --plain
// app/Http/Controllers/ArticlesController.php
use App\Article;
class ArticlesController extends Controller {
public function index() {
$articles = Article::all();
return view('articles.index', compact('articles'));
}
public function show($id) {
return $id;
}
}
Phương thức index()
ở đây sẽ lấy ra tất cả các bài trong bảng Articles để đưa lên View. Còn phương thức
show()
trước mắt là sẽ chỉ hiển thị ra $id
mà đã nhận được qua tham số truyền đến. Sau đó ta sẽ tạo đến View articles.index
.
<!-- resouces/views/articles/index.blade.php -->
@extends('layout')
@section('content')
<h1>Articles</h1>
<hr/>
@foreach($articles as $article)
<article>
<h2>
<a href="{{ url('articles', $article->id) }}">
{{ $article->title }}
</a>
</h2>
<div class="body">
{{ $article->body }}
</div>
</article>
@endforeach
@endsection
Phần này đang kế thừa layout
View mà đã tạo ra trước đây. Và bằng vòng lặp @foreach sẽ lặp lại việc tạo từng bài một. Riêng tiêu đề của bài thì tôi dùng Helper là url
để gắn link chuyển đến trang với từng id
một. Bạn hãy thử access vào địa chỉ http://localhost:8000/articles
để xác nhận việc danh sách các bài có được hiển thị hay không. Nếu có thì bằng việc click vào tiêu đề bài nào sẽ dẫn đến trang hiển thị của bài đó. Nhưng lúc này ta chỉ thấy id
của bài mà thôi.
Hiển thị bài viết
Do đó ta cần implement cho phương thức show()
:
// app/Http/Controllers/ArticlesController.php
class ArticlesController extends Controller {
// ...
public function show($id) {
$article = Article::findOrFail($id);
return view('articles.show', compact('article'));
}
}
Bằng việc dùng đến phương thức findOfFail
tôi đang lấy ra bài viết theo id
từ Articles. Kế đó sẽ làm View articles.show
.
<!-- resouces/views/articles/show.blade.php -->
@extends('layout')
@section('content')
<h1>{{ $article->title }}</h1>
<hr/>
<article>
<div class="body">{{ $article->body }}</div>
</article>
@endsection
Giờ thì khi truy cập đến địa chỉ http://localhost:8000/articles
, rồi click vào 1 bài bất kì nào, thay vì thấy id
như trước bạn sẽ thấy được cả tiêu đề và nội dung của một bài viết. Đến đây ta đã hoàn thiện mô hình MVC trong Laravel.
Boostrap 3
Nhưng cái bạn thấy khi này chưa hề có CSS nào được thiết lập cả nên tôi sẽ đưa Boostrap3 vào để tinh chỉnh phần hiển thị màn hình. Về cách đưa boostrp vào ta có thể thực hiện theo một vài cách khác nhau.
- Sử dụng Bootstrap CDN : Download về bản Bootstrap mà đã được biên dịch và đặt vào bên dưới thư mục
public
. - Sử dụng source code : Download source của Bootstrap đặt vào thư mục
resources/assets
rồi dùngElixir
để biên dịch và đặt vào thư mụcpublic
. - Cách thích hợp nhất nhưng nhiều bước nên cũng phức tạp nhất : Tôi sẽ nói ở bài sắp tới.
Trong phạm vi bài này tôi sẽ dùng cách đơn giản nhất là qua Bootstrap CDN. Và khi đã xong hãy thử sửa lại layout
View để xác nhận xem Bootstrap đã được chạy đúng chưa.
// resources/views/layout.blade.php
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Blog</title>
<!-- ① Thêm CSS-->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<div class="container"><!-- ② Thêm -->
@yield('content')
</div>
<!-- ③ Thêm Scripts -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
</body>
</html>
① Là bước thêm vào xử lý đọc CSS của Bootstrap CDN.
② Thêm vào div.container
bao quanh @yield(‘content’)
. Nếu mà chỉ định class=”container”
thì Bootstrap sẽ hiển thị margin trái phải. Chi tiết hơn về Bootstrap thì bạn có thể tham khảo thêm ở trang chủ.
③ Thêm vào jQuery CDN và Javascript của Bootstrap CDN.
Chỉ đơn giản như vậy là bạn đã kết thúc việc đưa Bootstrap vào sử dụng. Để thấy được sự thay đổi hãy vào lại http://localhost:8000/articles/
, nếu bạn thấy bên trái phải có margin một khoảng trống thì đã ok rồi. Sự thay đổi này thực sự rất đơn giản, nhưng dần dần ở các bài sau khi tạo ra menu hay form bạn sẽ thấy rõ hơn tác dụng làm đẹp
của Bootstrap.
Tạo Form
Thêm vào gói LARAVELCOLLECTIVE/HTML
Ở trên View của Laravel ta sẽ có hai cách thực hiện việc mô tả Form đó là trực tiếp viết HTML vào hoặc dùng đến các hàm của Helper. Còn trong bài này tôi sẽ cài đặt gói laravelcollective/html
, sử dụng đến các hàm Helper. Gói này đã được tách riêng biệt từ bản Laravel 5 không còn đi kèm như bản 4 nữa. Và gói này được phát triển bởi cộng đồng sử dụng.
Còn một gói khác là illuminate/html
nhưng mà nó đã không còn được maintain nữa nên nếu bạn sử dụng thì hãy chú ý điều này.
Trước tiên là cài đặt nó bằng composer
.
composer require laravelcollective/html
Sau đó là sửa file config/app.php
để cho gói laravelcollective/html
vào.
<?php
// config/app.php
return [
// Đăng kí service, provider
'providers' => [
// ...
Collective\Html\HtmlServiceProvider::class, // Thêm
],
// Đăng kí Facade
'aliases' => [
// ...
'Form' => Collective\Html\FormFacade::class, // Thêm
'Html' => Collective\Html\HtmlFacade::class, // Thêm
],
];
Đến đây bạn sẽ thấy phần service provider hay facade nhưng hãy tạm bỏ qua nó, cứ hiểu để thêm vào gói laravelcollective/html
thì cần thiết lập những phần đó là được.
ROUTING
Ta cần thêm vào route đến ArticlesController@create như sau :
<?php
// app/Http/routes.php
// ...
Route::get('articles', 'ArticlesController@index');
// Thêm
Route::get('articles/create', 'ArticlesController@create'); // ①
Route::get('articles/{id}', 'ArticlesController@show'); // (a)
// ...
Điều bạn cần chú ý ở đây như tôi đã đánh dấu ở trên là phần ① cần ở trước (a). Nếu như không làm vậy thì khi mà có GET request đến articles/create
phần articles/{id}
sẽ match trước mất, nên ArticlesController@show
sẽ được thực thi. Vì thứ tự route là matching từ trên xuống.
CONTROLLER
Ta sẽ implement phương thức create
trong ArticlesController.php
. Đơn giản chỉ là hiển thị ra View.
<?php namespace AppHttpControllers;
// app/Http/Controllers/ArticlesController.php
// ...
class ArticlesController extends Controller
{
// ...
public function create()
{
return view('articles.create');
}
}
VIEW
Bạn hãy tạo mới file create.blade.php
như dưới đây :
// resources/views/articles/create.blade.php
@extends('layout')
@section('content')
<h1>Write a New Article</h1>
<hr/>
{!! Form::open() !!}
<div class="form-group">
{!! Form::label('title', 'Title:') !!}
{!! Form::text('title', null, ['class' => 'form-control']) !!}
</div>
<div class="form-group">
{!! Form::label('body', 'Body:') !!}
{!! Form::textarea('body', null, ['class' => 'form-control']) !!}
</div>
<div class="form-group">
{!! Form::label('published_at', 'Publish On:') !!}
{!! Form::input('date', 'published_at', date('Y-m-d'), ['class' => 'form-control']) !!}
</div>
<div class="form-group">
{!! Form::submit('Add Article', ['class' => 'btn btn-primary form-control']) !!}
</div>
{!! Form::close() !!}
@endsection
Bạn có nhận ra sự mới mẻ trong chỗ này.
Thứ nhất là mô tả {!! XXX !!}, nó là mô tả của blade template, sử dụng khi mà hiển thị ra kết quả đã được PHP xử lý. Trước bạn đã gặp dạng mô tả {{ XXXX }}
nhưng mà {!! XXXX !!}
có cái khác là không xử lý escape. Lý do là vì lần này HTML của Form được sinh ra bằng PHP nên không cần phải escape.
Thứ hai là mô tả Form::XXX
, phần này có nghĩa là bạn đang dùng đến gói laravelcollective/html
đã cài đặt bước trên rồi. Dù nhìn qua thì nó đang gọi đến phương thức static của lớp Form nhưng thực ra là đang gọi đến instance method của lớp Collective\Html\FormBuilder
. Ở đây ta dùng một cái tên gọi khác là alias
, có thể hiểu là nó đang access vào instance của Collective\Html\FormBuilder
- đây gọi là Facade. Hãy cùng đi vào chi tiết :
- Tạo tag bắt đầu của form : Form::open()
- Tạo tag kết thức của form : Form::close()
- Tạo tag label : Form::label()
- Tạo tag input : Form::input() input
- Tạo tag là input[type=text] : Form::text()
- Tạo rag textarea : Form::textarea()
- Tạo tag là input[type=submit] : Form::submit()
Bạn có thể xem chi tiết hơn nữa ở trên docs API reference theo link dưới. http://laravelcollective.com/docs/5.1/html
Và việc chỉ đinh class=”xxxx”
ở đây là việc chỉ định class của Bootstrap3 đã được đưa vào ở phần bên trên. Lúc này form của bạn sẽ được đẹp hơn so với mặc định.
Hãy vào http://localhost:8000/articles/create
để xem. Còn phần source bạn có thể View Source sẽ thấy HTMK như sau :
<form method="POST" action="http://localhost:8000/articles/create" accept-charset="UTF-8">
<input name="_token" type="hidden" value="pEUyvKqFqjlcEfh2lvIkGEeI3rWAAwdbgo3XicvW">
<div class="form-group">
<label for="title">Title:</label>
<input class="form-control" name="title" type="text" id="title">
</div>
<div class="form-group">
<label for="body">Body:</label>
<textarea class="form-control" name="body" cols="50" rows="10" id="body"></textarea>
</div>
<div class="form-group">
<label for="published_at">Publish On:</label>
<input class="form-control" name="published_at" type="date" value="2015-03-04" id="published_at">
</div>
<div class="form-group">
<input class="btn btn-primary form-control" type="submit" value="Add Article">
</div>
</form>
Hãy để ý phần method
đã được thiết lập là POST
, phần action
được thiết lập URL hiện tại nhưng ta sẽ thay đổi ngay sau đây.
Save
Khi mà Form được Submit thì sẽ thực hiện xử lý đến DB. Hãy thêm route đến ArticlesController@store
và lưu ý là sẽ dùng POST
chứ không dùng GET
.
<?php
// app/Http/routes.php
// ...
Route::get('articles', 'ArticlesController@index');
Route::get('articles/create', 'ArticlesController@create');
Route::get('articles/{id}', 'ArticlesController@show');
// Thêm
Route::post('articles', 'ArticlesController@store');
// ...
Tiếp đến phần implement phương thức store
trong ArticlesController.php
.
<?php namespace AppHttpControllers;
use AppArticle;
use App\Http\ControllersController;
class ArticlesController extends Controller {
// ...
public function create() {
return view('articles.create');
}
public function store() {
// ① Get giá trị vào của Form
$inputs = \Request::all();
// ② Sử dụng Mass assignment tạo bài viết trong DB
Article::create($inputs);
// ③ Chuyển hướng đến danh sách bài viết
return redirect('articles');
}
}
① Ở phần \Request::all()
này đang lấy ra tất cả giá trị nhập vào của Form. Lớp Request
này cũng là một Facade - Illuminate/Http/Request
. Và vì ta cần phải chỉ định nó như là một lớp global để dùng lớp Facade ở trong controller name space nên bạn đừng quên gắn thêm dấu gạch ngược \
ở trước tên lớp. Chi tiết hơn bạn có thể tham khảo theo link dưới.
http://laravel.com/docs/5.1/requests
http://laravel.com/api/5.1/Illuminate/Http/Request.html
② Tôi dùng chức năng Mass assignment đã từng đề cập trong bài trước để tạo dữ liệu vào bảng Articles, bạn chú ý đừng quên thiết lập biến fillable
ở Article model.
③ Cuối cùng sau khi thực hiện xong lưu trữ bài viết, ta chuyển hướng người dùng đến danh sách để có thể xem được kết quả bằng cách dùng hàm redirect()
.
Và tôi sẽ đi sửa một chút file View create.blade.php
đã tạo thành :
// resources/views/articles/create.blade.php
// ...
{!! Form::open(['url' => 'articles']) !!} // Truyền tham số vào Form::open() để chỉ định url sẽ mở
// ...
Khi bạn xác nhận nội dung của HTML đươc tạo ra nó sẽ như sau :
<form method="POST" action="http://localhost:8000/articles" accept-charset="UTF-8">
...
Bạn thấy url
của action
đã được thay đổi, khi mà click submit thì phướng thức ArticlesController@store
được thực hiện.
Tôi sẽ đặt một nút dùng để tạo mới bài viết ở trên danh sách như sau :
// resources/views/articles/index.blade.php
@extends('layout')
@section('content')
<h1>Articles</h1>
<hr/>
{!! link_to('articles/create', 'Add new article', ['class' => 'btn btn-primary']) !!}
@foreach($articles as $article)
...
...
@endforeach
@stop
Dùng hàm link_to
sẽ tạo ra một tag link dẫn đến articles/create
và kết hợp với lớp của bootstrap3 sẽ tạo style cho link thành giống một nút.
Đến đây ta đã hoàn tất những màn hình, chức năng từ danh sách, nút tạo mới, màn hình tạo mới bài viết rồi tạo xong sẽ chuyển hướng người dùng về lại danh sách để xem kết quả.
All rights reserved