<?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>Laravel &#8211; Larry的午茶時光</title>
	<atom:link href="https://blog.yuyansoftware.com.tw/category/php/laravel/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.yuyansoftware.com.tw</link>
	<description></description>
	<lastBuildDate>Mon, 14 Apr 2025 03:56:51 +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>Laravel &#8211; Larry的午茶時光</title>
	<link>https://blog.yuyansoftware.com.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Laravel 11 發布與新功能！最低要求 PHP 8.2，檔案結構已大改，Laravel Reverb 套件支援 WebSocket</title>
		<link>https://blog.yuyansoftware.com.tw/2024/03/laravel-11-release-new-features/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 24 Mar 2024 09:00:46 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[新功能]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=19317</guid>

					<description><![CDATA[前幾天 (2024年3月中) Laravel 11 發布了。包含最低要求 PHP 8.2，整個程式架構的大幅度重構，新的 Laravel Reverb 套件支援 WebSocket，以及 attribute casting 的再次更改。]]></description>
										<content:encoded><![CDATA[
<p><a href="https://laravel-news.com/laravel-11" target="_blank" rel="noreferrer noopener">https://laravel-news.com/laravel-11</a><br>前幾天 (2024年3月中) Laravel 11 發布了。要先提醒讀者的是，Laravel 11 是一個超大的改版，要不要使用 Laravel 11 要再三思。</p>



<p>以下是 Laravel 11 的一些主要更新和新功能：</p>



<h2 class="wp-block-heading">PHP 8.2 是 Laravel 11 最低的 PHP 版本要求</h2>



<p>Laravel 目前的策略看起來是跟著 PHP 的最新版本。Laravel 9 要求 PHP 8.0，Laravel 10 要求 PHP 8.1，Laravel 11 要求 PHP 8.2。</p>



<p>但這個策略會有一個副作用：系統廠商跟開發者不可能隨時都去更新 PHP 版本，設限一個這麼高的 PHP 版本，反而會限制新版 Laravel 的普及。</p>



<h2 class="wp-block-heading">Streamlined Directory Structure (簡化以及提高效率)</h2>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">bootstrap/app.php</mark></code> 檔案重寫，而且專案層級的設定會寫在這裡 (Laravel 10 以前這個檔案根本不會打開)。</p>



<p>一些專案層級的 routing, middleware, service providers, exception handling 等，都在這個檔案設定，而不是散落在專案四處。</p>



<p>Laravel 10 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Providers</mark></code> 底下有 5 個 providers，Laravel 11 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Providers</mark></code> 底下只有 AppServiceProvider。之前其他 service provider 的功能整合到 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">bootstrap/app.php</mark></code>，或是直接在 AppServiceProvider 中設定。</p>



<p>舉例來說，auth policy 之前是註冊在 AuthServiceProvider，Laravel 11 是註冊在 AppServiceProvider。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">routes/api.php</mark></code>，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">routes/channels.php</mark></code> 已被移除，因為很多應用並沒有用到這些檔案。當然，可以用 artisan 指令新增回來。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Http/Middleware</mark></code> 整個資料夾不見了！現在都在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">bootstrap/app.php</mark></code> 中設定 (withMiddleware closure)。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Http/Kernel.php</mark></code> 也移除了！因為它的功能本來就是管理 middleware，現在 Middleware 整個資料夾都不見了，當然也不需要 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Kernel.php</mark></code> 了。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Console</mark></code> 整個資料夾也不見了！scheduled tasks 直接寫在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">routes/console.php</mark></code> (它預設還在)。如果是用 artisan 指令新增 command，仍然會在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Console/Commands</mark></code> 底下。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Exceptions</mark></code> 整個資料夾也不見了！exception handling 的設定就直接寫在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">bootstrap/app.php</mark></code>。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Http/Controllers/Controller.php</mark></code> 變成一個空的 abstract class，不再 extends 任何東西。</p>



<h2 class="wp-block-heading">預設的 database 變成 SQLite (而不是 MySQL)</h2>



<p><strong>SQLite</strong> database 其實就是一個檔案，當然比 MySQL 輕量、跨平台，以及更低的軟體相依性。但目前 MySQL 還是最多人使用的 database，<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">使用 MySQL 的開發者要記得 DB 設定要切到 MySQL。</mark></p>



<h2 class="wp-block-heading">新增 Laravel Reverb 套件支援 WebSocket</h2>



<p><strong>Laravel Reverb</strong> 是一個支援 WebSocket 的套件。傳統 HTTP 是一問一答，但如果 client 不問，並不會知道主機資料已經變化。所以傳統是用「輪詢」(Polling) 的方式，例如每幾秒、每幾分鐘問一次主機。</p>



<p>經由 WebSocket 後主機可以主動通知 client：主機資料已經改變了。實際應用場景包含訊息推播、即時訊息更新，等等。client 端不需詢問主機或重整網頁。</p>



<h2 class="wp-block-heading">Per-second rate limiting</h2>



<p>之前 rate limiting 是寫在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">App\Providers\RouteServiceProvider</mark></code>，而且只能以「每分鐘」為單位來限制。</p>



<p>Laravel 11 沒有 RouteServiceProvider，所以是寫在 AppServiceProvider，而且可以「每秒鐘」為單位來限制。<br><a href="https://laravel.com/docs/11.x/routing#rate-limiting" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/11.x/routing#rate-limiting</a></p>



<h2 class="wp-block-heading">Health routing</h2>



<p>定義在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">bootstrap/app.php</mark></code>，有一個 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">/up</mark></code>，方便第三方 health check 軟體，例如 Kubernetes 來使用。當 request <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">/up</mark></code> 時，Laravel 會發一個 DiagnosingHealth event，開發者可以接這個 event，再做一些確認健康動作。</p>



<h2 class="wp-block-heading">Encryption Key Rotation</h2>



<p>Laravel 會加密所有 cookie，包含 session cookie。Laravel 11 新增一個環境變數 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_PREVIOUS_KEYS</mark></code>。開發者可以將之前用來加密的 key (多筆)，以逗號分隔的方式，存在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_PREVIOUS_KEYS</mark></code>。</p>



<p>Laravel 仍會用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_KEY</mark></code> 加密，解密時仍會先用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_KEY</mark></code>，不行時再從 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_PREVIOUS_KEYS</mark></code> 中一一嘗試。</p>



<p>這樣開發者就可以更改 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_KEY</mark></code>，並將舊的 key 放在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">APP_PREVIOUS_KEYS</mark></code>，而不影響所有使用者操作。</p>



<h2 class="wp-block-heading">Automatic Password Rehashing</h2>



<p>Laravel 是以 bcrypt 加密密碼，其中有一個參數 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">BCRYPT_ROUNDS</mark></code>，在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">config/hashing.php</mark></code> 中使用。當開發者重新設定環境變數 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">BCRYPT_ROUNDS</mark></code> 後，當使用者登入或 auth，Laravel 11 會 re-hash 使用者密碼。</p>



<h2 class="wp-block-heading">Prompt validator</h2>



<p><strong>Laravel Prompts</strong> 套件在 Laravel 10.17 加入。用 artisan commands 的方式可以撰寫一些客製的指令以及回覆。</p>



<p>其中可以檢查 console 的輸入值是否 valid。Laravel 11 開始可以使用 validator (request validate 的那種寫法)，來檢查 console 的輸入值。</p>



<h2 class="wp-block-heading">Queue interaction testing</h2>



<p>之前如果要對 <strong>queued jobs</strong> 做單元測試，是非常麻煩的。Laravel 11 開始 queued job 可以生成一個 fake instance，直接在主執行緒上做測試以及 assert。</p>



<h2 class="wp-block-heading">New artisan commands</h2>



<p>這幾個指令終於加到了 artisan command</p>



<pre class="wp-block-code"><code>php artisan make:class
php artisan make:enum
php artisan make:interface
php artisan make:trait
</code></pre>



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



<p>之前是用 property 來設定 casts，Laravel 11 開始請用 method 來設定 casts，這樣在 casts function 裡還可以做複雜的變化 (call 其他 methods)。</p>



<p><a href="https://laravel-news.com/laravel-11" target="_blank" rel="noreferrer noopener">https://laravel-news.com/laravel-11</a> 的範例</p>



<pre class="wp-block-code"><code>protected function casts(): array
{
    return &#91;
        'email_verified_at' =&gt; 'datetime',
        'password' =&gt; 'hashed',
        'options'=› AsEnumCollection::of(UserOption::class),
    ];
}
</code></pre>



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



<p>以上是 Laravel 11 幾個重要的更新。當然在本文上述之外，還有其他的更新。</p>



<p>Laravel 10 之前的更新基本上都是新增功能，或是某個模組的翻新。Laravel 11 則是超大幅度的程式重構，當然寫法也都完全不同。</p>



<p><a href="https://laravel.com/docs/10.x/reverb" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/10.x/reverb</a><br>另外，新的 Laravel Reverb 套件(支援 WebSocket)，需要 Laravel 10.47 以上，以及 PHP 8.2 以上。</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Laravel 11 本身有超大幅度的程式重構，主機環境也需要 PHP 8.2 以上。</mark>開發者如果要使用 Laravel 11，要想清楚喔。</p>
]]></content:encoded>
					
		
		
			</item>
		<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 10 的新功能：最低要求 PHP 8.1，Laravel Pennant 實現 feature flag，Process facade 執行外部程式</title>
		<link>https://blog.yuyansoftware.com.tw/2023/02/laravel-10-release-new-features/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 26 Feb 2023 09:29:55 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[新功能]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=14267</guid>

					<description><![CDATA[前幾天 (2023年2月中) Laravel 10 發佈了。包含最低要求 PHP 8.1，新的 Laravel Pennant 套件和 Process facade，本篇來聊一聊 Laravel 10 的主要更新和新功能。]]></description>
										<content:encoded><![CDATA[
<p><a href="https://laravel-news.com/laravel-10" target="_blank" rel="noreferrer noopener nofollow">https://laravel-news.com/laravel-10</a><br>前幾天 (2023年2月中) Laravel 10 發佈了。</p>



<p>我之前的文章 <a href="https://blog.yuyansoftware.com.tw/2022/05/laravel-9-release-new-features/">Laravel 9 的新功能</a> 有提到，從 Laravel 9 開始，major version 每一年更新一次，目前是訂在每年的二月更新 major version。</p>



<p>以下是 Laravel 10 的一些主要更新和新功能：</p>



<h2 class="wp-block-heading">PHP 8.1 是 Laravel 10 最低的 PHP 版本要求</h2>



<p>PHP 8.0 不行喔，檢查你的主機環境是不是 PHP 8.1 以上，才有辦法跑 Laravel 10。</p>



<h2 class="wp-block-heading">新的 Laravel Pennant 套件</h2>



<p>Laravel 10 發佈了新的 Laravel Pennant 套件。Laravel Pennant 的邏輯是，先定義一個使用者的條件，也許他是 beta tester，也許他是公司中某些指定的用戶。</p>



<p>這就是軟體工程的 <strong>Feature Flag</strong>。在「正式站」中上線多個程式碼分支，只有預先定義的部分用戶，可以使用新功能以及做最終測試。</p>



<p>參考官方文件 <a href="https://laravel.com/docs/10.x/pennant" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/10.x/pennant</a></p>



<p>來自上方文件的 sample code，你可以在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">AppServiceProvider</mark></code> 定義使用者的資格</p>



<pre class="wp-block-code"><code>class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Feature::define('new-api', fn(User $user) =&gt; match (true) {
            $user-&gt;isInternalTeamMember() =&gt; true,
            $user-&gt;isHighTrafficCustomer() =&gt; false,
            default =&gt; Lottery::odds(1 / 100),
        });
    }
}
</code></pre>



