A+ A A-

larablog 建構日誌:畫面切版

到 YouTube 搜尋 Laravel 教學,找到哥布林老師的教學影片

  我的 Laravel 學習是從書籍開始,到知名書店網站用「Laravel」為關鍵字搜尋,找到一兩本發行時間較近的書籍後下單購買,然後一邊看書一邊累積相關知識。學習一段時間後,我想透過「專案實做」的方式驗證自己所累積的知識是否足夠專案所需,如果不夠該怎麼補足或加強。

  因為娛樂與學習,我經常在 YouTube 網站搜尋感興趣的影片。在搜尋「用 Laravel 製作部落格」的影片中發現哥布林老師的【Laravel實戰】部落格從零開始實作攻略 #by Voyager ,從中看到 BootstrapMade 網站提供眾多專業設計的佈景主題下載,付費版本具備更多功能及售後支援。

  哥布林老師的影片中使用的是 Moderna 這個主題的部落格頁面作為教學示範,比較過 BootstrapMade 網站上其他主題後,覺得 Moderna 的設計調性跟自己比較契合,所以使用這個主題資源給 larablog 使用。

  Moderna 主題提供 Bootstrap 4 及 5 版本免費下載,larablog 使用的是後者。

Moderna 下載頁面

複製前端資源檔案至 /public

  Laravel 專案預設的前台頁面路徑是 public,與前端有關的圖片、CSS 樣式表、JavaScript 腳本檔等檔案應放置在此。將下載的主題壓縮檔解壓縮後將 assets 資料夾內所有檔案/資料夾複製到專案 public 資料夾內。

larablog 的 public 資料夾

Blade template 位置:resources/views

  Moderna 主題部落格樣式頁面是 blog.html(部落格首頁)與 blog-single.html(文章全文),將這兩個檔案複製到專案resources/views資料夾內,更名為 home.blade.php(部落格首頁)與 blog_page.blade.php

命名方式建議以容易識別為原則,與首頁有關的檔案通常會命名為 home 或 index,副檔名需為 .blade.php。

routes/web.php

  routes 資料夾中的 web.php 是網站路由檔案,在這裡看到存取網頁根目錄時會回傳 welcome 這個 View 檔案,指向的是 resources/views 資料夾內的 welcome.blade.php

  將這段程式碼複製後在下方貼上,然後將原有程式碼註記使其不執行,更改為自訂的首頁檔案名後存檔,之後在瀏覽器輸入網址就會改由自訂的首頁檔案顯示。

//Route::get('/', function () {
//    return view('welcome');
//});

Route::get('/', function () {
    return view('home');
});

頁面跑版?很正常,修改資源引用路徑

Moderna Blog 頁面 demo

  上圖是 Moderna 的部落格頁面(blog.html)示範,下面的圖片是我將頁面更名為 Blame template 後(home.blade.php)在瀏覽器顯示的畫面。

更名為 home.blade.php 後瀏覽器的顯示畫面

  範例圖不見了,樣式檔也沒套用到,難道我複製錯了嗎?

  複製的路徑是正確的,只是引用資源的位置要修改。

修正資源引用路徑,改以 asset() 包含

  打開 /resources/views/home.blade.php,在第 21 至第 31 行是 CSS 樣式表的引用敘述,內容是這樣的:

<!-- Vendor CSS Files -->
    <link href="assets/vendor/animate.css/animate.min.css" rel="stylesheet">
    <link href="assets/vendor/aos/aos.css" rel="stylesheet">
    <link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="assets/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
    <link href="assets/vendor/boxicons/css/boxicons.min.css" rel="stylesheet">
    <link href="assets/vendor/glightbox/css/glightbox.min.css" rel="stylesheet">
    <link href="assets/vendor/swiper/swiper-bundle.min.css" rel="stylesheet">

    <!-- Template Main CSS File -->
    <link href="assets/css/style.css" rel="stylesheet">

  在此看到引用的 CSS 樣式表檔案位於 asset 資料夾下,複製到 Laravel 專案後因為資料夾路徑不同了無法引用,才變成上面圖片的樣子。

  那麼就動手修正吧:在 Blade template 中呼叫變數需使用兩組大括弧({{ }})包含,接著使用 asset() 取用 public 資料夾內檔案。

  所以上述的樣式檔引用路徑會更改為以下內容:

