<?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>新功能 &#8211; Larry的午茶時光</title>
	<atom:link href="https://blog.yuyansoftware.com.tw/tag/new-features/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.yuyansoftware.com.tw</link>
	<description></description>
	<lastBuildDate>Thu, 23 Jan 2025 04:11:31 +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>新功能 &#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 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 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>
	</channel>
</rss>