<p>‘new-api’ 字串是你定義的 feature name。<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">larry 很喜歡這一段 sample code，是因為它同時用了 PHP 7.4 的 arrow function，和 PHP 8 的 match expression。</mark></p>



<p>字面上看，如果用戶是內部 team member，或「不是」高流量的客戶，都給這個用戶使用 ‘new-api’。如果用戶「不是」內部 team member，又使用了高流量，就會 return default: 抽籤決定這個用戶可不可以使用 ‘new-api’。</p>



<p>這段 sample code 寫得很漂亮，很有意思。</p>



<p>上面 ‘new-api’ 是字串，也可以改為使用 class based 寫法</p>



<pre class="wp-block-code"><code>namespace App\Features;
  
class NewApi
{
    /**
     * Resolve the feature's initial value.
     */
    public function resolve(User $user): mixed
    {
        return match (true) {
            $user-&gt;isInternalTeamMember() =&gt; true,
            $user-&gt;isHighTrafficCustomer() =&gt; false,
            default =&gt; Lottery::odds(1 / 100),
        };
    }
}
</code></pre>



<p>你在 controller 裡就可以用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Feature::active</mark></code> 或 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Feature::when</mark></code> 去確認使用者資格，並做相對應的動作</p>



<pre class="wp-block-code"><code>public function index(Request $request): Response
{
        return Feature::active(NewApi::class)
                ? $this-&gt;resolveNewApiResponse($request)
                : $this-&gt;resolveLegacyApiResponse($request);
}

