Blade Template

การติดตั้งมีได้หลายวิธี แต่วิธีง่ายกว่าคือ สิ่งแรกคือต้องมีโปรแกรม PHP ก่อน อาจมาได้จาก การติดตั้ง XAMPP และ Composer (ค้นคำตาม Google แล้วติดตั้ง)

ติดตั้ง Laravel ผ่าน Composer

ใช้ CLI ไปยังไดเร็กทรอรี่ ที่ต้องการจะติดตั้ง web-app ของ Laravel เช่น ต้องการติดตั้งในโฟลเดอร์ D:/myWebApp ใช้ผ่าน CLI โดยเปิด Command Prompt แล้วเขียนคำสั่งดังนี้

CLI 1.


>D:
>mkdir myWebApp
>cd myWebApp

เมื่อเข้าสู่โพลเดอร์ myWebApp แล้วให้สร้าง เว็บแอปฯ ตามกรอบงาน Laravel ในชื่อ example-app

CLI 2.


>composer create-project laravel/laravel example-app
>cd example-app
>php artisan serve

View คือ Blade

การแสดงผลของหน้าเว็บของ Laravel ใช้หน้า xxx.blade.php โดย xxx คือชื่อ View ที่ใช้เป็นหน้า Template ที่สามารถแทรกโปรแกรม PHP ลงไปได้ ซึ่งเมื่อสร้างโปรเจ็กต์ขึ้นมาใหม่ Laravel จะสร้างหน้า welcome มากให้อัตโนมัติ เราจะเริ่มต้นการทำงานที่หน้านี้

ใส่ Bootstrap CSS

เพื่อมีรูปแบบที่เป็นมาตรฐาน ควรเพิ่ม Bootstrap CSS ใน อิลีเม้นท์ <head> ของไฟล์ welcome.blade.php

Code 1. resources/views/welcome.blade.php

<link href=
 "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" 
 rel="stylesheet" 
 integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" 
 crossorigin="anonymous">
        

หรือจะใส่เป็นไฟล์ที่เก็บใน public/css/bootstrap.min.css

Code 2. resources/views/welcome.blade.php

<link rel="stylesheet" href="css/bootstrap.min.css">
        

ส่งผ่านข้อมูลสู่ view

ข้อมูลสามารถส่งผ่าน Route ซึ่งเป็นเส้นทาง URI หรือ Controller ซึ่งควบคุมการแสดง View เช่น การส่งข้อมูลผ่านมากับ Route

Code 3. routes/web.php

Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
        

การผูกข้อมูลกับ view ใช้ {{ }} ตัวอย่างการผูกข้อมูลที่ส่งมาจาก Route

Code 4. resources/views/welcome.blade.php

<div class="content">
    <div class="title m-b-md">
    Hello, {{ $name }}
    </div>
</div>
        

แทรก ภาษา PHP ใน view

ถ้าต้องการเขียนภาษา PHP ใน view จะใช้ @php..@endphp แทรก

Code 5. resources/views/welcome.blade.php

<div class="content">
    <div class="title m-b-md">
    @php
    $lastname = "Limsatta";
    @endphp
    Hello, {{$name}} {{$lastname}}
    </div>
</div>
        

โลจิกใน View

คำสั่ง if, while, switch, for, foreach นอกจากจะใช้ในแบบ @php.. @endphp  ได้ แต่ก็มีคำสั่งโดยคง (Blade directives) มากกว่าที่ยกตัวอย่าง อ่านเพิ่มเติมได้ที่ https://laravel.com/docs/9.x/blade ในที่นี้ยกตัวอย่างเพียงบางตัว จาก if

@if (count($records) === 1)
    I have one record!
@elseif (count($records) > 1)
    I have multiple records!
@else
    I don't have any records!
@endif

และอื่น ๆ เช่น


@switch($i)
    @case(1)
        First case...
        @break 
    @case(2)
        Second case...
        @break 
    @default
        Default case...
@endswitch

@foreach ($users as $user)
    <p>This is user {{ $user->id }}</p>
@endforeach

@for ($i = 0; $i < 10; $i++)
    The current value is {{ $i }}
@endfor

สร้าง Component