<!-- Vendor CSS Files -->
    <link href="{{ asset('vendor/animate.css/animate.min.css') }}" rel="stylesheet">
    <link href="{{ asset('vendor/aos/aos.css') }}" rel="stylesheet">
    <link href="{{ asset('vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">
    <link href="{{ asset('vendor/bootstrap-icons/bootstrap-icons.css') }}" rel="stylesheet">
    <link href="{{ asset('vendor/boxicons/css/boxicons.min.css') }}" rel="stylesheet">
    <link href="{{ asset('vendor/glightbox/css/glightbox.min.css') }}" rel="stylesheet">
    <link href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}" rel="stylesheet">

    <!-- Template Main CSS File -->
    <link href="{{ asset('css/style.css') }}" rel="stylesheet">

  圖片(副檔名為 .jpg)與 JavaScript 檔案(副檔名為 .js)也以同樣的方式變更引用路徑,存檔後瀏覽器重新讀取頁面後就會看到正常顯示的畫面。

檢視頁面原始碼,確認頁面區塊與 HTML 原始碼對應關係

  解決了資源檔案引用的問題後,我回到 home.blade.php 的編輯畫面,查看 HTML 碼段落與網頁畫面的對應關係:

  1 - 39 行定義 HTML 頁面至 (head 標籤結束),包含:

  • 5、6、9、10 行:meta 資訊。
  • 8 行:頁面標題。
  • 12 - 14 行:網站圖標(favicon)。
  • 21 - 31 行:CSS 樣式檔引用。

  41 行開始是頁面本體(body)敘述,包含:

  • 47 - 51 行:網站圖標(文字或圖形)。

logo 標示

  • 53 - 82 行:網站選單。

網站選單

  • 89 - 103 行:頁面標題及網頁路徑(麵包屑)。

麵包屑

  • 105 - 263 行:部落格文章。

部落格文章

  • 265 - 271 行:頁面分頁條。

分頁條

  • 275 - 357 行:畫面右邊的側邊欄:搜尋、分類項目、近期文章、文章標籤。

側邊欄

  362 行開始是頁尾部分,包含:

  • 365 - 379 行:電子報訂閱表單。

電子報訂閱表單

  • 381 - 433 行:常用連結(選單項目)、聯絡資訊、關於本網站說明。

常用連結

  • 435 - 446 行:版權宣告。

版權宣告

  • 449 - 450 行:回到頁首圖標
  • 452 - 463 行:JavaScript 檔案引用

定義切版依據:依照共用程度或功能區塊

  接下來依照「共用程度」或是「功能運作」,將 HTML 碼切割成不同的 Blade template 檔案儲存,主要有兩個目的:

  1. 如果某個區塊是每個頁面會用到的(樣式檔引用、網站選單,頁尾的常用連結、版權宣告等),獨立成單一檔案後只要呼叫該檔案顯示即可,可以減少重複內容,修改後可以整體套用。
  2. 功能運作區塊獨立成單一檔案後可以透過引入與否做到功能開關。

子資料夾內檔案

  Blade template 檔案存放路徑在專案 resoureces/views,可以透過建立資料夾的方式管理不同用途的 Blade template 檔案。當需要引用子資料夾中的檔案時使用 資料夾.檔案 的方式呼叫。

//存取網站根目錄時顯示「frontend」資料夾中的「index.blade.php」檔案。
Route::get('/', function () {
    return view('frontend.index');
});