public function index(Request $request): Response
{
        return Feature::when(NewApi::class,
            fn() =&gt; $this-&gt;resolveNewApiResponse($request),
            fn() =&gt; $this-&gt;resolveLegacyApiResponse($request),
        );
}
</code></pre>



<p>以上大概就是 Laravel Pennant 的邏輯。不用這個套件其實本來就可以寫這樣的效果，只是&nbsp;Laravel Pennant 把這個需求的寫法架構化了。</p>



<h2 class="wp-block-heading">新的 Process facade</h2>



<p><a rel="noreferrer noopener" href="https://laravel.com/docs/10.x/processes" target="_blank">https://laravel.com/docs/10.x/processes</a><br>新的 Process facade 讓開發者可以輕易的執行 Laravel 專案外，也就是作業系統層級的指令。例如官方文件的 sample code</p>



<pre class="wp-block-code"><code>use Illuminate\Support\Facades\Process;
 
$result = Process::run('ls -la');
// run function 是同步執行，也就是有可能會 hang 住 
return $result-&gt;output();
</code></pre>



<p>也可以指定要下命令的資料夾路徑</p>



<pre class="wp-block-code"><code>$result = Process::path(__DIR__)-&gt;run('ls -la');</code></pre>



<p>上面 <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color"><code>run</code></mark> function 是同步執行 (sync) 外部指令，也就是執行的當下有可能會 hang 住。而 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">start</mark></code> function 是非同步執行 (async)</p>



<pre class="wp-block-code"><code>$process = Process::timeout(120)-&gt;start('bash import.sh');
// 執行 120 秒沒完成的話，throw timeout exception 

while ($process-&gt;running()) {
    // ...
}
 
$result = $process-&gt;wait();
// 這一行會等 process 執行完 (有可能會 hang 住)
// 拿到 result
</code></pre>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">如果讀者是資深的軟體開發者，尤其是當年是 C++ 出身的，應該有感，這就是 multi-thread (多執行緒) 的概念啊。</mark></p>