การสร้าง คอมโพเน้นท์ (Component) จะเน้นไปที่สร้างบางส่วนที่จะนำไปแสดงผลที่ view อีกที เข่น สร้างเฉพาะ Form สำหรับกรอกข้อมูล เพราะเมื่อสร้าง คอมโพเน้นท์ แล้ว จะสามารถไปใช้ซ้ำได้ในหน้า view ต่าง ๆ ได้ หรือสร้างขึ้นเพื่อความสะดวกในการดูแล ดังนั้นการสร้าง คอมโพเน้นท์ไม่ได้เป็นตัวสร้าง view เพื่อแสดงผลด้วยตัวเองโดยตรง

ใช้ CLI สร้างคอมโพเน้นท์ หรือจะสร้างจากเขียนเองก็ได้

CLI 3.


>php artisan make:component Forms/Login
        

ซึ่งเป็นส่วนหนึ่งของ view ในการสร้างจะใช้อักษรพิมพ์ใหญ่ ซึ่งจะได้ผลคอมโพเน้นท์สองที่ ไว้ใน app/View/Components และใน resources/views/components

Code 6. app/View/Components/Forms/Login.php

<?php
namespace App\View\Components\Forms;
use Illuminate\View\Component;
class Login extends Component
{
    public function __construct()
    {
        //
    }
    public function render()
    {
        return view('components.forms.login');
    }
}
        

ให้สังเกตฟังก์ชัน render( ) จะเป็นฟังก์ชันที่สร้าง view ซึ่งตรงกับ ไฟล์ login.blade.php และจะนำ view นี้ซึ่งเป็นคอมโพเน้นท์ไปแสดงที่ view อื่น

Code 7. resources/views/components/forms/login.blade.php

<div>
    <!-- Order your soul. Reduce your wants. - Augustine -->
</div>
        

และเมื่อแก้ให้ใข login.blade.php ดังนี้

Code 8. resources/views/components/forms/login.blade.php

<div style="width:500px; margin:20px auto">
    <h2>Login</h2>
        <form method="POST" action="/token">
        @csrf
        <div class="mb-3 row">
            <label class="col-sm-2 col-form-label">Email</label>
            <div class="col-sm-10">
                <input type="email" name="email" class="form-control" 
                 id="inputEmail">
             </div>
         </div>
        <div class="mb-3 row">
            <label class="col-sm-2 col-form-label">Password</label>
            <div class="col-sm-10">
            <input type="password" name="password" class="form-control" 
                autocomplete="off">
             </div>
        </div>
        <div>   
            <button type="submit" class="btn btn-primary" 
            style="float:right">Submit</button>
        </div>
        </form>
</div>
        

นำ Login ไปอ้างอิงใน view ใช้ขึ้นต้นด้วย x และตามด้วยลำดับชั้นของโฟลเดอร์ที่สร้างใน views

Code 9. resources/views/welcome.blade.php

<div style='text-align:left; margin:5px 10px' >
    <x-forms.login />
</div>
        

การเขียน <x-forms.login> ถือว่าเป็นการสร้างออบเจ็กต์คอมโพเน้นท์ของ Login

ในกรณีที่ไม่ได้สร้างโฟลเดอร์ forms การอ้างอิงจะใช้เพียง <x-. หรือ <x- แล้วตามด้วยชื่อคอมโพเน้นท์

<x-.login/>

หรือ

<x-login/>

ส่งข้อมูลเข้าคอมโพเน้นท์

การส่งข้อมูลเข้าคอมโพเน้นท์ จะส่งผ่านค่าที่สมาชิกมีการเข้าถึงได้แบบ public เช่น ต้องการให้ ตัวสมาชิกหนึ่งชื่อ $email โดยการส่งต้องส่งผ่านคอนสตรักเตอร์

Code 10. app/View/Components/Forms/Login.php

<?php
//namespace …ที่เขียนก่อนหน้านี้
class Login extends Component{
    public $email
    public function __construct($email)  {
        $this->email= $email;
    }
    //…และฟังก์ชันที่เขียนเขียนก่อนหน้านี้    
}
        

เมื่ออ้างอิงสมาชิก $email ของคอมโพเน้นท์ ใน view จะใช้เครื่องหมาย ":" นำหน้า เช่น ":email"

Code 11. resources/views/welcome.blade.php

<x-forms.login :email="$defaultEmail"/>
        

จากตัวอย่างนี้อ้างอิงตัวแปรเข้าคอนสตรักเตอร์ด้วย :value แล้วมีค่าเท่ากับ $defaultEmail ดังนั้นตัวแปร $defaultEmail จะต้องมีค่าก่อน

