Laravel Blade template module hóa trong thiết kế giao diện – Phần 1

Laravel Blade là gì?

Mục tiêu chính của mô hình MVC là tách biệt giữa application logic và presentation logic, Laravel cũng vậy nó không muốn ở trong các view có các câu truy vấn hoặc các code không liên quan đến view, và không muốn các Controller, Model quan tâm đến định dạng dữ liệu. Tuy nhiên view cũng cần một số cú pháp đơn giản để đưa các logic vào trong nó, chính vì thế Laravel Blade ra đời. Blade rất đơn giản nhưng là một bộ máy tạo các mẫu web rất mạnh mẽ, không như các bộ máy tạo mẫu khác của PHP, Blade không hạn chế bạn sử dụng các mã PHP thuần túy trong view. Trong thực tế, tất cả các Blade view được biên dịch thành mã PHP thuần túy và được cache cho đến khi chúng có thay đổi. File Blade view có phần mở rộng là .blade.php và thông thường được lưu trong thư mục resources/views.

Kế thừa một template

Các lợi ích của Laravel Blade là kế thừa template và tính năng section, để bắt đầu chúng ta xem một ví dụ đơn giản sau:

Tạo một thư mục layouts trong thư mục resources/views để chứa các template, tiếp theo chúng ta tạo một blade view trong layouts với tên default.blade.php (resources/views/layouts/default.blade.php) có nội dung như sau:

<html>
    <head>
        <title>All Laravel - @yield('title')</title>
    </head>
    <body>

        @section('sidebar')
            Phần chính trong sidebar.
        @show

        <div class="container">
            @yield('content')
        </div>

    </body>
</html>

Chúng ta có thể thấy template này chứa các mã HTML, có thể insert CSS, ngoài ra nó có các lệnh khá lạ @yield, @section. @section định nghĩa phần nội dung còn @yield sử dụng để hiển thị nội dung mà section đem lại. Các view sẽ kế thừa blade template này bằng cách sử dụng lệnh @extends. Tạo một view có tên first-blade-example nằm trong fontend (resources/views/fontend/first-blade-example.blade.php) với nội dung:

@extends('layouts.default')

@section('title', 'Ví dụ đầu tiên về Blade template')

@section('sidebar')
    @parent

    <p>Phần phụ của sidebar.</p>
@endsection

@section('content')
    <p>Phần nội dung chính của trang ở đây.</p>
@endsection

Ok, giờ chúng ta tạo một route cho ví dụ đầu tiên về blade template.

Route::get('first-blade-example', function(){
	return view('fontend.first-blade-example');
});

Giờ chúng ta chạy http://laravel.dev/first-blade-example

Ví dụ đầu tiên về Blade template trong Laravel

Chúng ta sẽ đi vào chi tiết, first-blade-example.blade.php kế thừa lại template default nằm trong thư mục layouts bằng cầu lệnh extends(‘layouts.default’). Các phần @yield trong Blade template default sẽ lấy giá trị trong phần @section của view kế thừa để hiển thị. Chúng ta thấy khá thú vị phải không? tính năng này có cái gì đó gần giống khái niệm master page trong .NET.

Như vậy trong phần blade template default chúng ta có thể đưa vào sẵn các vùng hiển thị mà trang nào trong website cũng có. Chúng ta tiếp tục đến ví dụ thứ 2: Chúng ta thấy các page trong website đều có phần menu, vậy chúng ta sẽ định nghĩa phần menu trong blade template default (resources/views/layouts/default.blade.php):

<html>
    <head>
        <title>All Laravel - @yield('title')</title>
    </head>
    <body>
        <div id="menu">
            <ul>
                <li><a href="/">Trang chủ</a></li>
                <li><a href="/first-blade-example">Ví dụ</a></li>
                <li><a href="/contact">Liên hệ </a></li>
            </ul>    
        </div>

        @section('sidebar')
            Phần chính trong sidebar.
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

Giờ vào lại http://laravel.dev/first-blade-template

Ví dụ thứ 2 về blade template trong Laravel

Phần menu ở Blade template default đã được kế thừa sang view first-blade-template. Giờ đây, bất kỳ page mới nào chúng ta chỉ cần kế thừa lại Blade template default là có sẵn phần menu.

Component và Slot trong Laravel Blade – tính năng mới trong Laravel 5.4