<p>Process facade 也包含了類似 thread pool 的功能。</p>



<pre class="wp-block-code"><code>use Illuminate\Process\Pool;
use Illuminate\Support\Facades\Process;
 
$pool = Process::pool(function (Pool $pool) {
    $pool-&gt;path(__DIR__)-&gt;command('bash import-1.sh');
    $pool-&gt;path(__DIR__)-&gt;command('bash import-2.sh');
    $pool-&gt;path(__DIR__)-&gt;command('bash import-3.sh');
})-&gt;start(function (string $type, string $output, int $key) {
    // ...
});

// 執行 import-1.sh, import-2.sh, import-3.sh
 
while ($pool-&gt;running()-&gt;isNotEmpty()) {
    // ...
}
 
$results = $pool-&gt;wait();
</code></pre>



<p>講到 multi-thread、thread pool，或是 process pool，真是讓我想起一些回憶，也許這就是寫部落格的好處之一吧，哈哈。</p>



<p>延伸閱讀，我2013年1月的文章：<a rel="noreferrer noopener" href="https://blog.yuyansoftware.com.tw/2013/01/multi-thread/" target="_blank">Multi-thread 多執行緒程式設計</a></p>



<h2 class="wp-block-heading">Laravel framework 整個更新標示 function parameter type &amp; return type</h2>



<p>將現代 PHP feature 帶入 Laravel framework 中，明確的標示&nbsp;function parameter type &amp; return type。</p>



<h2 class="wp-block-heading">Invokable validation rule 指令調整</h2>



<pre class="wp-block-code"><code>// Laravel 9 產生 Rule Object with __invoke function
artisan make:rule Uppercase --invokable

// Laravel 10 做一樣的事，不用 --invokable flag
artisan make:rule Uppercase
</code></pre>



<h2 class="wp-block-heading">新的 test 指令選項，顯示測試執行秒數</h2>



<pre class="wp-block-code"><code>php artisan test --profile</code></pre>



<p>上面指令會執行測試，並將每個測試的執行時間，由慢到快排序出來。開發者可以輕易找到，跑得過慢的測試。</p>



<h2 class="wp-block-heading">新的 Str::password helper function</h2>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Str::password()</mark></code> 預設會產生 32 位長度的英文、數字、符號、空白，混合字串。輸入參數也可以指定字串長度。</p>



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



<p>這次 Laravel 10 主要是 Laravel Pennant 和 Process facade 的導入。Laravel Pennant 功能上 larry 覺得還好，相同功能本來就可以用其他方式寫。倒是官方文件 sample code 同時用了 PHP 7.4 的 arrow function，和 PHP 8 的 match expression，這個寫法是很漂亮的。</p>



<p>Process facade 其實就是 multi-thread 程式設計。sync、async，甚至 thread pool 都實作出來了。如果開發者需要執行 Laravel 專案外，也就是作業系統層級的指令，應該是更方便了。</p>