Code 12. resources/views/welcome.blade.php

<form>
    @php
       $value="theerapol@g.com"
    @endphp
    <x-forms.login :email="$value"/>
</form>
        

ให้สังเกตนิดว่า เครื่องหมาย ":" จะเว้นหนึ่งช่องว่าง ถ้าพิมพ์ติดกันจะไม่ทำงาน และห้ามเว้นช่องว่าง กับตัวแปรกับเครื่องหมายเท่ากับ เช่น :email= "$value" อย่างนี้ก็ไม่ได้ นอกจากนี้ ค่า $email จะต้องพิมพ์ติดกันหมด จะใช้ 'your email' ซึ่งพิมพ์ไม่ติดกันไม่ได้ และที่สำคัญคือ $value ต้องเป็นตัวแปรเท่ากัน นั้นหมายความว่าจะต้องเขียน @php ... @endphp มาก่อน

ต่อมาทดสอบการส่งค่า โดยกำหนดให้หน้า view ของคอมโพเน้นท์แสดงค่าในฟอร์ม ดังปรับปรุงคอมโพเน้นท์ใหม่ดังนี้

Code 13. resources/views/components/forms/login.blade.php

<div class="col-sm-10">
    <input value={{$email}}
        type="email" name="email" class="form-control" 
        id="inputEmail" placeholder="name@example.com">
</div>
        

รูป 1 ค่าที่ส่งไปในฟอร์ม จะแทนที่ค่าของ placeholder

ส่งข้อมูลผ่าน Slots

การส่งค่าจากคอมโพเน้นท์ ไปสู่ view ยังมีอีกวิธีที่กลับด้านกันคือ ส่งค่าจาก view ไปคอมโพเน้นท์ คือ การส่งผ่าน ตัวแปรชื่อ $slot ให้ทดลองผ่านคอมโพเน้นท์ที่สร้างใหม่ชื่อ Alert

CLI 4.


>php artisan make:component Alert
        

ต่อจากนี้ปรับปรุงคอมโพเน้นท์นี้ดังนี้

Code 14. resources/views/components/alert.blade.php

<div class="alert alert-danger">
    {{$slot}}
</div>
        

ตัวแปร $slot ก็จะกลายเป็นข้อมูลที่ใส่เข้า <x-alert> ซึ่งเป็น view ของคอมโพเน้นท์ Alert ดังนี้จึงเขียนในส่วนแสดงผลใหม่ (วางบนก่อนส่วนคอมโพเน้นท์ Login)

Code 15. resources/views/welcome.blade.php

<x-alert>
    <strong>Whoop!</strong>Something went wrong!
</x-alert>
        

แต่บางครั้งต้องการการแสดงของ Slot ได้หลายที่ในคอมโพเน้นท์ ก็ให้เพิ่ม ชื่อตัวแปร สมมุติให้ชื่อ title ดังปรับปรุงใหม่

Code 16. resources/views/components/alert.blade.php

<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
    {{$slot}}
</div>
        

และไม่ลืมที่จะสร้างสมาชิกใหม่ $title ให้คลาส Alert ด้วย

 public $title = 'Alert'; 

การแก้ไขนี้จะทำให้สามารถมีส่วนแสดงเพิ่มขึ้น แต่ต้องกำกับด้วยชื่อ name = "title" ใน <x-slot> ที่เป็นส่วนแสดงเพิ่มเติม

Code 17. resources/views/welcome.blade.php

<x-alert>
        <x-slot name="title">
             Alert
        </x-slot>
        <strong>Login!</strong>Need to login first
</x-alert>
        

นอกจากส่งข้อมูลในลักษณะ view template ไปมาได้แล้ว ยังส่งข้อมูลไปยังคอมโพเน้นท์ โดยตรงได้ แต่ต้องมีฟังก์ชันรองรับในการเข้าถึงแบบ public สมมุติให้ เพิ่มฟังก์ชัน formatAlert($str) ฟังก์ชันนี้ทำหน้าที่กลับลำดับอักษร ด้วยฟังก์ชัน strrev( )

Code 18. app/View/Components/Alert.php

public function formatAlert($str){
        return strrev($str);
}
        

การสร้างค่าผ่านฟังก์ชัน formatAlert($str) ใช้การอ้างอิงผ่านตัวแปร $component ดังตัวอย่างต่อไปนี้

Code 19. resources/views/welcome.blade.php