Component và slot đem lại những lợi ích như section, layouts, tuy nhiên component có thể sử dụng lại được. Ví dụ sau đây sẽ minh chứng điều đó:

Tạo route trong routes/web.php:

Route::get('components', function () {
    return view('fontend.component-example');
});

Chúng ta sẽ thêm thư viện css Bootstrap vào Blade template default giúp cho giao diện responsive (tương thích với nhiều thiết bị) và thiết kế giao diện nhanh chóng. Thay đổi nội dung resources/views/layouts/default.blade.php như sau:

<!DOCTYPE html>
<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>
<!-- 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>

    <!-- 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">
            @section('sidebar')
                Phần chính trong sidebar.
            @show
            @yield('content')
        </p>
    </div>

    <footer class="footer">
        <div class="container">
            <p class="text-muted">Copyright © 2017, <a href="https://allaravel.com">All Laravel</a></p>
        </div>
    </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>

Blade template default đã được style lại theo phong cách của Bootstrap, xem Bootstrap basic template để hiểu hơn. Xem lại giao diện xem thế nào http://laravel.dev/first-blade-example

Ví dụ sử dụng blade template kèm theo bootstrap

Hehe, nhìn giao diện trong đỡ lởm hơn rồi, chúng ta tiếp tục với ví dụ về component và slot trong Laravel. Tạo một blade template alert trong resources/views/layouts/alert.blade.php với nội dung:

<div class="alert {{ $class }}">
    <div class="alert-title"><strong>{{ $title }}</strong></div>
    {{ $message }}
</div>

Chúng ta thêm phần thiết kế message cho blade template default (resources/views/layouts/default.blade.php)

    <!-- Begin page content -->
    <div class="container">
        <div class="page-header">
            <h1>Môi trường test Laravel cho website allaravel.com</h1>
        </div>
        <div>
            <!-- Alert with error -->
            @component('layouts.alert') 

                @slot('class')
                    alert-danger
                @endslot

                @slot('title')
                    Something is wrong
                @endslot

                @slot('message')
                    Test thử message lỗi
                @endslot
                Phần message phụ
            @endcomponent

            <!-- Alert with success -->
            @component('layouts.alert') 

                @slot('class')
                    alert-success
                @endslot

                @slot('title')
                    Success
                @endslot

                @slot('message')
                    Test thử message thành công
                @endslot

                Phần message phụ
            @endcomponent
        </div>
        <p class="lead">
            @section('sidebar')
                Phần chính trong sidebar.
            @show
            @yield('content')
        </p>
    </div>

Ví dụ sử dụng Component và Slot trong Laravel Blade

@component sẽ gọi đến các thành phần và @slot sẽ truyền giá trị vào các thành phần đó.

Hiển thị giá trị một biến trong Blade template

Trong các ví dụ từ đầu đến giờ, rất nhiều lần chúng ta sử dụng {{ $value }} và đây chính là cách in giá trị biến $value ra trong một blade template.

<h2>Hello, my name is {{ $name }}.</h2>

Nếu biến $name chưa được khởi tạo sẽ ứng dụng sẽ báo lỗi, chúng ta có thể kiểm tra trước biến $name và mặc định cho nó một giá trị nếu biến này chưa được khởi tạo.

<h2>Hello, my name is {{ isset($name) ? $name : 'All Laravel' }}.</h2>

Nếu biến $name chưa khởi tạo chúng ta sẽ nhận được kết quả là “Hello, my name is All Laravel”. Thay vì viết lệnh kiểu PHP thuần túy, Laravel cho phép viết code nhanh hơn như sau:

<h2>Hello, my name is {{ $name or 'All Laravel' }}.</h2>

Mặc định, câu lệnh {{ }} của Blade sẽ được tự động gửi vào lệnh htmlspecialchars của PHP, để ngăn chặn tấn công kiểu XSS. Muốn in ra nội dung không bị chuyển đổi chúng ta sử dụng cú pháp:

{!! $comment !!}

Ví dụ: trong các phần dùng để bình luận, để in nội dung bình luận chúng ta nên sử dụng {{ $comment }} vì trong nội dung comment có thể chứa các đoạn mã mà khi in ra kiểu {!! $comment !!} sẽ ảnh hưởng đến nội dung trang web. Chúng ta cùng thực hành:

Để dành nội dung cho phần thực hành, blade template default sẽ được sửa đổi một chút, loại bỏ những phần không quan trọng.