<p>延伸閱讀：<a href="https://blog.yuyansoftware.com.tw/2013/01/multi-thread/">Multi-thread 多執行緒程式設計</a></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 9 的新功能：最低要求 PHP 8.0，底層更新為 Symfony 6.0，開始使用 PHP 8.1 的 Enum</title>
		<link>https://blog.yuyansoftware.com.tw/2022/05/laravel-9-release-new-features/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Fri, 06 May 2022 08:48:56 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[新功能]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=10345</guid>

					<description><![CDATA[今年 (2022) 二月 Laravel 9 發佈了，從 Laravel 9 開始 major version 每年更新一次。Laravel 9 底層更新為 Symfony 6.0，最低要求 PHP 8.0，並開始使用 PHP 8.1 的 Enum。]]></description>
										<content:encoded><![CDATA[
<p><a rel="noreferrer noopener" href="https://laravel-news.com/laravel-9" target="_blank">https://laravel-news.com/laravel-9</a><br>今年 (2022) 二月 Laravel 9 發佈了。</p>



<p>從 Laravel 9 開始，major version 每一年更新一次。因為 Laravel 底層有使用 Symfony Framework 的元件，而且 Symfony 每年的 11 月會更新一次，所以 Laravel 目前是訂在隔年的二月更新 major version (這樣可以使用最新的 Symfony 版本)。像這次發布的 Laravel 9 底層是使用 Symfony 6.0。</p>



<p>以下列出 Laravel 9 的一些主要更新和新功能：</p>



<h2 class="wp-block-heading">PHP 8.0 是 Laravel 9 最低的 PHP 版本要求</h2>



<p>這是最根本的前提條件，檢查你的環境是不是 PHP 8.0 以上，才有辦法跑 Laravel 9。</p>



<h2 class="wp-block-heading">route:list 指令顯示結果的重新設計</h2>



<pre class="wp-block-code"><code>php artisan route:list</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">route:list</mark></code> 指令存在已久，如果 route 數量多而複雜，以前的顯示介面會有點混亂。Laravel 9 重新設計了 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">route:list</mark></code> 指令的顯示結果。</p>



<h2 class="wp-block-heading">新的 test 指令選項，顯示測試覆蓋率</h2>



<pre class="wp-block-code"><code>php artisan test --coverage</code></pre>



<p>Laravel 的測試是基於 PHPUnit，Laravel 的測試功能存在已經非常久。上面指令會執行測試，並顯示每支程式的測試覆蓋率。</p>



<h2 class="wp-block-heading">DB 預設使用 Anonymous Migration</h2>



<p>Laravel 的 database migration，以前是要自己取 migration class name。但如果因需求變更，刪除再新增同一 DB table，或是多次修改同一 DB table，取 migration class name 就會變成一件麻煩事，migrate 起來的行為也不見得會跟你的邏輯順序相同。</p>



<p>Anonymous Migration 是 Laravel 8 後期推出的功能，Laravel 9 變成專案預設，以後再也不用取 migration class name 了。</p>



<h2 class="wp-block-heading">新的 string functions</h2>



<p>因為 Laravel 9 最低要求 PHP 8.0。PHP 8 新的 string functions: <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">str_contains</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">str_starts_with</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">str_ends_with</mark></code> 也被引入，包裝成 Laravel 9 string functions: <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Str::contains</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Str::startsWith</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">Str::endsWith</mark></code></p>



<h2 class="wp-block-heading">寄信件模組從 Swift Mailer 更新為 Symfony Mailer</h2>



<p>Swift Mailer 為 Symfony Framework 的寄件元件，隨著 2021 年 11 月 Symfony 6.0 的推出，改為使用 Symfony Mailer，並停止維護 Swift Mailer。Laravel 9 也隨之改為使用 Symfony Mailer。</p>



<h2 class="wp-block-heading">Eloquent Accessor / Mutator 有新的寫法</h2>



<p>Laravel 8 以前的 Accessor / Mutator，例如你要存取一個 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>public function getFirstNameAttribute($value)
{
    return strtoupper($value);
}
 
public function setFirstNameAttribute($value)
{
    $this-&gt;attributes&#91;'name'] = $value;
}</code></pre>



<p>Laravel 9 改成</p>



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

// 注意有 return type
public function firstName(): Attribute
{
    // 官方文件是寫 Attribute::make(...
    // in this case, new 或 make 都可以
    return new Attribute(
        get: fn ($value) =&gt; strtoupper($value),
        set: fn ($value) =&gt; $value,
    );

    // 如果你是要 chain methods, 則是要用 Attribute::make(...
    // 例如
    /*
    return Attribute::make(
       ...
    )-&gt;shouldCache();
    */
}</code></pre>



<h2 class="wp-block-heading">用 PHP 8.1 的 enum 做 implicit route binding</h2>



<p><a rel="noreferrer noopener" href="https://laravel.com/docs/9.x/routing#implicit-enum-binding" target="_blank">https://laravel.com/docs/9.x/routing#implicit-enum-binding</a><br>官方文件的 sample code</p>



<pre class="wp-block-code"><code>// Category 為 enum
Route::get('/categories/{category}', function (Category $category) {
    return $category-&gt;value;
});</code></pre>



<p>可以更漂亮的限制哪些參數是 route 可以允許的, 如果是不允許的參數, 直接 return 404.</p>



<h2 class="wp-block-heading">用 Route::controller 將同一 controller 不同 route 更好的組織起來</h2>



<pre class="wp-block-code"><code>Route::controller(YourController::class)-&gt;group(function () {
    …. // route 1
    …. // route 2
});</code></pre>



<p>不用再重複寫 ControllerName，組織上，程式的簡潔度上都比較好。</p>



<h2 class="wp-block-heading">用 PHP 8.1 的 enum 做 Eloquent Attribute Casting</h2>



<p>官方文件的 sample code</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> 不管是存，還是取，都會直接 cast 成一個 enum object.</p>



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



<p><a rel="noreferrer noopener" href="https://laravel.com/docs/9.x/eloquent-mutators" target="_blank">https://laravel.com/docs/9.x/eloquent-mutators</a><br>包含上方的 Accessor / Mutator，以及這邊的 Casting，他們都屬於同一份文件，Laravel 9 在這份文件做了不少更新。</p>



<h2 class="wp-block-heading">Route parameter scoping</h2>



<p><a href="https://laravel.com/docs/9.x/routing#implicit-model-binding-scoping" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/9.x/routing#implicit-model-binding-scoping</a><br>官方文件的 sample code</p>



<pre class="wp-block-code"><code>Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
});</code></pre>



<p>如果是這種格式的 route，而且使用 custom key (:slug的部分)。Laravel 會自動 scope 該 user 底下的 posts.</p>



<p>Laravel 9 開始，如果沒使用 custom key，同樣也可以 scope child binding</p>



<pre class="wp-block-code"><code>Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
    return $post;
})-&gt;scopeBindings();</code></pre>



<p>這樣寫法好處是更漂亮、更簡潔的檢查第一、二個參數的 relation.</p>



<h2 class="wp-block-heading">Full Text Index &amp; query by whereFullText</h2>