<x-alert>
    <x-slot name="title">
        {{$component->formatAlert('Server Error')}}
    </x-slot>
    <strong>Whoop!</strong>Something went wrong!
</x-alert>
        

เขียน slot ในคลาสคอมโพเน้นท์

นอกจากเขียน slot ใน วิวว์คอมโนเน้นท์แล้วยังจะเขียนผ่านคลาสคอมโพเน้นท์ได้ด้วย วิธีการนี้เรียกว่า Inline โดยเขียนลงในฟังก์ชัน render( ) โดยการเปิดคำสั่งด้วย {{{{'blade' และจบคำสั่งด้วย blade; แทนการคืนค่าฟังก์ชัน view( )

แต่วิธีการนี้สร้างคอมโพเน้นท์ผ่าน CLI ขึ้นใหม่ ซึ่งจะได้ไฟล์เพียงไฟล์เดียวเท่านั้น

CLI 5.


>php artisan make: component Alert2 --inline
        
Code 20. app/View/Components/Alert2.php

public function render()
{
    return {{{{'blade'
        <div class="alert alert-danger">
             <strong>Login!</strong> Need to login first
        </div>
    blade;
}
        

แล้วเรียกใช้ ในไฟล์ welcome.blade.php

		<x-alert2></x-alert2>
		

ใช้คอมโพเน้นท์สร้าง Layouts

ก่อนหน้านี้ใช้หน้าเว็บ welcome.blade.php ทำเป็นหน้าเลย์เอาต์ (Layout) ซึ่งก็ใช้งานได้ดี แต่สำหรับการเขียนให้เป็นคอมโพเน้นท์แล้วทำเป็นหน้าเลย์เอาต์ได้ด้วย ซึ่งจะคงสถานะความเหมือนกันในทุก ๆ หน้าที่ใช้เลย์เอาต์คอมโพเน้นท์นี้ และสามารถที่จะสร้างเลย์เอาต์อื่น ๆ สำหรับการคงสภาพในรูปอื่น ๆ ได้ด้วย

เราจะเริ่มจากสร้างเลย์เอาต์ได้ดังนี้

Code 21. resources/views/components/layout.blade.php

<html>
    <head>
        <title> {{ $title ?? 'Todo Manager' }}</title>
    </head>
    <body>
        <h1>Todos</h1>
        <hr/>
        {{ $slot }}
    </body>
</html>
        

ต่อมานำเลย์เอาต์ที่สร้างไปใช้งาน โดยให้หน้าเว็บ tasks.blade.php เป็นหน้าเก็บเลย์เอาต์ที่สร้าง

Code 22. resources/views/tasks.blade.php

<x-layout>
    <x-slot name="title">
        Custom Title
    </x-slot>
    <ol>
    @foreach ($tasks as $task)
        <li>{{ $task }}</li>
    @endforeach
    </ol>
</x-layout>
        

จากตัวอย่าง ใช้การวนซ้ำของอาร์เรย์ $tasts ซึ่งเป็นข้อมูลที่เก็บไว้ที่หนึ่ง ซึ่งเรายังไม่ได้สร้าง สำหรับตัวแปร ใน slot อ้างอิงชื่อ “title” ที่ใช้เขียนนิยามตัวแปรไว้แล้วในหน้าเลย์เอาต์ และค่าของตัวแปรนี้ก็คือ Custom Title

เมื่อต้องการเป็นหน้าแรกที่เปิดเว็บเแอปฯ ตามเลย์เอาต์ที่สร้างใหม่ก็จะต้องเปลี่ยนเส้นทางใหม่

Code 23. routes/web.php

<?php
use Illuminate\Support\Facades\Route;

use App\Models\Task;

Route::get('/', function () {
    //return view('welcome');
    return view('tasks',['tasks'=> Task::all()]);
});
        

สิ่งคงเหลือคือ ข้อมูลที่ยังไม่ได้เขียน ตามตัวอย่างนี้เก็บข้อมูลไว้ที่ชื่อ Task ในเนมสเปส App\Model โดยการเรียกฟังก์ชันที่เป็นค่า สเตติก (static)

Code 24. app/Models/Task.php

<?php
namespace App\Models;
class Task{
    public static function all(){
        return array(
             "job1"=>"Homework",
             "job2"=>"Laravel",
            "job3" => "Angular"
        );
    }
}
        

สืบทอดเลย์เอาต์

เลย์เอาต์เมื่อสร้างขึ้นแล้ว อยากไปใช้กับอีกหน้าเว็บอื่น ให้นำเลย์เอาต์ไปใช้ได้เลยเพียงแต่เปลี่ยนข้อมูลใหม่ แต่ปัญหามีอยู่ว่า ไม่อยากใช้เลย์เอาต์เดิม และข้อมูลเดิมทั้งหมด แต่ต้องการมีข้อมูลเพิ่มเติม และบางเลย์เอาต์ ไม่ได้ต้องการเลย์เอาต์เดิมทั้งหมด อันนี้การสร้างให้สืบทอดเลย์เอาต์ได้ จึงเป็นตัวช่วยที่ดี

สมมุติว่ามีการสร้างเลย์เอาต์ ในชื่อ app.blade.php ซึ่งอยู่โฟลเดอร์ layouts ดังเขียนดังนี้

Code 25. resources/views/layouts/app.blade.php

<html>
    <head>
        <title>App Name - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            This is the master sidebar.
        @show

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

ตามที่ได้เห็นของเลย์เอาต์นี้ มีคำสั่งไดเร็กทีฟ (Directive) ของ PHP ฝั่งอยู่ในภาษา HTML ร่วมอยู่ด้วยคือ

ต่อไปจะเป็นการกำหนดหน้าที่จะแสดงผลแต่ได้รับการสืบทอดจากเลย์เอาต์ต้นแบบที่สร้างก่อนหน้านี้

Code 26. resources/views/child.blade.php

@extends('layouts.app')

@section('title', 'Page Title')
@section('sidebar')
    @parent
    <p>This is appended to the master sidebar.</p>
@endsection
@section('content')
    <p>This is my body content.</p>
@endsection
        

หน้าแสดงผลจะใช้เลย์เอาต์ต้นแบบ จึงไม่จำเป็นต้องมีโครงสร้าง HTML เพราะใช้โครงร่าง HTML จากต้นแบบแล้ว จากการใช้คำสั่ง @extends(‘layouts.app’)

สำหรับการแสดงผลมีด้วยกันสามส่วนหรือสาม แซกชันท์ (section)

  1. @section('title', 'Page Title') ส่วนนี้ใช้ชื่อ title ให้มีค่าเท่ากับ 'Page Title' ดังนั้นที่เลย์เอาต์ต้นแบบมีชื่อ title ตรงไหนจะถูกแทนที่ด้วย Page Title ซึ่งตรงกับคำสั่ง @yield('title') ดังนั้นในส่วนนี้จะแสดงในส่วนแทป (Tab) ของเว็บ ในส่วนนี้ไม่การจบขอบเขตได้ @endsection เพราะไม่มีข้อมูลอะไรแสดงเพิ่มเติม ซึ่งต่างกับในกับอีกสองแซกชันต่อไป
  2. @section('sidebar') เป็นการใช้ ส่วนเลย์เอาต์ต้นแบบมาแสดงผล จากการเขียนกำกับว่า @parent ซึ่งในต้นแบบมีข้อความว่า "This is the master sidebar." นอกจากนี้ยังมีเพิ่มเติมข้อความอีกว่า "This is appended to the master sidebar." ทำให้มีการแสดงผลของสองข้อความนี้
  3. @section('content') เป็นการแสดงผลตามข้อมูล "This is my body content." ไปแสดงผลตรงที่เลย์เอาต์ต้นแบบกำกับในชื่อ @yield('content')

ตอนนี้ได้นิยามเลย์เอาต์ใหม่แล้ว แต่ยังไม่กำหนดเส้นทางเว็บ จึงต้องกำหนดเส้นทางใหม่

Code 27. routes/web.php

Route::get('/', function () {
    //return view('welcome');
    //return view('tasks',['tasks'=> Task::all()]);
    return view('child');
});
        

เมื่อทดสอบจะได้ผลคือ


This is the master sidebar.
This is appended to the master sidebar.
This is my body content.		
		

Exercise

  1. สร้างคอมโพเน้นท์ ที่เป็นเมนูนำทาง และนำไปใช้กับหน้าต่าง ๆ ได้
  2. ถ้าแมนูรายนำทางแต่ละหน้าไม่เหมือนกัน ให้สร้างเป็นตัวแปรรับรายการนำทาง ใส่เข้าคอมโพเน้นท์ ดูตัวอย่าง Code 3 และ Code 10