Trong phần 1, chúng ta đã cùng nhau tìm hiểu các khái niệm cơ bản Laravel Blade, trong phần 2 này tiếp tục với kiểu cách lập trình trong Blade và cách thức tổ chức Blade template để tận dụng tối đa tính module trong phát triển presentation logic. OK, bắt đầu thôi.
Cấu trúc điều kiện trong Laravel Blade
Mục tiêu của Laravel Blade là để kế thừa và hiển thị dữ liệu, để thực hiện tốt hơn việc này, Laravel Blade cung cấp các lệnh tương tự như trong ngôn ngữ PHP thuần túy như các câu lệnh điều kiện, các vòng lặp…
Câu lệnh điều kiện if
Sử dụng các lệnh @if, @else, @elseif và @endif như trong PHP thuần túy, cú pháp sử dụng như sau:
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
Chúng ta sẽ thực hành một ví dụ ở cuối bài, ví dụ này sẽ thực hành tất cả các kiến thức giúp cho bạn nhanh chóng nắm bắt và cũng giảm thiểu thời gian hơn.
Câu lệnh vòng lặp
Các câu lệnh vòng lặp cũng không có gì là lạ với các bạn khi đã làm quen ở ngôn ngữ PHP thuần túy. Laravel Blade hỗ trợ rất nhiều kiểu vòng lặp
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach
@forelse ($users as $user)
<li>{{ $user->name }}</li>
@empty
<p>No users</p>
@endforelse
@while (true)
<p>I'm looping forever.</p>
@endwhile
Khi sử dụng các vòng lặp, đôi khi chúng ta cần kiểm tra một điều kiện nào đó để tiếp tục vòng lặp hoặc thoát ra khỏi vòng lặp, sử dụng các lệnh @continue, @break:
@foreach ($users as $user)
@if ($user->type == 1)
@continue
@endif
<li>{{ $user->name }}</li>
@if ($user->number == 5)
@break
@endif
@endforeach
Blade còn cho phép viết ngắn gọn hơn nữa,
@foreach ($users as $user)
@continue($user->type == 1)
<li>{{ $user->name }}</li>
@break($user->number == 5)
@endforeach
Đôi khi, trong các vòng lặp chúng ta cần biết xem đây là vòng lặp đầu tiên hay cuối cùng, tổng số vòng lặp, số vòng lặp còn lại… Laravel Blade cung cấp biến $loop giúp bạn xử lý các yêu cầu.
@foreach ($users as $user)
@if ($loop->first)
Đây là vòng lặp đầu tiên.
@endif
@if ($loop->last)
Đây là vòng lặp cuối cùng.
@endif
<p>Mã người dùng: {{ $user->id }}</p>
@endforeach
Với các vòng lặp lồng nhau, biến $loop làm thế nào để biết các yêu cầu trên? thuộc tính parent sẽ giúp bạn xử lý:
@foreach ($users as $user)
@foreach ($user->posts as $post)
@if ($loop->parent->first)
Đây là vòng đầu tiên của vòng lặp ngoài cùng.
@endif
@if ($loop->parent->last)
Đây là vòng cuối cùng của vòng lặp ngoài cùng.
@endif
@endforeach
@endforeach
Bạn có thể tham khảo thêm các thuộc tính khác của $loop ở bảng sau:
Thuộc tính | Mô tả |
---|---|
$loop->index | Trả về chỉ mục vòng lặp hiện hành (bắt đầu bởi 0). |
$loop->iteration | Trả về số vòng lặp đã thực hiện (bắt đầu bởi 1). |
$loop->remaining | Trả về số lần lặp còn lại |
$loop->count | Trả về tổng số lần lặp |
$loop->first | Trả về true nếu là vòng lặp đầu tiên |
$loop->last | Trả về true nếu là vòng lặp cuối cùng |
$loop->depth | Trả về cấp độ lồng nhau của vòng lặp. |
$loop->parent | Trong vòng lặp lồng nhau, trả về $loop của vòng lặp cha. |
Comment trong Laravel Blade
Cũng giống như ngôn ngữ PHP thuần túy, có nhiều lúc chúng ta cần comment một đoạn code lại. Blade cũng cho phép comment bằng cách đưa đoạn code đó vào {{– –}}, chú ý là đoạn comment này sẽ không được sinh ra khi tạo mã HTML, tức là khi xem source code của trang bạn sẽ không nhìn thấy các comment này, không giống như comment trong HTML vẫn thấy khi xem source code của trang.
{{-- Comment đoạn này vào
@foreach ($users as $user)
@endforeach
--}}
Chèn code thuần PHP vào Laravel Blade
Laravel Blade đã cung cấp đủ điều kiện để bạn loại bỏ code PHP thuần ra khỏi template, tuy nhiên có những lúc chúng ta làm nhanh cũng cần thêm một vài đoạn code PHP thuần vào. Theo cách truyền thống, bạn có thể đưa code PHP vào <?php ?>
hoặc đưa vào giữa các câu lệnh @php, @endphp của Blade.
<?php
$frameworks = array('first' => 'Laravel', 'second' => 'CodeIgniter', 'third' => 'Yii');
?>
@foreach ($frameworks as $frm)
{{ $frm }}
@endforeach
Hoặc
@php
$frameworks = array('first' => 'Laravel', 'second' => 'CodeIgniter', 'third' => 'Yii');
@endphp
@foreach ($frameworks as $frm)
{{ $frm }}
@endforeach
Quản lý layout tốt hơn với subview trong Blade
Chúng ta quay lại với layout default ở Phần 1 (resources/views/layouts/default.blade.php), nếu phần code tạo menu rất dài, chúng ta sẽ khó mà quản lý các đoạn code trong layout này, Blade cho phép chúng ta tách ra thành các layout khác và sử dụng @include để kết hợp. Tạo thêm layout menu với đường dẫn đầy đủ như sau resources/views/layouts/menu.blade.php và thêm copy đoạn mã tạo menu từ resources/views/layouts/default.blade.php sang:
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">All Laravel TEnv</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="/">Trang chủ</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Ví dụ <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/first-blade-example">Ví dụ Blade 1</a></li>
<li><a href="/second-blade-example">Ví dụ Blade 2</a></li>
</ul>
</li>
<li><a href="/contact">Liên hệ</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
Tạo thêm layout footer để chứa phần code cho footer (resources/views/layouts/footer.blade.php)
<footer class="footer">
<div class="container">
<p class="text-muted">Copyright © 2017, <a href="https://allaravel.com">All Laravel</a></p>
</div>
</footer>
Ok, khi đó trong default layout chúng ta sẽ chỉ còn như sau:
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<title>All Laravel - @yield('title')</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Custom styles for this template -->
<link href="http://getbootstrap.com/examples/sticky-footer-navbar/sticky-footer-navbar.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<
![endif]-->
</head>
<body>
@include('layouts.menu')
<!-- Begin page content -->
<div class="container">
<div class="page-header">
<h1>Môi trường test Laravel cho website allaravel.com</h1>
</div>
<p class="lead">
@yield('content')
</p>
</div>
@include('layouts.footer')
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</body>
</html>
Với cách thức này việc quản lý các thành phần của giao diện ứng dụng web thật đơn giản, ngoài ra nó có tính kế thừa rất cao khi chúng ta module hóa các thành phần của trang web. Vấn đề khác là các layout này có thể không tồn tại khi sử dụng @include, Laravel sẽ báo lỗi ngay. Vậy cần kiểm tra xem các view này có tồn tại không trước khi include, bạn có thể xem lại đoạn này trong [Laravel View](https://allaravel.com/blog/laravel-view-xay-dung-logic-trong-giao-dien/):
```{.language-php}
use Illuminate\Support\Facades\View;
Route::get('contact', function(){
if (View::exists('fontend.contact')) {
return view('fontend.contact');
} else {
return 'Trang liên hệ đang bị lỗi, bạn vui lòng quay lại sau';
}
});
Nhưng đang ở trong một blade chúng ta có thể sử dụng @includeif, nó sẽ kiểm tra xem view có tồn tại không trước khi include.
@includeIf('layouts.menu', $data)
Cá nhân hóa lệnh trong Laravel Blade
Các lệnh trong Blade có thể là chưa đủ hoặc bạn muốn tạo những lệnh riêng giúp cho việc lập trình nhanh chóng hơn. Laravel hoàn toàn cho phép bạn làm điều đó, ví dụ bạn muốn tạo ra một lệnh Blade riêng để định dạng các biến thời gian, mở AppServiceProvider.php trong thư mục app/Providers, đây là Service Provider mặc định của Laravel và thêm code vào:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
}
Ok, giờ trong các blade chúng ta có thể sử dụng @datetime($value), khi các template này được biên dịch nó sẽ chuyển sang thành
<?php echo ($value)->format('m/d/Y H:i'); ?>
Ví dụ thực hành Laravel Blade phần 2
Như đã nói ở đầu, tất cả bài viết này sẽ gói gọn trong một ví dụ giúp bạn đỡ mất thời gian: Tạo ra một layout cho tin tức, duyệt qua mảng danh sánh các tin tức và in ra màn hình toàn bộ tin tin theo kiểu grid, tin tức mới nhất sẽ có màu khác để dễ nhận biết.
Tạo route mới trong routes/web.php
Route::get('news', function(){
$news_list = array(
['title' => 'Bài viết số 1', 'content' => 'Nội dung bài viết 1', 'post_date' => '2017-01-03'],
['title' => 'Bài viết số 2', 'content' => 'Nội dung bài viết 2', 'post_date' => '2017-01-03'],
['title' => 'Bài viết số 3', 'content' => 'Nội dung bài viết 3', 'post_date' => '2017-01-03'],
['title' => 'Bài viết số 4', 'content' => 'Nội dung bài viết 4', 'post_date' => '2017-01-03']
);
return view('fontend.news-list')->with(compact('news_list'));
});
Tạo một layout cho các phần tử tin tức tên news-time.blade.php nằm trong thư mục resources/views/layouts nội dung như sau:
<div class="panel panel-{{ $class }}">
<div class="panel-heading">
<h3 class="panel-title">{{ $title }}</h3>
</div>
<div class="panel-body">
{{ $content }}
</div>
<div class="panel-footer">{{ $post_date }}</div>
</div>
Tạo view news-list.blade.php trong resources/views/fontend
@extends('layouts.default')
@section('title', 'Danh sách tin tức')
@section('content')
@foreach($news_list as $news)
<div class="col-md-3">
@if($loop->first)
@include('layouts.news-item', ['title' => $news['title'], 'content' => $news['content'], 'post_date' => $news['post_date'], 'class' => 'success'])
@else
@include('layouts.news-item', ['title' => $news['title'], 'content' => $news['content'], 'post_date' => $news['post_date'], 'class' => 'primary'])
@endif
</div>
@endforeach
@endsection
Các ví dụ chúng ta không dùng đến Laravel Controller để cho nhanh, trong các dự án lớn bạn nên tạo Controller để quản lý code tốt hơn. Ok, giờ bạn truy cập đến http://laravel.dev/news sẽ có giao diện giống như hình ảnh ở đầu ví dụ.