共用區塊獨立成 Blade template 檔案

  「CSS 樣式表及 JavaScript 檔案」、「網站選單」及「頁尾常用連結及版權宣告」是每個頁面都會用到的,所以將這三個部分的 HTML 碼獨立出來成為 Blade template 檔案,之後在 home.blade.php 引入,驗證是否正常顯示。

  我在/resources/views 資料夾底下建立 layouts 存放上述區塊獨立出來的 Blade template 檔案,分別為:

  • master.blade.php:網站頁面主版,存放 內容(包含CSS 樣式)及 JavaScript 檔案引入。
  • header.blade.php:網頁路徑、網站圖標(文字或圖片)及網站選單。
  • footer.blade.php:頁尾常用連結及版權宣告。

  獨立後的 Blade template 檔案再依需要對內容進行增減,以下是檔案說明:

layouts/master.blade.php

  • 第 2 行:語言改成漢語(臺灣)。
<html lang="zh-Hant-TW">
  • 第 8 - 16 行:移除 meta 關鍵字敘述(Google 已不再索引),新增 Open Graph(開放社交關係圖)資訊讓網站頁面在社群網站分享時以自己所希望的方式呈現。 參考資料:Open Graph 介紹:讓網站在社群上被漂亮分享,就靠 OG 標籤! 內容中使用「@yield」表示該項目資訊交由之後頁面(其他 Blade template,或資料庫內容)呈現,meta name="og:description" 內容與 meta name="description" 共用。
<!-- meta and open graph information -->
<meta name="description" content="@yield('meta_description')">
<meta name="og:title" content="@yield('title')" />
<meta name="og:type" content="article" />
<meta name="og:image" content="@yield('cover_image')" />
<meta name="og:url" content="@yield('url')" />
<meta name="og:site_name" content="從零開始的 Laravel 部落格建置日誌" />
<meta name="og:description" content="@yield('meta_description')" />
<meta name="robots" content="index, nofollow" />

  • 第 18 行:頁面標題。標題內容一樣由之後頁面決定(每個頁面的標題都不同),再加上網站名稱。
<title>@yield('title') - 從零開始的 Laravel 部落格建置日誌</title>
  • 第 20 - 22 行:指定網站的瀏覽器圖示(favicon),「apple-touch-icon」部分是給 apple 設備設定「加入主畫面」時顯示的圖示。
<!-- Favicons -->
<link href="{{ asset('img/favicon.png') }}" rel="icon">
<link href="{{ asset('img/apple-touch-icon.png') }}" rel="apple-touch-icon">
  • 第 24 - 39 行:導入 Google 字型、CSS 動畫和圖示、以及主樣式。
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Roboto:300,300i,400,400i,500,500i,700,700i&display=swap" rel="stylesheet">

<!-- Vendor CSS Files -->
<link href="{{ asset('vendor/animate.css/animate.min.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/aos/aos.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/bootstrap-icons/bootstrap-icons.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/boxicons/css/boxicons.min.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/glightbox/css/glightbox.min.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}" rel="stylesheet">

<!-- Template Main CSS File -->
<link href="{{ asset('css/style.css') }}" rel="stylesheet">
  • 第 51 行:引入 layouts/header.blade.php:網站圖標(文字或圖片)及網站選單、網頁路徑 。
<!-- ======= Header ======= -->
    @include('layouts.header')
<!-- End Header -->
  • 第 58 行:頁面左方主要內容顯示區,由之後的頁面承接。
 <!-- 頁面主內容 -->
     @yield('content')
 <!-- 頁面主內容 -->
  • 第 65 行:引入 layouts/footer.blade.php:顯示頁尾區塊。
<!-- ======= Footer ======= -->
    @include('layouts.footer')
<!-- End Footer -->
  • 第 68 行:頁面右下方的箭頭圖示,點選後回到頁面最上方。
<a href="#" class="back-to-top d-flex align-items-center justify-content-center">
    <i class="bi bi-arrow-up-short"></i>
</a>
  • 第 71-82 行:引入 JavaScript 檔案。
<!-- Vendor JS Files -->
<script src="{{ asset('vendor/aos/aos.js') }}"></script>
<script src="{{ asset('vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('vendor/glightbox/js/glightbox.min.js') }}"></script>
<script src="{{ asset('vendor/isotope-layout/isotope.pkgd.min.js') }}"></script>
<script src="{{ asset('vendor/php-email-form/validate.js') }}"></script>
<script src="{{ asset('vendor/purecounter/purecounter.js') }}"></script>
<script src="{{ asset('vendor/swiper/swiper-bundle.min.js') }}"></script>
<script src="{{ asset('vendor/waypoints/noframework.waypoints.js') }}"></script>