<!DOCTYPE html>
<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>
<!-- 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>

    <!-- 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>

    <footer class="footer">
        <div class="container">
            <p class="text-muted">Copyright © 2017, <a href="https://allaravel.com">All Laravel</a></p>
        </div>
    </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>

Tạo route trong routes/web.php

Route::get('second-blade-example', function(){
    $comment = 'Tôi là <span class="label label-success">All Laravel</span>'; 
    return view('fontend.second-blade-example')->with('comment', $comment);
});

Tạo view mới tên second-blade-example.blade.php trong thư mục resources/views/fontend với nội dung:

@extends('layouts.default')

@section('title', 'Ví dụ thứ hai về Blade template')

@section('content')
    <p>
    	<h3>Comment đã được chuyển đổi ký tự đặc biệt chống tấn công XSS</h3>
    	{{ $comment }}
    </p>
    <p>
    	<h3>Comment chưa được chuyển đổi ký tự đặc biệt</h3>
    	{!! $comment !!}
    </p>
@endsection

Ok, giờ thử truy cập vào http://laravel.dev/second-blade-example

Ví dụ in biến trong Blade có chuyển đổi và không chuyển đổi ký tự đặc biệt

Giờ chúng ta đã thấy rõ sự khác biệt giữa {{ $comment }} và {!! $comment !!}, do đó khi in các nội dung do người dùng đưa vào chúng ta nên sử dụng {{ $comment }} còn những nội dung chúng ta kiểm soát được thì dùng {!! $comment !!}. Nếu gặp kẻ xấu cố tình chèn vào nội dung comment một đường dẫn đến file javascript và trong file đó có code điều hướng trang, trang web của bạn sẽ bị điều hướng sang các trang với mục đích xấu.

Sử dụng cú pháp Blade khi tích hợp các framework Javascript

Rất nhiều các framework Javascript cũng sử dụng {{ }} để in ra giá trị các biến, do đó khi tích hợp một framework Javascript vào dự án mà trong code có những đoạn {{ value }} của Javascript thì nó sẽ không hoạt động do hệ thống mặc định đây là in biến trong Laravel. Để giải quyết vấn đề này bạn sử dụng @, nó sẽ thông báo với Laravel rằng bỏ qua mà không xử lý {{ value }}.

<h1>All Laravel</h1>
Hello, @{{ value}}.

Nếu bạn có rất nhiều các đoạn in biến trong Javascript mà sử dụng @ thì mất thời gian bởi phải điền toàn bộ @ cho các đoạn {{ value }}, một giải pháp Laravel đưa ra là đưa cả đoạn có các lệnh in trong Javascript vào cặp lệnh @verbatim và @endverbatim

@verbatim
    <h2>{{ title }}</h2>
    <p>
        {{ content }}.
    </p>
    <p>
        {{ author }}.
    </p>
@endverbatim

Nếu không có lệnh @verbatim và @endverbatim thì bạn phải viết như sau:

    <h2>@{{ title }}</h2>
    <p>
        @{{ content }}.
    </p>
    <p>
        @{{ author }}.
    </p>

Nếu mà có hàng trăm đoạn {{ value }} thì cũng mỏi tay đấy nhỉ, Laravel cho thấy là một framework PHP tuyệt vời, nó gần như nắm bắt được hết các khó khăn của lập trình viên.

Lời kết

Laravel Blade giúp cho chúng ta module hóa trong thiết kế giao diện một cách rất khoa học, việc quản lý và tạo mới giao diện rất đơn giản và hiệu quả. Cách đây vài năm khi tìm hiểu về Laravel Blade tôi cũng đã phải ngạc nhiên hết cỡ vì Laravel đem đến cho lập trình những khái niệm cực kỳ đơn giản dễ hiểu nhưng lại tạo ra những sản phẩm cực phức tạp. Laravel Blade còn rất nhiều vấn đề và chúng tôi hẹn gặp lại bạn ở phần 2.

4 thoughts on “Laravel Blade template module hóa trong thiết kế giao diện – Phần 1

  1. Chậc, cái cần giải thích thì không nói. Ví dụ @parent là gì ? @show là gì ? Trên documentation của Laravel dùng @stop mà vì sao lại dùng @endsection cũng không thấy giải thích ?

  2. Phần 2 của bạn nên nói rõ về module hoá như nào. Hiện phần 1 mình vẫn thấy là nghiên cứu cơ bản về blade template thôi.

Add Comment