<p>Laravel 9 開始, 如果使用 MySQL 或 PostgreSQL，可以使用 fullText method 做整段的 index。官方文件的 sample code</p>



<pre class="wp-block-code"><code>$table-&gt;text('bio')-&gt;fullText();</code></pre>



<p>當 query 時，如果有 full text index 的欄位，可以使用兩個 method：whereFullText、orWhereFullText。官方文件的 sample code</p>



<pre class="wp-block-code"><code>$users = DB::table('users')
           -&gt;whereFullText('bio', 'web developer')
           -&gt;get();
</code></pre>



<h2 class="wp-block-heading">Bootstrap 5 Pagination Views</h2>



<p>Laravel 9 原生支援 Bootstrap 5 Pagination。在 AppServiceProvider, boot method, call</p>



<pre class="wp-block-code"><code>Paginator::useBootstrapFive();</code></pre>



<p>即可使用 Bootstrap 5 Pagination View。</p>



<h2 class="wp-block-heading">重新設計的 debug page</h2>



<p>Laravel debug page 使用的是 Spatie Ignition，也重新設計了。</p>



<h2 class="wp-block-heading">2 個新的 helper functions: str &amp; to_route</h2>



<p>str() 就是之前的 Str::of()，可以做一些字串的操作。</p>



<p><a rel="noreferrer noopener" href="https://laravel.com/docs/9.x/redirects#redirecting-named-routes" target="_blank">https://laravel.com/docs/9.x/redirects#redirecting-named-routes</a><br>之前&nbsp; Redirect to Named Route 的 sample code</p>