<!-- Template Main JS File -->
<script src="{{ asset('js/main.js') }}"></script>

layouts/header.blade.php

頁首標題、選單、網頁路徑

  • 第 5 - 9 行:網站圖標,使用文字(第 6 行)或是圖片(第 7 - 8 行)擇一。
<div class="logo">
    <h1 class="text-light"><a href="index.html"><span>Moderna</span></a></h1>
    <!-- Uncomment below if you prefer to use an image logo -->
    <!-- <a href="index.html"><img src="assets/img/logo.png" alt="" class="img-fluid"></a>-->
</div>
  • 第 11 - 40 行:網站選單範例,提供第一至第三層選單的呈現語法,第 39 行是行動版頁面會出現的漢堡選單。選單項目可透過 Voyager 製作、產生。
<nav id="navbar" class="navbar">
    <ul>
        <li><a class="" href="index.html">Home</a></li>
        <li><a href="about.html">About</a></li>
        <li><a href="services.html">Services</a></li>
        <li><a href="portfolio.html">Portfolio</a></li>
        <li><a href="team.html">Team</a></li>
        <li><a class="active" href="blog.html">Blog</a></li>
        <li class="dropdown"><a href="#"><span>Drop Down</span> <i class="bi bi-chevron-down"></i></a>
            <ul>
                <li><a href="#">Drop Down 1</a></li>
                <li class="dropdown"><a href="#"><span>Deep Drop Down</span>
                    <i class="bi bi-chevron-right"></i></a>
                        <ul>
                            <li><a href="#">Deep Drop Down 1</a></li>
                            <li><a href="#">Deep Drop Down 2</a></li>
                            <li><a href="#">Deep Drop Down 3</a></li>
                            <li><a href="#">Deep Drop Down 4</a></li>
                            <li><a href="#">Deep Drop Down 5</a></li>
                        </ul>
                </li>
                <li><a href="#">Drop Down 2</a></li>
                <li><a href="#">Drop Down 3</a></li>
                <li><a href="#">Drop Down 4</a></li>
            </ul>
       </li>
       <li><a href="contact.html">Contact Us</a></li>
   </ul>
   <i class="bi bi-list mobile-nav-toggle"></i>
</nav><!-- .navbar -->

layouts/footer.blade.php

頁尾

  規劃上沒有「電子報訂閱」功能,所以將相關區塊刪除。

  • 第 7 - 16 行:頁尾左方區塊一,預定顯示網站選單(可透過 Voyager 產生後指定)。
<div class="col-lg-3 col-md-6 footer-links">
    <h4>Useful Links</h4>
    <ul>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Home</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">About us</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Services</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Terms of service</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Privacy policy</a></li>
    </ul>
</div>
  • 第 18 - 27 行:頁尾左方區塊二,預定顯示網站使用條款及隱私權規定。(選單項目可透過 Voyager 產生後指定)
<div class="col-lg-3 col-md-6 footer-links">
    <h4>Our Services</h4>
    <ul>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Web Design</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Web Development</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Product Management</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Marketing</a></li>
        <li><i class="bx bx-chevron-right"></i> <a href="#">Graphic Design</a></li>
    </ul>
</div>
  • 第 29 - 51 行:頁尾左方區塊三及區塊四,預計將區塊三的樣式從 col-lg-3 更改 col-lg-6,再將區塊四敘述刪除後合併為單一區塊,填入網站簡介。 46 - 49 行是社群網站圖示,可參考 Bootstrap Icons 網站更換成需要的樣式。
<div class="col-lg-3 col-md-6 footer-contact">
    <h4>Contact Us</h4>
    <p>
        A108 Adam Street <br>
        New York, NY 535022<br>
        United States <br><br>
        <strong>Phone:</strong> +1 5589 55488 55<br>
        <strong>Email:</strong> info@example.com<br>
    </p>
