<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Link7 &#8211; Larry的午茶時光</title>
	<atom:link href="https://blog.yuyansoftware.com.tw/tag/link7/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.yuyansoftware.com.tw</link>
	<description></description>
	<lastBuildDate>Wed, 22 Jan 2025 02:16:17 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.yuyansoftware.com.tw/wp-content/uploads/2022/10/favicon-45x45.png</url>
	<title>Link7 &#8211; Larry的午茶時光</title>
	<link>https://blog.yuyansoftware.com.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Laravel Passwordless Authentication 無密碼驗證登入，使用 Signed URL (簽署過的網址)</title>
		<link>https://blog.yuyansoftware.com.tw/2023/07/laravel-passwordless-authentication/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 23 Jul 2023 05:00:59 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[網路科技]]></category>
		<category><![CDATA[Link7]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=16096</guid>

					<description><![CDATA[Passwordless Authentication 無密碼驗證登入是這幾年有在討論的技術議題，包含微軟、Google、Apple 態度上應該都是往這方向走。本文會走一遍 Laravel 官網一篇關於無密碼登入的文章。]]></description>
										<content:encoded><![CDATA[
<p>Passwordless Authentication <strong>無密碼驗證登入</strong>是這幾年有在討論的技術議題，包含微軟、Google、Apple 態度上應該都是往這方向走。</p>



<p>無密碼登入的實現方式包含：登入時在預先綁定的 mobile device 壓指紋驗證。或是傳簡訊或 Email 到之前註冊的手機或信箱，使用者依照簡訊或 Email 的內容 (通常是一串碼)，輸入網站做登入，等等。</p>



<p>實踐的方式各有變化，撰文的現在 (2023年7月) 還沒有一個通用的規範。</p>



<p>larry 之前關於 <a href="https://blog.yuyansoftware.com.tw/2022/04/cpbltv-hami-video-subscribe/">中華電信 Hami Video</a> 的文章，Hami Video 就是用手機簡訊方式做無密碼登入。當然，那篇文章也提到：Hami Video 的背後是中華電信，只有電信商才能負擔這麼大量的手機簡訊費用。</p>



<h2 class="wp-block-heading">無密碼登入適合哪種用戶使用？</h2>



<p>無密碼登入適合哪種用戶使用？特別適合網站的低頻使用用戶，甚至一次性、或是幾個月、一兩年才使用一次的用戶。對於這些用戶，他們不用去管理密碼，並保密自己的密碼，自己設定的密碼強度也有可能不夠。</p>



<p>「保密自己的密碼」這句話邏輯上似乎有點好笑。但根據 larry 這麼多年服務客戶的經驗，還是有人直接密碼明碼給別人，請別人幫忙去處理事情。不是說不相信別人，而是我們就事論事，這樣密碼就有可能外洩。</p>



<p>有時候教育的成本很高，還不如設計無密碼登入。</p>



<h2 class="wp-block-heading">Laravel 官網的文章與範例</h2>



<p>今年 (2023) 三月份，larry 看到 Laravel 官網有一篇關於無密碼登入的文章。它是用寄 Email 給用戶的方式，Email 中包含一個簽署過的網址 (Signed URL)，用戶點該連結後直接登入。<br><a href="https://laravel-news.com/passwordless-authentication-in-laravel" target="_blank" rel="noreferrer noopener nofollow">https://laravel-news.com/passwordless-authentication-in-laravel</a></p>



<p>這篇 Laravel 官網文章有附作者的 GitHub Repo，程式架構的東西看文字不一定好懂，直接從作者的 sample project 來看可能會更為理解。</p>



<p>首先 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">resources/views/app/auth/login.blade.php</mark></code> 中包含了<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;livewire:auth.login-form /&gt;</mark></code></p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">resources/views/app/auth/register.blade.php</mark></code> 中包含了<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;livewire:auth.register-form /&gt;</mark></code></p>



<p>它是用 Laravel Livewire 開發的，讀者不一定要使用 Livewire，本篇重點在理解 Laravel 官網文章的概念。</p>



<p>Livewire 的 component 概念可以參考我之前的文章 <a href="https://blog.yuyansoftware.com.tw/2021/05/laravel-blade-template-component-slot-layout/">Laravel Blade Templates：Components, Slots, and Building Layouts</a></p>



<p>其中 class based component 的概念，Livewire 應該是基於此開發的。</p>



<p>上方提到的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;livewire:auth.login-form /&gt;</mark></code> 對應到 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Http/Livewire/Auth/LoginForm.php</mark></code></p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;livewire:auth.register-form /&gt;</mark></code> 對應到 <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color"><code>app/Http/Livewire/Auth/RegisterForm.php</code></mark></p>



<p>使用者送出 Email 時會觸發 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">LoginForm::submit</mark></code> 這個 function，裡面會觸發 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Actions/Auth/SendLoginLink.php</mark></code></p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">SendLoginLink::handle</mark></code> 裡面會使用 Laravel 內建的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">URL::temporarySignedRoute</mark></code> 將你指定的網址簽章編碼，並設定該連結的有效期限。Signed Route 是 Laravel 5.6 開始支援，時間過的真的好快啊。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">SendLoginLink::handle</mark></code> 將簽章編碼好的連結傳給 Mailable，並寄給用戶。Mailable 是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Mail/Auth/LoginLink.php</mark></code></p>



<p>使用者收信點連結後導回網站，由 controller <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">LoginController::__invoke</mark></code> 檢查簽章、登入、以及轉向指定網頁。</p>



<p>以上就是整個 Email 用戶 Signed URL，使用者無密碼登入的流程。Laravel 官網文章也有附使用者無密碼「註冊」的 sample code，但只是無密碼登入的變化，這裡就不再重複。</p>



<h2 class="wp-block-heading">結語</h2>



<p>Passwordless Authentication <strong>無密碼驗證登入</strong>不只是一個登入方式的變化，而是很值得參考的網路科技的趨勢，至少包含微軟、Google、Apple 態度上應該都是往這個方向走。</p>



<p>本篇討論的 Laravel 官網文章是用 Email 用戶 Signed URL 的方式，Laravel 早已有簽章網址的模組，可以很輕易且漂亮的實作，並且可以指定簽章網址的有效期限。</p>



<p>除了無密碼登入，很多商業應用都可以使用 Signed URL 的方式。Signed URL 概念上、實作上，應該是一個滿值得大家了解的功能。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Laravel Eloquent Accessor、Mutator &#038; Attribute Casting，Laravel 9 開始已大改</title>
		<link>https://blog.yuyansoftware.com.tw/2023/04/laravel-accessor-mutator-attribute-casting/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 23 Apr 2023 13:51:54 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Link7]]></category>
		<category><![CDATA[新功能]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=14774</guid>

					<description><![CDATA[larry 想藉 Laravel 9 大幅更新 Accessor &#038; Mutator 的這個機會，走一遍 Mutator &#038; Casting 的官方文件。包含新寫法，value object 的概念，以及 PHP 8.1 開始支援的 Enum Casting。]]></description>
										<content:encoded><![CDATA[
<p>去年 (2022) 發布的 Laravel 9 中，Eloquent Accessor &amp; Mutator 迎來了一次大改，Attribute Casting 的部分則是調整不大。</p>



<p>伴隨著 PHP 8.1 的釋出，Laravel 8 的後繼版本已經支援 Enum Casting (當然 Laravel 9 之後的版本也會支援)。</p>



<p>延伸閱讀：<a href="https://blog.yuyansoftware.com.tw/2022/05/laravel-9-release-new-features/">Laravel 9 的新功能：最低要求 PHP 8.0，底層更新為 Symfony 6.0，開始使用 PHP 8.1 的 Enum</a></p>



<p>larry 想藉 Laravel 9 大幅更新 Accessor &amp; Mutator 的這個機會，走一遍 Mutator &amp; Casting 的官方文件。本篇文章會以 Laravel 9 Mutator &amp; Casting 官方文件為主。<br><a rel="noreferrer noopener" href="https://laravel.com/docs/9.x/eloquent-mutators" target="_blank">https://laravel.com/docs/9.x/eloquent-mutators</a></p>



<p>首先，依照官方文件的語意，Accessor 指的是 get 這個動作，Mutator 指的是 set。</p>



<p>什麼時機會使用到 Accessor / Mutator 呢？例如，每次存一個值都要 encrypt，或是取出來用時都要 decrypt。或是，你要每次都要把 array 轉成 json 存到資料庫，使用時取出 json 要再轉回 array。</p>



<p>也就是，如果你每次「存」或「取」都要做一定動作，可以考慮使用 Accessor / Mutator。Laravel 9 開始，假如你要存取一個 model 裡的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">first_name</mark></code> attribute，要這樣寫</p>



<pre class="wp-block-code"><code>use Illuminate\Database\Eloquent\Casts\Attribute;

// 注意有 return type
// 注意是 protected method, 不允許調用端直接調用
protected function firstName(): Attribute
{
    // 可以只寫 get 或 set 其一
    return Attribute::make(
        get: fn ($value) =&gt; ucfirst($value),
        set: fn ($value) =&gt; strtolower($value),
    );
}</code></pre>



<p>有時後程式邏輯需要把資料庫中的幾個欄位，重組成一個新的 object，Laravel 稱之為 value object。</p>



<pre class="wp-block-code"><code>use Illuminate\Database\Eloquent\Casts\Attribute;

// 注意是 protected method
// 當調用端存取 address 時會觸發這個 function 
// 也就是自動新增了一個 address attribute
protected function address(): Attribute
{
    // class Address 就是所謂的 value object
    return Attribute::make(
        get: fn ($value, $attributes) =&gt; new Address(
            $attributes&#91;'address_line_one'],
            $attributes&#91;'address_line_two'],
        ),
        set: fn (Address $value) =&gt; &#91;
            'address_line_one' =&gt; $value-&gt;lineOne,
            'address_line_two' =&gt; $value-&gt;lineTwo,
        ],
    );
}</code></pre>



<p>讀者可以觀察一下，<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">get / set 使用的都是 PHP 7.4 的 arrow function。</mark>get return 的是一個新生成的 object，set 中設定的是一個 array。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">class Address</mark></code> 就是所謂的 value object。調用端要使用，會像是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">$user-&gt;address-&gt;lineOne</mark></code>。</p>



<p>定義 value object 時，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">lineOne</mark></code>、<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">lineTwo</mark></code> 都要是 public。</p>



<pre class="wp-block-code"><code>class Address
{
    public $lineOne;
    public $lineTwo;

    public function __construct(...)
    {
        //…
    }
}</code></pre>



<p>其實把 DB 中的欄位重新包裝成 value object (如上方的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">class Address</mark></code>)，在程式邏輯中使用，已經算是 Attribute Casting。Laravel 提供了一個不用寫 Accessor / Mutator 的方式，將 DB 欄位轉成常用的資料格式。</p>



<h2 class="wp-block-heading">Attribute Casting</h2>



<p>例如 Laravel 的 DB migration <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">boolean</mark></code>，實際在 MySql 裡是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">tinyint(1)</mark></code>，存的值是 1 或 0。這時你可以 cast 成 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">boolean</mark></code>。</p>



<pre class="wp-block-code"><code>use Illuminate\Database\Eloquent\Casts\AsStringable;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;

class User extends Model
{
    protected $casts = &#91;
        'is_admin' =&gt; 'boolean',
        'directory' =&gt; AsStringable::class,
        'options' =&gt; 'array',
        // 也可以 cast 成 PHP ArrayObject 或 Laravel Collection
        //'options' =&gt; AsArrayObject::class,
        //'options' =&gt; AsCollection::class,
    ];
}</code></pre>



<p>也就是如果 DB migration 是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">boolean</mark></code>，這樣可以使用沒錯。但如果要寫漂亮一點，model attribute 可以 cast 成 <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color"><code>boolean</code></mark>。字串的部分，你也可以 cast 成 Laravel fluent string，方便後續操作。</p>



<p>另外，如果你的 DB migration 有 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">json</mark></code> 或 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">text</mark></code>，model attribute 可以 cast 成 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">array</mark></code>，存取時自動會在 array 和 json 格式轉換。等同於資料庫欄位可以直接存 array，這是很有趣的。</p>



<p>接下來是一個重要功能 Enum Casting (僅支援 PHP 8.1 以上的環境)</p>



<pre class="wp-block-code"><code>use App\Enums\ServerStatus;
 
protected $casts = &#91;
    'status' =&gt; ServerStatus::class,
];</code></pre>



<p>這樣要存或取 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">$model-&gt;status</mark></code> 都會直接轉換成 enum。</p>



<pre class="wp-block-code"><code>if ($server-&gt;status == ServerStatus::Provisioned) 
{
    $server-&gt;status = ServerStatus::Ready;
    $server-&gt;save();
}
</code></pre>



<h2 class="wp-block-heading">Custom Casts</h2>



<p>如果你執行 Artisan command</p>



<pre class="wp-block-code"><code>php artisan make:cast YourClass</code></pre>



<p>就會新增一個 YourClass 在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Casts</mark></code> 目錄下。例如 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">class Address</mark></code></p>



<pre class="wp-block-code"><code>use App\ValueObjects\Address as AddressValueObject;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Address implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        // 將 DB 欄位轉成 value object return
        return new AddressValueObject(
            $attributes&#91;'address_line_one'],
            $attributes&#91;'address_line_two']
        );
    }

    public function set($model, $key, $value, $attributes)
    {
        if (! $value instanceof AddressValueObject) 
        {
            //throw exception
        }
 
        // return array 去設定 DB 欄位
        return &#91;
            'address_line_one' =&gt; $value-&gt;lineOne,
            'address_line_two' =&gt; $value-&gt;lineTwo,
        ];
    }
}</code></pre>



<p>記得文章上方的 Accessor / Mutator，使用的是 PHP 7.4 的 arrow function。function expression 內容就如同上方範例，只是 Custom Casts 把程式獨立到一個 class。</p>



<p>記得要設定 model，這樣等於新增了一個 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">address</mark></code> attribute (DB 存的是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">address_line_one</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">address_line_two</mark></code>)</p>



<pre class="wp-block-code"><code>use App\Casts\Address;
 
protected $casts = &#91;
    ‘address’ =&gt; Address::class,
];</code></pre>



<h2 class="wp-block-heading">結語</h2>



<p>Accessor / Mutator 與 Casting 是一體的兩面。你可以寫 Accessor / Mutator，但如果正好有 Laravel 預先定義的 cast 資料類型，就直接用 casting 比較方便，包含 PHP 8.1 的 enum。</p>



<p>使用情境上，如果每次存取資料都有一個固定動作，可以考慮使用 Accessor / Mutator 或 Casting。例如每次存一個值都要 encrypt，或是每次都要把 array 轉成 json 存到資料庫，使用時取出 json 要再轉回 array。</p>



<p>或是轉成 Laravel Stringable 或 Collection 等資料格式，方便後續處理。將幾個 DB 欄位重新包裝成 value object 也是一個使用情境。</p>



<p>Accessor / Mutator 與 Casting 不是 Laravel 的大主題，但實際走過文件，還是有很多細節要注意。希望大家在資料庫欄位 / 程式邏輯之間的轉換 (ORM，Object Relational Mapping)，也有更深一層的理解囉。&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Laravel Vite：繼 Laravel Mix 之後新的前端檔案管理工具</title>
		<link>https://blog.yuyansoftware.com.tw/2022/12/laravel-vite/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sat, 10 Dec 2022 11:53:49 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[前端工程]]></category>
		<category><![CDATA[gulp]]></category>
		<category><![CDATA[Link7]]></category>
		<category><![CDATA[新功能]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=13078</guid>

					<description><![CDATA[從 Laravel 9.19 開始 (2022/6/30)，Laravel 前端檔案管理工具從 Mix 改為 Vite。Laravel Mix 的底層是 Webpack，而 Vite 是基於原生 JavaScript ESM 的前端開發工具。Vite 在熱更新時 (HMR) 是先 refresh 瀏覽器，才編譯與該頁面有關的檔案。]]></description>
										<content:encoded><![CDATA[
<p>從 Laravel 5.4 開始 (2017/1/24)，前端檔案管理工具就是 <strong>Laravel Mix</strong>，我當年還有一篇關於 <a rel="noreferrer noopener" href="https://blog.yuyansoftware.com.tw/2017/04/laravel-mix/" target="_blank">Laravel Mix 的文章</a></p>



<p>Laravel 9.19 開始 (2022/6/30)，Laravel 前端檔案管理工具從 Laravel Mix 改為 <strong>Vite</strong>。</p>



<p>每次寫關於軟體工程演進類的文章，都會有感於時間過得真的太快。2017~2022，五年多就這樣過去了。五年的時間，希望大家在人生中都有累積些什麼。</p>



<p>Laravel Mix 的底層是 <strong>Webpack</strong>，在 Laravel 根目錄會有 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">package.json</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">webpack.mix.js</mark></code> 兩支檔案。執行</p>



<pre class="wp-block-code"><code>npm install</code></pre>



<p>會依照 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">package.json</mark></code> 去安裝套件到你的開發環境。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">package.json</mark></code> 中有一個 “scripts” 欄位，定義了很多 npm 指令，並將 npm 指令轉成 mix 指令。mix 指令就會去跑 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">webpack.mix.js</mark></code> 這支檔案裡的 script，包含 compile css / js。</p>



<p><strong>Laravel Vite</strong> 是基於 <a rel="noreferrer noopener" href="https://vitejs.dev/" target="_blank">Vite</a>，是一個基於原生 JavaScript ESM 的前端開發工具。與 Webpack 機制上不同的是，Webpack 一律依照 script 打包全部檔案素材 (dev mode 也是打包全部)。</p>



<p>而 Vite 在<strong>熱更新</strong>時 (hot module replacement，HMR)，是先 refresh 瀏覽器，檢查現在前端頁面有關的檔案是否有更新，如果有，才編譯與該頁面有關的檔案 (而不是每次都全部打包)。因此在開發階段，尤其是大型專案，會大幅加速開發時間。</p>



<p><strong>Laravel Vite</strong> 一樣有 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">package.json</mark></code>，一樣要跑 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">npm install</mark></code>。只是根目錄 script 檔案不是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">webpack.mix.js</mark></code>，改為用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">vite.config.js</mark></code>。</p>



<p>Laravel Vite 的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">package.json </mark></code>中一樣有 “scripts” 欄位：</p>



<pre class="wp-block-code"><code>npm run dev  // 轉成 “vite”
npm run build  // 轉成 "vite build"</code></pre>



<p>vite 指令就會去跑 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">vite.config.js</mark></code> 這支檔案裡的 script，預設是</p>



<pre class="wp-block-code"><code>export default defineConfig({
    plugins: &#91;
        laravel({
            input: &#91;
                'resources/sass/app.scss',
                'resources/js/app.js',
            ],
            refresh: true,
        }),
    ],
});</code></pre>



<p>入口的 css / js 一樣是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">resources/sass/app.scss</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">resources/js/app.js</mark></code>。注意有一個設定 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">refresh: true</mark></code>，這代表在執行 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">npm run dev</mark></code> 時，如果底下這幾個路徑檔案有更新</p>



<ul class="wp-block-list">
<li>app/View/Components/**</li>



<li>lang/**</li>



<li>resources/lang/**</li>



<li>resources/views/**</li>



<li>routes/**</li>
</ul>



<p>瀏覽器已 load 的頁面會整頁 refresh。以上是預設的監聽路徑，你也可以將自己想監聽的檔案路徑設定給 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">refresh</mark></code> 參數 (以設定 array 代替 true)，這樣 Vite 就會監聽你定義的路徑。</p>



<p>跟之前一樣 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app.scss</mark></code> 中預設 import 隔壁的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">_variables.scss</mark></code>。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app.js</mark></code> 中預設 import 隔壁的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">bootstrap.js</mark></code>，在其中 load 你需要的套件，例如：jQuery，Bootstrap，Vue，等。</p>



<p>在前端檔案加一行 (與 <strong>Mix</strong> 不同，<strong>Vite </strong>引用 css / js 在同一行)</p>



<pre class="wp-block-code"><code>@vite(&#91;'resources/sass/app.scss', 'resources/js/app.js'])</code></pre>



<p>就可以將 compile 好的 css / js 引入 (並加 preload 屬性)。如果是 Build Mode (<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">npm run build</mark></code>)，css / js 檔名會加上版本號 (auto-versioning)。</p>



<p>目前如果先安裝 laravel/ui，build 出來的 css / js 會包含 Bootstrap 5.2.3，是目前 (2022年12月) Bootstrap 的最新版。</p>



<p>Vite 也可以處理前端素材檔案，例如圖檔跟字型檔。在 JS 的入口處 (<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app.js</mark></code>) 寫入</p>



<pre class="wp-block-code"><code>import.meta.glob(&#91;
  '../images/**',
  '../fonts/**',
]);</code></pre>



<p>執行 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">npm run build</mark></code>，這樣 Vite 就會將素材檔名編碼過，並移到 public folder 底下。前端要用素材檔可以這樣寫</p>



<pre class="wp-block-code"><code>&lt;img src="{{ Vite::asset('resources/images/logo.png') }}"&gt;
{{-- &lt;img src="http://your-url.com/build/assets/logo.1234ooxx.png"&gt; --}}</code></pre>



<h4 class="wp-block-heading">結論</h4>



<p>從 Webpack 到 Vite，底層是完全不同了。但可以看出來 <strong>Laravel Vite</strong> 創作時，作者盡量不動 Laravel 的架構，與之前一樣的 css / js 入口檔案。</p>



<p>Vite config 當然有一些獨立的功能和寫法。既然 Laravel 決定改用 Vite 了，我們應該也要隨之學習和習慣 Vite 的使用。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Laravel 官方網站關於第三方 API 串接的教程</title>
		<link>https://blog.yuyansoftware.com.tw/2022/08/integrate-third-party-api-in-laravel/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 21 Aug 2022 10:49:06 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Link7]]></category>
		<category><![CDATA[PHP 8]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=11421</guid>

					<description><![CDATA[前陣子看到 Laravel 官方部落格有一篇關於 API 串接，很好的文章。larry 覺得這篇文章很好是在於，除了 API 串接的架構我們可以參考，另外關於 PHP 的寫法 (包含 PHP 8 的新語法)，還有一些軟體工程的概念，都很值得我們參考。]]></description>
										<content:encoded><![CDATA[
<p>前陣子看到 Laravel 官方部落格有一篇關於 API 串接，很好的文章<br><a rel="noreferrer noopener" href="https://laravel-news.com/working-with-data-in-api-integrations" target="_blank">https://laravel-news.com/working-with-data-in-api-integrations</a></p>



<p>larry 覺得這篇文章很好是在於，除了 API 串接的架構我們可以參考，另外關於 PHP 的寫法 (包含 PHP 8 的新語法)，還有一些軟體工程的概念，都很值得我們參考。</p>



<p>這篇教程是以範例的方式來進行。首先，新增一個 service<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/MedicalTrust/MedicalTrustService.php</mark></code></p>



<p>Laravel service container 的觀念，要做 service container binding</p>



<pre class="wp-block-code"><code>// app/Providers/AppServiceProvider.php

public function boot()
{
    $this-&gt;app-&gt;singleton(MedicalTrustService::class,
        fn () =&gt; new MedicalTrustService(
	    baseUrl: ‘......’,
            apiToken: ‘.....’,
	),
    );
}</code></pre>



<p>注意教程範例中，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 本身就是 concrete class，也沒有 implement 任何介面，其實不用特別寫 binding (系統會自動 resolve)。他這樣寫應該是為了餵參數進去，如果參數寫在 service 裡面，binding 這一段其實可以省掉。</p>



<p>新增一個 trait<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/Concerns/BuildsBaseRequest.php</mark></code></p>



<p>這邊用的是 Laravel HTTP Client 的用法<br><a href="https://laravel.com/docs/9.x/http-client" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/9.x/http-client</a></p>



<pre class="wp-block-code"><code>trait BuildsBaseRequest
{
    public function buildRequestWithToken(): PendingRequest
    {
        //…
    }

    public function buildRequestWithDigestAuth(): PendingRequest
    {
        //…
    }
}</code></pre>



<p>新增一個 trait<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/Concerns/CanSendGetRequest.php</mark></code></p>



<pre class="wp-block-code"><code>trait CanSendGetRequest
{
    public function get(PendingRequest $request, string $url): Response
    {
        return $request-&gt;get(
    	    url: $url,
    	);
    }
}</code></pre>



<p>新增一個 trait<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/Concerns/CanSendPostRequest.php</mark></code></p>



<pre class="wp-block-code"><code>trait CanSendPostRequest
{
    public function post(PendingRequest $request, string $url, array $payload = &#91;]): Response
    {
        return $request-&gt;post(
    	    url: $url,
    	    data: $payload,
    	);
    }
}</code></pre>



<p>注意這份教程所有輸入參數到 function，都使用 PHP 8 Named Arguments 的語法。</p>



<p>還記得本文最上方新增的<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/MedicalTrust/MedicalTrustService.php</mark></code><br>此時就可以把這些 trait 都用上</p>



<pre class="wp-block-code"><code>class MedicalTrustService
{
    use BuildBaseRequest;
    use CanSendGetRequests;
    use CanSendPostRequests;
 
    // PHP 8 的 Constructor property promotion
    public function __construct(
        private readonly string $baseUrl,
        private readonly string $apiToken,
    ) {}

    // ...
}
</code></pre>



<p>注意這份教程所有 class constructor，都使用 PHP 8 Constructor property promotion 的語法。</p>



<p>新增一個 service<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/MedicalTrust/Resources/DentalResource.php</mark></code></p>



<pre class="wp-block-code"><code>class DentalResource
{
    public function __construct(
        private readonly MedicalTrustService $service,
    ) {}
 
    public function list(string $identifier): Response
    {
        return $this-&gt;service-&gt;get(
    	    request: $this-&gt;service-&gt;buildRequestWithToken(),
    	    url: "/dental/{$identifier}/records",
    	);
    }
 
    public function addRecord(string $identifier, array $data = &#91;]): Response
    {
        return $this-&gt;service-&gt;post(
            request: $this-&gt;service-&gt;buildRequestWithToken(),
    	    url: "/dental/{$identifier}/records",
    	    payload: $data,
    	);
    }
}
</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 注入了 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code>，也可以說是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 再包裝 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code>。</p>



<p>注意這種寫法，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 把牙醫的 API 實作完全分離出去了。也就是如果我們要新增，例如復健科，要另新增一個 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">RehabResource.php</mark></code></p>



<p>教程的作者提到，這樣寫可以，但要輸入 payload 時，很有可能是長長一段 json 或是 array，所以教程的作者希望把 API 輸入做成 object。下面重寫了 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">function addRecord</mark></code>，把 payload 用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewDentalTreatment</mark></code> object 去實作 (larry 本篇重點是了解 whole picture，資料結構做成物件的細節我們先不看)。</p>



<pre class="wp-block-code"><code>class DentalResource
{
    // 省略

    public function addRecord(string $identifier, NewDentalTreatment $request): Response
    {
        return $this-&gt;service-&gt;post(
      	    request: $this-&gt;service-&gt;buildRequestWithToken(),
      	    url: "/dental/{$identifier}/records",
      	    payload: $request-&gt;toArray(),
        );
    }
}
</code></pre>



<p>在調用端 (caller side)，我們新增一個&nbsp;<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Http/Controllers/Dental/Crowns/StoreController.php</mark></code></p>



<pre class="wp-block-code"><code>class StoreController
{
    public function __construct(
        private readonly DentalResource $api,
	private readonly DentalTreatmentFactory $factory,
    ) {}
 
    public function __invoke(NewCrownRequest $request): RedirectResponse
    {
        // do some request validation here
	// if fails, throw Exception

    	$treatment = $this-&gt;api-&gt;addRecord(
    	    identifier: $request-&gt;get('patient'),
    	    request: $this-&gt;factory-&gt;make(
  	        attributes: $request-&gt;validated(),
  	    ),
  	);

  	// ...
    }
}
</code></pre>



<p>所以整個架構是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 注入 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code>，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 注入 controller。注意 controller function 的參數是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewCrownRequest</mark></code>。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewCrownRequest</mark></code> 是 custom form request，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">$request-&gt;validated()</mark></code> 將 post 資料餵給 factory，產生 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewDentalTreatment</mark></code> object，餵給 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code>。</p>



<p>Again，我們這篇主要是理解 whole picture，所以 factory method 如何產生 object 這一段我們暫時不看。</p>



<h4 class="wp-block-heading">結論</h4>



<p>基本上 get / post 等基礎工程寫在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code>，它是一個非常通用的 service。<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 做的是針對這組 API，做 post 參數的格式，並將 payload 物件化，開有語意的 function 給 caller 端使用。將 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 注入 controller 就可以使用了。</p>



<p>本篇除了參考原教程作者串 API 的架構，另外就是參考他 PHP 的寫法。例如 trait 的使用，為什麼要用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 注入 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 這樣的分層。他也大量使用 PHP 8 的新寫法 Named Arguments 以及 Constructor property promotion。輸入的 array 資料物件化的部分也可以參考一下。</p>



<p>larry 最後想提的是，我們要參考的是原教程為什麼要這樣寫的精神，而不是拘泥於它的架構。實際上我們架構如何設計與撰寫，應該還是以實際的狀況和需求來判斷。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Laravel Blade Templates：Components, Slots, and Building Layouts</title>
		<link>https://blog.yuyansoftware.com.tw/2021/05/laravel-blade-template-component-slot-layout/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Mon, 17 May 2021 14:15:50 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Laravel Blade]]></category>
		<category><![CDATA[Link7]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=7775</guid>

					<description><![CDATA[Component &#038; Slot 的概念從 Laravel 5.4 開始就有了，一直演進到目前是 Laravel 8。寫法上觀念上都有很大的改變，也看得出來 Laravel 前端 template 是越來越往 component based 的框架在走。]]></description>
										<content:encoded><![CDATA[
<p><a rel="noreferrer noopener" href="https://laravel.com/docs/8.x/blade" target="_blank">https://laravel.com/docs/8.x/blade</a></p>



<p>******* 以下文章是基於 Laravel 8 *******</p>



<p>Component &amp; Slot 的概念從 Laravel 5.4 開始就有了，一直演進到目前是 Laravel 8。寫法上觀念上都有很大的改變，也看得出來 Laravel 前端 template 是越來越往 component based 的框架在走。前幾年開始到目前 (2021年5月)，似乎 component based 的前端框架還是主流，這點可以再做觀察。</p>



<p>本篇文章主要是講 Laravel 8 的 Component &amp; Slot。如果基礎 Laravel Blade Templates 還不熟的讀者，可以參考我之前的文章：<a rel="noreferrer noopener" href="https://blog.yuyansoftware.com.tw/2016/09/laravel-blade-template-basics/" target="_blank">Laravel Blade Templates：The Basics</a></p>



<p>首先，Component 分為兩種：class based component 和 anonymous component。簡單來說，class based component 是由一支 Class 檔案搭配一支 view template 檔案。而 anonymous component 則是簡單只有一支 view template 檔案。</p>



<h4 class="wp-block-heading">Class based component</h4>



<p>如上方 Laravel 8 官方文件，要建立一個 class based component，可以下這個指令</p>



<pre class="wp-block-code"><code>php artisan make:component Alert</code></pre>



<p>這個指令會在</p>



<ul class="wp-block-list">
<li>App\View\Components 新增一支 Class 檔案。</li>



<li>resources/views/components 新增一支 view template 檔案。</li>
</ul>



<p>建立 class based component 時也可以建立檔案結構 (比較好管理) </p>



<pre class="wp-block-code"><code>php artisan make:component Forms/Input</code></pre>



<p>這個指令會在</p>



<ul class="wp-block-list">
<li>App\View\Components\Forms 新增一支 Class 檔案。</li>



<li>resources/views/components/forms 新增一支 view template 檔案。</li>
</ul>



<p>調用端如何使用？用 x- 開頭接 component class name，以小寫 dash (-) 分隔的方式</p>



<pre class="wp-block-code"><code>&lt;x-alert/&gt;

&lt;x-user-profile/&gt;</code></pre>



<p>如果 component 是有檔案結構，如上例 Forms/Input，除了小寫 dash (-) 分隔的規則，檔案上下層以 dot (.) 分隔</p>



<pre class="wp-block-code"><code>&lt;x-forms.input/&gt;</code></pre>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">Component 如何接收調用端的資料</h4>



<p>component class 用 constructor 接收調用端的資料 (下例並非完整，僅用於勾勒出 component 如何接收資料，完整 class 請參考官方文件)</p>



<pre class="wp-block-code"><code>class Alert extends Component
{
    ...

    public function __construct($type, $message)
    {
        $this-&gt;type = $type;
        $this-&gt;message = $message;
    }

    ...
}</code></pre>



<p>這樣調用端就可以將 type, message 傳進 component。<br>注意傳 php 變數的屬性前要加 colon (:)</p>



<pre class="wp-block-code"><code>&lt;x-alert type="error" :message="$message"/&gt;</code></pre>



<p>再看一下上方 class Alert。component class 的 view 可以直接使用 component class 的 public 變數，不用特別傳到 view 裡 (跟一般 Laravel controller 不同)。以上方 class Alert 來說，$type, $message 應該是 public 變數，class Alert 的 view 可以直接使用。</p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">Component Attributes</h4>



<p>所有不在 component constructor 接收的變數，都會自動存到 component 的 &#8220;attribute bag&#8221;，其實就是 component 預設的 $attributes 變數。所以如果沒有特別設定 component constructor，想直接顯示調用端所下的屬性，component view 可以這樣寫</p>



<pre class="wp-block-code"><code>&lt;div {{ $attributes }}&gt;
    &lt;!-- Component content --&gt;
&lt;/div&gt;</code></pre>



<p>很多時候，component view 本身可以設定 css class，同時也開放調用端下 css，可以使用 $attributes-&gt;merge</p>



<pre class="wp-block-code"><code>&lt;div {{ $attributes-&gt;merge(&#91;'class' =&gt; 'alert alert-'.$type]) }}&gt;
    {{ $message }}
&lt;/div&gt;</code></pre>



<p>還記得 class Alert，$type 變數是 constructor 接收。如果調用端這樣寫</p>



<pre class="wp-block-code"><code>&lt;x-alert type="error" :message="$message" class="mb-4"/&gt;</code></pre>



<p>上面 $attributes-&gt;merge 就會把 class=&#8221;alert alert-error&#8221; 跟 &#8220;mb-4&#8221; 合併在一起，最後產出的 html 是</p>



<pre class="wp-block-code"><code>&lt;div class="alert alert-error mb-4"&gt;
    &lt;!-- Contents of the $message variable --&gt;
&lt;/div&gt;</code></pre>



<p>html 屬性不是只有 css class，其他屬性不見得能下多個值。如果 $attributes-&gt;merge 這樣寫</p>



<pre class="wp-block-code"><code>&lt;button {{ $attributes-&gt;merge(&#91;'type' =&gt; 'button']) }}&gt;
    {{ $slot }}
&lt;/button&gt;</code></pre>



<p>當調用端 x-button 沒有指定 type，最後產出的 html button type=&#8221;button&#8221;。<br>當調用端 x-button 有指定 type，最後產出的 html button type=調用端指定的值。<br>所以非 css class 的屬性，$attributes-&gt;merge 的行為是「覆寫」，而非「合併」</p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">Slot</h4>



<p>除了上段討論的 $attributes 變數，component 也預設了 $slot 變數。如果 component view 是</p>



<pre class="wp-block-code"><code>&lt;span class="alert-title"&gt;{{ $title }}&lt;/span&gt;

&lt;div class="alert alert-danger"&gt;
    {{ $slot }}
&lt;/div&gt;</code></pre>



<p>調用端 x-alert 中間夾的內容，會自動填到 $slot 變數中。但 component view 還有一個 $title，此時調用端就要新增一個 &lt;x-slot name=&#8221;title&#8221;&gt;，中間夾的內容會填入 $title (這個地方 title 也是一個 slot)</p>



<pre class="wp-block-code"><code>&lt;x-alert&gt;
    &lt;x-slot name="title"&gt;
        Server Error
    &lt;/x-slot&gt;

    &lt;strong&gt;Whoops!&lt;/strong&gt; Something went wrong!
&lt;/x-alert&gt;</code></pre>



<p>slot 可以看成是一個比較快，不需要 component class，又可以將資料傳到 component 的機制。</p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">Anonymous Component</h4>



<p>anonymous component 不需要 component class，在 resources/views/components 新增一支 view template 檔案，調用端就可使用了 &lt;x-your-component/&gt;</p>



<p>resources/views/components 可以有檔案結構，調用端檔案上下層以 dot (.) 分隔，例如 &lt;x-forms.input/&gt;，這些都跟 class based component 相同。</p>



<p>因為沒有 component class，還是要有一個機制可以傳入指定變數。anonymous component 使用 @props directive，其中並可指定變數的預設值，例如下面 type 的預設值是 &#8220;info&#8221;</p>



<pre class="wp-block-code"><code>@props(&#91;'type' =&gt; 'info', 'message'])

&lt;div {{ $attributes-&gt;merge(&#91;'class' =&gt; 'alert alert-'.$type]) }}&gt;
    {{ $message }}
&lt;/div&gt;</code></pre>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">使用 Component 做版面 layout</h4>



<p>Laravel 演進到現在 (Laravel 8)，其實是鼓勵開發者用 component 做版面 layout，而不是用傳統的 @extends, @yield, @section 等。</p>



<p>用 component 做版面 layout，主要是上面提到的 slot 機制。例如 resources/views/components/layout.blade.php</p>



<pre class="wp-block-code"><code>&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;{{ $title ?? 'Todo Manager' }}&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Todos&lt;/h1&gt;
        &lt;hr/&gt;
        {{ $slot }}
    &lt;/body&gt;
&lt;/html&gt;</code></pre>



<p>child view：resources/views/tasks.blade.php。其中 &lt;x-layout&gt; 中整段會填到 $slot，也注意到 title slot 的寫法，可以傳入 $title 變數。</p>



<pre class="wp-block-code"><code>&lt;x-layout&gt;
    &lt;x-slot name="title"&gt;
        Custom Title
    &lt;/x-slot&gt;

    @foreach ($tasks as $task)
        {{ $task }}
    @endforeach
&lt;/x-layout&gt;</code></pre>



<p>相較於傳統的 @extends, @yield, @section 佈局方式</p>



<ul class="wp-block-list">
<li>child view 的角色是 component 的調用端</li>



<li>master view 的角色是 component 本身</li>
</ul>



<p>如果對 design pattern 有了解的讀者，傳統的佈局方式跟 component based 的佈局方式，是類似的 design pattern，所以使用上邏輯是很像的。</p>



<p>也可以參考我 <a rel="noreferrer noopener" href="https://blog.yuyansoftware.com.tw/category/software-engineering-philosophy/design-pattern/" target="_blank">Design Pattern 系列的文章</a></p>



<div style="height:1px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">結論</h4>



<p>本文主要是希望能勾勒出 Laravel 8 component 的使用，與使用 component 做版面 layout 的方式。沒有談太多細節、或變化型的使用，很多細節的部分應該是由開發者自行斟酌使用。Laravel 前端目前看起來是往 component based 的方向在走，習慣於 @extends, @yield, @section 佈局方式的開發者，可以考慮使用 component。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