<pre class="wp-block-code"><code>return redirect()-&gt;route('profile', &#91;'id' =&gt; 1]);</code></pre>



<p>Laravel 9 新寫法的 sample code</p>



<pre class="wp-block-code"><code>return to_route('users.show', &#91;'user' =&gt; 1]);</code></pre>



<p>甚至可以指定 HTTP status code 和 response header</p>



<pre class="wp-block-code"><code>return to_route('users.show', &#91;'user' =&gt; 1], 302, &#91;'X-Framework' =&gt; 'Laravel']);</code></pre>



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



<p>這次 Laravel 9 主要是底層語言與模組的更新，程式的寫法上更加簡潔漂亮。另外，如果你的環境是 PHP 8.1 以上，PHP 8.1 enum 的用法也可以參考一下。</p>



<p>這次 Laravel 的更新，可能對有軟體工程基礎，對 Laravel 又有經驗的讀者，會比較有參考價值。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>初探 Alpine.js — Laravel 預設的前端框架從 Vue.js 改為使用 Alpine.js</title>
		<link>https://blog.yuyansoftware.com.tw/2021/05/alpine-js-frontend-framework/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 30 May 2021 14:19:26 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[前端工程]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=7856</guid>

					<description><![CDATA[從 Laravel 5 預設是 Vue.js，到 vue 只是一個選項，到 Laravel 8 預設使用 Alpine.js。就 Laravel 的態度而言，整個趨勢是從 Vue.js 轉到 Alpine.js。本篇我們就來聊一下 Alpine.js]]></description>
										<content:encoded><![CDATA[
<p>這幾年到目前 (2021年5月) 為止，最受歡迎的 JavaScript framework 主要還是三大家：Angular, React, Vue.js。</p>



<p>Laravel 從 5.x 版開始用 Vue.js 當預設的前端框架，Laravel 5 的那一段時間，開發者數量成長很快，相信也間接推動了 Vue.js 的普及。</p>



<p>接下來 Laravel 6 和 7 是使用一個 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">laravel/ui</mark></code> 套件來建構前端，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">laravel/ui</mark></code> 建構時可以選擇 bootstrap, vue, react 三選一。納入更多選擇，也可以解讀為 Laravel 漸漸脫離 Vue.js 的相依性。</p>



<p>目前 (2021年5月) Laravel 8 則是用一個 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">laravel/breeze</mark></code> 套件來建構前端，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">laravel/breeze</mark></code> 預設是使用 Alpine.js，但也可以指定要用 vue 或 react。</p>



<p>所以可以看出來，從 Laravel 5 預設是 Vue.js，到 vue 只是一個選項，到 Laravel 8 預設使用 Alpine.js。就 Laravel 的態度而言，整個趨勢是從 Vue.js 轉到 Alpine.js。</p>



<p>本篇我們就來聊一下 Alpine.js</p>



<p>Alpine.js 是一個相對非常年輕的前端框架，1.0 版是 2019 年 12 月釋出。作者是 Caleb Porzio，他是 Laravel 開發者和 Laravel 框架本身的開發者。所以他當時也是用 Vue.js，後來才創作了 Laravel Livewire 及 Alpine.js。</p>



<p>因爲作者 Caleb Porzio 的背景是 Laravel 開發者，想要簡化前端的工作，所以 Alpine.js 的創作精神就是輕量、簡化。Alpine.js 的檔案size很小，安裝及使用簡單，不會因為簡單功能就需要寫一堆 JS code，更沒有編譯 TypeScript 等事情。</p>



<p>Alpine.js 官方網頁 <a rel="noreferrer noopener" href="https://github.com/alpinejs/alpine" target="_blank">https://github.com/alpinejs/alpine</a></p>



<p>Alpine.js 的使用首先用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-data</mark></code> 宣告一個 component scope。<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-data</mark></code> 裡宣告的變數，在這個 component scope 裡都能存取。</p>



<pre class="wp-block-code"><code>&lt;div x-data="{ foo: 'bar' }"&gt;
...
&lt;/div&gt;</code></pre>



<p>用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-text</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-html</mark></code> 可以將變數填入DOM。<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-html</mark></code> 因為內容可以是 html，沒有 escape，所以使用者編輯的內容，不要使用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-html</mark></code></p>



<pre class="wp-block-code"><code>&lt;span x-text="foo"&gt;&lt;/span&gt;
&lt;span x-html="foo"&gt;&lt;/span&gt;
</code></pre>



<p>使用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-on:[event]</mark></code> 可以設定 event handler，例如 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-on:click</mark></code>，也可以縮寫成 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">@click</mark></code></p>



<pre class="wp-block-code"><code>&lt;button x-on:click="foo = 'bar'"&gt;&lt;/button&gt;
// 縮寫
&lt;button @click="foo = 'bar'"&gt;&lt;/button&gt;
</code></pre>



<p>使用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-model</mark></code> 可以雙向綁定 DOM 上顯示的值和 component scope 裡變數的值</p>



<pre class="wp-block-code"><code>&lt;input type="text" x-model="foo"&gt;</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-if</mark></code> 可以達成 if else 的功能，注意 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-if</mark></code> 需要搭配 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;template&gt;</mark></code> 使用。官方文件有說，因為 <strong>Alpine.js 不使用虛擬 DOM (virtual DOM)，而是使用真實的 DOM (real DOM)</strong>，使用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;template&gt;</mark></code> 以達成輕量、簡化的效果。</p>



<p>另外注意 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-if</mark></code> 和 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-show</mark></code> 的差別，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-if</mark></code> 判斷式是 false 時，整個 DOM 都會拿掉。<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-show</mark></code> 判斷式是 false 時，該 DOM 還會在，只是 css 設成 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">display: none</mark></code></p>



<pre class="wp-block-code"><code>&lt;template x-if="foo"&gt;
	&lt;div&gt;Some Element&lt;/div&gt;
&lt;/template&gt;
</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-for</mark></code> 可以達成迴圈的功能，注意 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-for</mark></code> 也需要搭配 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;template&gt;</mark></code> 使用。如果 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-for</mark></code> 中的 array 資料有可能增減、變換順序，記得要指定 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">:key</mark></code>。照官方文件的說法，每次 iterate 都會標記一個 key，這樣 array 資料有增減、變換順序時，DOM 的變化才會正確。</p>



<pre class="wp-block-code"><code>&lt;template x-for="item in items" :key="item.id"&gt;
    &lt;div x-text="item.content"&gt;&lt;/div&gt;
&lt;/template&gt;

// 如果迴圈中要取用 index, 這樣寫. 而且 index 也可以當 key
&lt;template x-for="(item, index) in items" :key="index"&gt;
    &lt;div x-text="item.content"&gt;&lt;/div&gt;
&lt;/template&gt;
</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-show</mark></code> 與上方 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-if</mark></code> 比較，保留了 DOM，只是 css 設成 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">display: none</mark></code>。直接使用在 DOM 上，不需要 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">&lt;template&gt;</mark></code>。另外可以使用 transition 來讓 show/hide 更流暢。</p>



<pre class="wp-block-code"><code>/* x-show.transition.in, x-show.transition.out, x-show.transition.scale, x-show.transition.duration …
*/
&lt;div x-show.transition="open"&gt;
    These contents will be transitioned in and out.
&lt;/div&gt;
</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-bind</mark></code> 可以填入 html 屬性，例如</p>



<pre class="wp-block-code"><code>// 填入圖片來源
&lt;img x-bind:src=”yourImageUrl”&gt;
// 縮寫
&lt;img :src=”yourImageUrl”&gt;

// 指定 input type
&lt;input x-bind:type="inputType"&gt;
// 縮寫
&lt;input :type="inputType"&gt;
</code></pre>



<p>CSS class 的 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-bind</mark></code> 寫法不同</p>



<pre class="wp-block-code"><code>// foo==true 時才會填上 'hidden'
&lt;div x-bind:class="{ 'hidden': foo }"&gt;&lt;/div&gt;
// 縮寫
&lt;div :class="{ 'hidden': foo }"&gt;&lt;/div&gt;
</code></pre>



<p>二元屬性 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">x-bind</mark></code> 的寫法也不同 (二元屬性例如：disabled, readonly, required &#8230;)</p>



<pre class="wp-block-code"><code>// myVar==true 時才會填上 disabled, myVar==false 時整個 disabled 屬性拿掉
&lt;button x-bind:disabled="myVar"&gt;Click me&lt;/button&gt;
// 縮寫
&lt;button :disabled="myVar"&gt;Click me&lt;/button&gt;
</code></pre>



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



<p>本篇文章主要是想聊一下 Alpine.js 的來龍去脈，以及整個 Alpine.js 的運作邏輯，因此並沒有包含太多細節。</p>



<p>跟其他 JavaScript framework 相比，Alpine.js 很大的特點是<strong>不使用虛擬 DOM (virtual DOM)</strong>，一來輕量、簡化；二來不會因為要寫個簡單功能，就要額外再寫一大堆 JS code。</p>



<p>如果讀者是 Laravel 的開發者，可以研究一下 Alpine.js 的使用，因為畢竟目前 (2021年5月) Laravel 8 的前端預設是用 Alpine.js。甚至 larry 覺得，一般應用如果要做簡單的前端 templating，也可以考慮使用 Alpine.js，而不是那些大型框架。</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>
		<item>
		<title>Laravel reset password email 重設密碼</title>
		<link>https://blog.yuyansoftware.com.tw/2017/12/laravel-reset-password-email/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Thu, 21 Dec 2017 08:31:00 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">http://test234.yuyansoftware.com.tw/2017/12/21/laravel-reset-password-email-%e9%87%8d%e7%bd%ae%e5%af%86%e7%a2%bc%e3%80%81%e9%87%8d%e8%a8%ad%e5%af%86%e7%a2%bc/</guid>

					<description><![CDATA[因為每次要做 Laravel 重設密碼的 email 都要去改 Laravel 框架內部的 code, 不是很優，決定花點時間研究什麼是比較正確的做法。]]></description>
										<content:encoded><![CDATA[
<p>圖片來源 <a href="https://github.com/laravel/laravel" target="_blank" rel="noreferrer noopener" aria-label=" (在新分頁中開啟)">https://github.com/laravel/laravel</a></p>



<p><a rel="noreferrer noopener" href="https://laravel.com/docs/5.5/passwords" target="_blank">https://laravel.com/docs/5.5/passwords</a><br>因為每次要做 Laravel 重設密碼的 email 都要去改 Laravel 框架內部的 code, 不是很優，決定花點時間研究什麼是比較正確的做法。</p>



<p>******* 以下是以 Laravel 5.5 而言 *******</p>



<p>app\User 繼承 Illuminate\Foundation\Auth\User, 它實作 Illuminate\Contracts\Auth\CanResetPassword</p>



<p>CanResetPassword 是一個 interface class, 有一個 function&nbsp; sendPasswordResetNotification($token)<br><a rel="noreferrer noopener" aria-label=" (在新分頁中開啟)" href="https://github.com/laravel/framework/blob/5.5/src/Illuminate/Contracts/Auth/CanResetPassword.php" target="_blank">https://github.com/laravel/framework/blob/5.5/src/Illuminate/Contracts/Auth/CanResetPassword.php</a></p>



<p>另 Illuminate\Foundation\Auth\User use trait Illuminate\Auth\Passwords\CanResetPassword<br><a href="https://github.com/laravel/framework/blob/5.5/src/Illuminate/Auth/Passwords/CanResetPassword.php" target="_blank" rel="noreferrer noopener" aria-label=" (在新分頁中開啟)">https://github.com/laravel/framework/blob/5.5/src/Illuminate/Auth/Passwords/CanResetPassword.php</a></p>



<p>Illuminate\Auth\Passwords\CanResetPassword 已經實作好 (我們不用動)</p>



<pre class="wp-block-code"><code>public function sendPasswordResetNotification($token) {
    $this-&gt;notify(new ResetPasswordNotification($token));
}</code></pre>



<p>我們就照官方的命名 ResetPasswordNotification, 新增一個 notification class</p>



<pre class="wp-block-code"><code>php artisan make:notification ResetPasswordNotification</code></pre>



<p>生成的 ResetPasswordNotification 很貼心的已經有 function toMail($notifiable), 把你希望的 mail 內容填在裡面。記得之前傳了一個 token 進來，記得要在 ResetPasswordNotification constructor 去接這 token (dependency injection pattern)</p>



<p>一些通知信的內容寫法 (function toMail 的內容)，可以參考這份文件<br><a rel="noreferrer noopener" href="https://laravel.com/docs/5.5/notifications#mail-notifications" target="_blank">https://laravel.com/docs/5.5/notifications#mail-notifications</a></p>



<p>上方連結提到你也可以用 view 的方式來撰寫信件內容 (larry 個人認為這是較好的方式)</p>



<pre class="wp-block-code"><code>return (new MailMessage)-&gt;view(
        'emails.name', &#91;'invoice' =&gt; $this-&gt;invoice]);</code></pre>



<p>上方連結提到，信件的標題預設是「the class name of the notification formatted to &#8220;title case&#8221;」所以如果 class name 是 ResetPasswordNotification, 預設的信件標題就是 &#8220;Reset Password Notification&#8221;. 信件標題當然要改，設定如下：</p>



<pre class="wp-block-code"><code>return (new MailMessage)
                -&gt;subject('Notification Subject')
                -&gt;line('...');</code></pre>



<p>以上大概就是一個比較好的 reset password email 的寫法 ~</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
