Ở bài trước controller của TaskFlow mới chỉ trả về mảng và chuỗi. Tiếp tục với series, bài này mình sẽ giới thiệu với mọi người về Blade template trong Laravel 13 và dùng nó để làm trang danh sách, trang chi tiết project bằng HTML.
1. View và Blade template.
View là các file chứa giao diện (HTML), tách riêng khỏi phần xử lý trong controller. View nằm trong thư mục resources/views, VD welcome.blade.php ở Bài 2.
Blade là template engine có sẵn của Laravel. File Blade có đuôi .blade.php, được biên dịch thành PHP thuần và cache trong storage/framework/views cho đến khi bị sửa, nên gần như không làm chậm ứng dụng.
Để tạo view, các bạn tạo file bằng tay hoặc dùng lệnh artisan:
php artisan make:view projects.index
Tên view dùng dấu . thay cho dấu / của thư mục con: projects.index ứng với file resources/views/projects/index.blade.php.
Trả về view và truyền dữ liệu.
Để trả về view từ route hoặc controller, các bạn sử dụng hàm view() với cú pháp như sau:
view($view, $data);
Trong đó: $view là tên view, $data (không bắt buộc) là mảng dữ liệu truyền vào view, mỗi key sẽ trở thành một biến trong view.
VD: Method index của ProjectController trả về view projects.index thay cho mảng.
- app/Http/Controllers/ProjectController.php
use Illuminate\View\View;
public function index(): View
{
$projects = $this->projects->all();
return view('projects.index', compact('projects'));
}
- resources/views/projects/index.blade.php
<h1>Danh sách project</h1>
<p>TaskFlow đang có {{ count($projects) }} project.</p>
Truy cập /projects sẽ thấy "TaskFlow đang có 2 project.". Hàm compact('projects') của PHP tạo ra mảng ['projects' => $projects]. Các bạn cũng có thể truyền từng biến bằng method with(): view('projects.index')->with('projects', $projects), gọi nối tiếp được nhiều lần.
2. Hiển thị dữ liệu.
Để in giá trị ra view, các bạn đặt biến (hoặc bất kỳ biểu thức PHP nào) trong cặp {{ }}. Blade biên dịch nó thành <?php echo e(...); ?>, hàm e() gọi htmlspecialchars nên các ký tự HTML không bị trình duyệt hiểu là thẻ, giúp chống lỗi XSS. Nếu muốn in nguyên bản, các bạn dùng {!! !!}.
VD: Với route Route::view('/hello', 'hello', ['name' => '<b>TaskFlow</b>']) (xem Bài 3) và view resources/views/hello.blade.php như sau:
<p>Xin chào, {{ $name }}</p>
{{-- Output: <p>Xin chào, <b>TaskFlow</b></p> --}}
<p>Xin chào, {!! $name !!}</p>
{{-- Output: <p>Xin chào, <b>TaskFlow</b></p> --}}
Chú ý: Chỉ dùng {!! !!} với dữ liệu tin cậy, dữ liệu người dùng nhập luôn in bằng {{ }}.
Các bạn cũng thấy {{-- ... --}} ở trên: đó là comment của Blade, khác với <!-- ... --> của HTML, nó không xuất hiện trong HTML trả về.
3. Câu lệnh điều kiện.
Blade có các directive (các lệnh bắt đầu bằng @) tương ứng với câu lệnh điều kiện của PHP:
@if (count($projects) > 1)
Có nhiều project.
@elseif (count($projects) === 1)
Có một project.
@else
Chưa có project nào.
@endif
@unless ($tab === 'tasks')
Đang xem tab tổng quan.
@endunless
@empty($project['tasks'])
Project chưa có task nào.
@endempty
@if, @elseif, @else, @endif hoạt động giống hệt if của PHP, @unless thì ngược lại: chạy khi điều kiện sai. @isset và @empty là cách viết gọn của hàm isset() và empty(), VD @isset($project['description']) ... @endisset.
4. Vòng lặp.
Tương tự, Blade có @for, @foreach, @while, và thêm @forelse: giống @foreach nhưng có nhánh @empty chạy khi mảng rỗng.
VD: Thay dòng "TaskFlow đang có ..." trong projects/index.blade.php bằng danh sách link đến trang chi tiết.
<ul>
@forelse ($projects as $project)
<li>
{{ $loop->iteration }}.
<a href="{{ route('projects.show', ['id' => $project['id']]) }}">{{ $project['name'] }}</a>
</li>
@empty
<li>Chưa có project nào.</li>
@endforelse
</ul>
{{-- Output: 1. Website toidicode.com, 2. TaskFlow API (link /projects/1, /projects/2) --}}
Bên trong @foreach và @forelse luôn có biến $loop: index (từ 0), iteration (từ 1), count, remaining, first, last, even, odd, và depth, parent khi lồng vòng lặp. Ngoài ra có @continue, @break, có thể kèm điều kiện: @break($loop->iteration === 5).
Khi thật sự cần PHP thuần trong view, các bạn dùng @php ... @endphp, nhưng hạn chế thôi: phần tính toán nên đặt ở controller.
5. Layout với @extends, @section và @yield.
Các trang thường có chung phần khung (thẻ <head>, menu, footer). Thay vì lặp lại ở mọi view, chúng ta viết một view layout chứa phần khung, các view con kế thừa layout và chỉ điền phần nội dung riêng:
@yield('tên')trong layout đánh dấu chỗ hiển thị sectiontên, tham số thứ hai (không bắt buộc) là giá trị mặc định.@extends('layouts.app')ở đầu view con cho biết view kế thừa layout nào.@section('tên') ... @endsectionkhai báo nội dung cho section, hoặc viết gọn@section('tên', 'giá trị')(giá trị vẫn được escape).@include('tên-view')chèn một view khác vào, view đó dùng được mọi biến của view cha và nhận thêm mảng dữ liệu ở tham số thứ hai.
VD: Layout chung và menu cho TaskFlow, các trang project sẽ kế thừa layout này.
- resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>@yield('title') - {{ config('app.name') }}</title>
</head>
<body>
@include('partials.nav')
<main>
@yield('content')
</main>
</body>
</html>
- resources/views/partials/nav.blade.php
<nav>
<a href="{{ route('home') }}">Trang chủ</a> |
<a href="{{ route('projects.index') }}">Project</a>
</nav>
- resources/views/projects/index.blade.php
@extends('layouts.app')
@section('title', 'Danh sách project')
@section('content')
<h1>Danh sách project</h1>
<ul>
{{-- vòng lặp @forelse như ở phần 4 --}}
</ul>
@endsection
Với trang chi tiết, mình thêm description, tasks vào từng project trong mảng mẫu (project 2 là null và []) và method find() tìm project theo id:
- app/Repositories/ProjectRepository.php
[
'id' => 1,
'name' => 'Website toidicode.com',
'description' => 'Viết series Laravel 13 & làm mới giao diện',
'tasks' => ['Viết bài Blade template', 'Thiết kế trang chủ'],
],
public function find(int $id): ?array
{
foreach ($this->all() as $project) {
if ($project['id'] === $id) {
return $project;
}
}
return null;
}
- app/Http/Controllers/ProjectController.php
public function show(Request $request, string $id): View
{
return view('projects.show')
->with('project', $this->projects->find((int) $id))
->with('tab', $request->query('tab', 'overview'));
}
- resources/views/projects/show.blade.php
@extends('layouts.app')
@section('title', $project['name'] ?? 'Không tìm thấy project')
@section('content')
@if ($project === null)
<p>Không tìm thấy project.</p>
@else
<h1>{{ $project['name'] }}</h1>
@isset($project['description'])
<p>{{ $project['description'] }}</p>
@endisset
@if ($tab === 'tasks')
<ul>
@forelse ($project['tasks'] as $task)
<li>{{ $task }}</li>
@empty
<li>Project chưa có task nào.</li>
@endforelse
</ul>
@else
<p>Số task: {{ count($project['tasks']) }}</p>
<a href="{{ route('projects.show', ['id' => $project['id'], 'tab' => 'tasks']) }}">Xem task</a>
@endif
@endif
@endsection
Lúc này /projects/1 có tiêu đề "Website toidicode.com - TaskFlow", dấu & trong mô tả thành & trong mã HTML. /projects/2?tab=tasks hiển thị "Project chưa có task nào.", /projects/9 hiển thị "Không tìm thấy project." nhưng vẫn trả về status 200, trả về lỗi 404 đúng chuẩn mình sẽ nói ở Bài 11.
6. Lời kết.
Như vậy chúng ta đã tìm hiểu xong phần 1 của Blade template trong Laravel 13: view, hiển thị dữ liệu, điều kiện, vòng lặp và layout với @extends. Các bạn có thể xem thêm tại tài liệu chính thức. Bài sau mình sẽ giới thiệu về Blade component, slot, layout component và Vite + Tailwind để làm giao diện cho TaskFlow.
Đăng ký nhận tin.
Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!





0 Comments