Kế thừa giao diện trong Laravel: Giải ngố @section() và nghệ thuật "lắp ráp" của Blade
Chào anh em Viblo, lại là mình đây!
Mặc dù anh em chúng ta mang tiếng là dân Backend, chuyên trị API và Database, nhưng chắc chắn khi làm việc với Laravel, không ít lần chúng ta phải đụng tay vào thư mục resources/views.
Nhớ cái thời code PHP thuần, để làm một cái layout có header và footer dùng chung cho mọi trang, chúng ta toàn phải dùng trò "cắt dán" tà đạo: include 'header.php';, rồi đoạn giữa chứa nội dung trang, xong chốt lại bằng include 'footer.php';. Cách làm đó khiến code cực kỳ lộn xộn và khó kiểm soát các thẻ đóng mở HTML.
Đó là lúc Blade Templating Engine của Laravel xuất hiện như một vị cứu tinh. Và trái tim của hệ thống template này chính là bộ đôi hoàn hảo: @yield và @section(). Hôm nay, hãy cùng mình mổ xẻ xem @section() thực chất là gì và cách nó giúp giao diện của bạn trở nên "DRY" (Don't Repeat Yourself) nhé!
1. Bản chất của @section(): Mảnh ghép của bức tranh
Để hiểu @section(), bạn phải hiểu cơ chế Template Inheritance (Kế thừa giao diện) của Blade.
Hãy tưởng tượng bạn có một cái khung tranh lớn (Master Layout) được dùng chung cho cả trang web. Trong cái khung đó, bạn khoét sẵn một vài cái lỗ trống và dán nhãn cho chúng (ví dụ: lỗ chứa Tiêu đề, lỗ chứa Nội dung chính). Trong Blade, việc "khoét lỗ" này được làm bằng lệnh @yield('tên_lỗ_trống').
Khi bạn tạo ra một trang con (ví dụ: Trang chủ), bạn bảo Laravel rằng: "Ê, lấy cái khung tranh lớn kia ra đây (@extends), rồi lấy cái mảnh ghép này lấp vào cái lỗ có tên tương ứng cho tôi". Việc tạo ra mảnh ghép đó chính là nhiệm vụ của @section().
2. Cách hoạt động thực tế
Hãy xem một kịch bản kinh điển:
Bước 1: Tạo cái khung (Master Layout)
Tạo file resources/views/layouts/app.blade.php:
<!DOCTYPE html>
<html>
<head>
<!-- Khoét một lỗ để trang con tự điền Tiêu đề -->
<title>@yield('title', 'Trang web mặc định')</title>
</head>
<body>
<header>Menu dùng chung ở đây</header>
<main>
<!-- Khoét một lỗ để trang con đổ nội dung vào -->
@yield('content')
</main>
<footer>Bản quyền 2026</footer>
</body>
</html>
Bước 2: Tạo mảnh ghép (Child View)
Tạo file resources/views/home.blade.php:
<!-- Khai báo sử dụng cái khung 'app' ở trên -->
@extends('layouts.app')
<!-- Tạo mảnh ghép đắp vào lỗ 'title' -->
@section('title', 'Trang chủ siêu ngầu')
<!-- Tạo mảnh ghép đắp vào lỗ 'content' -->
@section('content')
<h1>Chào mừng đến với trang chủ!</h1>
<p>Nội dung này sẽ được bê nguyên xi nhét vào giữa thẻ main của layout cha.</p>
@endsection
Khi chạy trang home, Laravel sẽ thông minh lấy nội dung bên trong @section('content') và thế chỗ chính xác vào vị trí của @yield('content') ở file cha. Mã HTML cuối cùng sinh ra sẽ hoàn hảo mà bạn không cần phải copy lại thẻ <html> hay <body> nào cả.
3. Tuyệt chiêu @parent: Kế thừa chứ không "Tuyệt tình"
Mặc định, khi bạn dùng @section() ở file con, nó sẽ ghi đè (overwrite) hoàn toàn nội dung (nếu có) của cái @section cùng tên ở file cha.
Nhưng giả sử, ở file layout cha, bạn đã nhúng sẵn file bootstrap.js. Sang file con, bạn muốn nhúng thêm custom.js nhưng VẪN MUỐN GIỮ LẠI thằng bootstrap.js của cha thì làm sao? Đây là lúc lệnh @parent tỏa sáng.
File layout cha:
@section('scripts')
<script src="bootstrap.js"></script>
@show <!-- Lưu ý: Dùng @show thay vì @yield nếu muốn section cha có nội dung mặc định -->
File view con:
@section('scripts')
<!-- Gọi lại nội dung của section cha -->
@parent
<!-- Viết thêm nội dung của view con -->
<script src="custom.js"></script>
@endsection
Kết quả HTML sinh ra sẽ bao gồm cả 2 file JS nối tiếp nhau. Cực kỳ thanh lịch!
4. Phân biệt @section và @push (Góc nhìn Senior)
Khi bắt đầu làm các dự án phức tạp, anh em sẽ nhận ra để quản lý file CSS/JS riêng cho từng trang con, dùng @section đôi khi hơi cồng kềnh.
Laravel cung cấp một cơ chế khác gọi là Stack (Ngăn xếp) với lệnh @push và @stack.
@sectionhoạt động theo cơ chế Ghi đè (trừ khi gọi@parent).@pushhoạt động theo cơ chế Cộng dồn (Push vào mảng).
Nếu bạn đang include một component (ví dụ một cái Datepicker) vào nhiều nơi trên cùng một trang, và component đó gọi @section('scripts'), nó sẽ bị đè lên nhau và chỉ in ra 1 lần. Nhưng nếu component đó dùng @push('scripts'), các đoạn script sẽ được nối đuôi nhau liên tục.
Do đó, kinh nghiệm xương máu là:
- Dùng
@sectioncho các khối Nội dung HTML lớn (như content, sidebar). - Dùng
@pushcho các đoạn nhúng Scripts và Styles (CSS/JS).
Lời kết
@section() tưởng chừng chỉ là một câu lệnh đơn giản của hệ thống Template, nhưng nó lại là công cụ cốt lõi giúp anh em Backend tổ chức mã nguồn HTML một cách khoa học, tái sử dụng cao và tuân thủ chặt chẽ nguyên tắc DRY. Hiểu rõ sự khác biệt giữa @yield, @section, @show và @push sẽ giúp anh em hoàn toàn làm chủ giao diện, dù dự án có phình to đến hàng trăm view đi chăng nữa. Happy coding!
All Rights Reserved