</div>

<div class="col-lg-3 col-md-6 footer-info">
    <h3>About Moderna</h3>
    <p>Cras fermentum odio eu feugiat lide par naso tierra. Justo eget nada terra videa magna derita
                        valies darta donna mare fermentum iaculis eu non diam phasellus.</p>
    <div class="social-links mt-3">
        <a href="#" class="twitter"><i class="bx bxl-twitter"></i></a>
        <a href="#" class="facebook"><i class="bx bxl-facebook"></i></a>
        <a href="#" class="instagram"><i class="bx bxl-instagram"></i></a>
        <a href="#" class="linkedin"><i class="bx bxl-linkedin"></i></a>
    </div>
</div>
  • 第 57 - 68 行:網站版權宣告及設計來源。
<div class="container">
    <div class="copyright">
        &copy; Copyright <strong><span>Moderna</span></strong>. All Rights Reserved
    </div>
    <div class="credits">
    <!-- All the links in the footer should remain intact. -->
    <!-- You can delete the links only if you purchased the pro version. -->
    <!-- Licensing information: https://bootstrapmade.com/license/ -->
    <!-- Purchase the pro version with working PHP/AJAX contact form: https://bootstrapmade.com/free-bootstrap-template-corporate-moderna/ -->
    Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a>
    </div>
</div>

將分離後的 Blade template 檔案引用進來

將共用區塊分離後的 head.php 內容行數減少三分之一,在維護上輕鬆許多。以下是重點說明:

@extends('layouts.master')
@section('meta_description', '')
@section('title', '首頁')
@section('cover_image', '')
@section('url', url()->full())
  • 第 1 行:繼承 layouts/master.blade.php 內容,引入頁面規劃主版。
  • 第 2 行:補上 layouts/master.blade.php@yield('meta_description') 資訊。
  • 第 3 行:補上 layouts/master.blade.php@yield('title') 資訊。
  • 第 4 行:補上 layouts/master.blade.php@yield('cover_image') 資訊。
  • 第 5 行:補上 layouts/master.blade.php@yield('url') 資訊。
  • 第 6 - 261 行:補上 layouts/master.blade.php@yield('content') 資訊,以 @section('content') 開頭 @endsection 結尾,中間是頁面主內容。 第 167 - 173 行是頁面分頁條,這裡會透過套件運作自動分頁。
<div class="blog-pagination">
    <ul class="justify-content-center">
        <li><a href="#">1</a></li>
        <li class="active"><a href="#">2</a></li>
        <li><a href="#">3</a></li>
    </ul>
</div>

將側邊欄各功能區塊獨立成 Blade template 檔案

  部落格畫面右方的側邊欄有「搜尋」、「文章分類」、「最近貼文」以及「標籤」四個功能區塊,以先前將重複內容獨立成 Blade template 檔案的相同流程,將功能區塊再分離成獨立 Blade template 檔案引入,一方面再減少 home.php 的內容長度,再來可以做成功能開關,在顯示使用條款這類不需要側邊欄的情況就不需引入。

  以下是獨立出來的各區塊 Blade template 檔案:

  • 搜尋:widgets/search.blade.php
  • 文章分類:widgets/categories.blade.php
  • 最近貼文:widgets/recent_posts.blade.php
  • 標籤:widgets/tags.blade.php

  內容分離後的側邊欄程式碼會是以下內容:

<div class="col-lg-4">
    <div class="sidebar">
        <!-- 搜尋表單 -->
        @include('widgets.search')

        <!-- 文章分類 -->
        @include('widgets.categories')

        <!-- 近期文章 -->
        @include('widgets.recent_posts')

        <!-- 標籤雲 -->
        @include('widgets.tags')
    </div><!-- End sidebar -->
 </div><!-- End blog sidebar -->

結語

  網站的基本頁面透過切版分離成單獨的 Blade template 檔案,接下來透過 Controller 與 Model 的協同合作,將 Blade template 中的範例資料改換成程式運作的動態產出。