Ray Lee | 李宗叡
Learn or Die
Published in
25 min readFeb 11, 2021

--

Photo by Mathyas Kurmann on Unsplash

# 版本

Laravel 8.x

# 前言

我喜歡使用 Laravel 開發的感覺, 除了開發快速, 程式碼簡潔且優雅之外, Laravel 框架本身也是一個很好的學習參照物。 本篇主要將官方文件重點整理成 Q&A 的形式呈現, 原子化的概念, 這方式並不適用於每個人, 但若對你有幫助, 我會很開心。

# 目錄

Laravel — 官方文件原子化翻譯 — 目錄

# Driver / Transport Prerequisites

# Mailgun Driver

以下的 Laravel example code 的意思是?

  • Example:
<?php
// config/services.php
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
],
  • Answer: 設定 mail driver mailgun service, 如果不是使用 United States Mailgun region, 可以自定義在 endpoint

# Postmark Driver

以下的 Laravel example code 的意思是?

  • Example:
<?php
composer require wildbit/swiftmailer-postmark
  • Answer: 使用 postmark mail driver 需要安裝的套件

以下的 Laravel example code 的意思是?

  • Example:
<?php
// config/services.php
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
  • Answer: mail driver psotmark 的設定

以下的 Laravel example code 的意思是?

  • Example:
<?php
// config/mail.php
'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
],
  • Answer: 可自定義 postmark mail driver 的 message_stream

# SES Driver

以下的 Laravel example code 的意思是?

  • Example:
composer require aws/aws-sdk-php
  • Answer: 要在 Laravel 使用 AWS 的服務, 如 s3, SES, SQS 時, 需安裝 AWS SDK

以下的 Laravel example code 的意思是?

  • Example:
<?php
// config/services.php
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'options' => [
'ConfigurationSetName' => 'MyConfigurationSet',
'Tags' => [
['Name' => 'foo', 'Value' => 'bar'],
],
],
],
  • Answer: 除了 AWS 預設的 key, secret, region 之外, 還可設置 additional 的 setting

# Generating Mailables

以下的 Laravel example code 的意思是?

  • Example:
php artisan make:mail OrderShipped
  • Answer: 建立一個 mailable class, 會放在 app/Mail

# Writing Mailables

# Configuring The Sender

# Using The from Method

以下的 Laravel example code 的意思是?

  • Example:
<?php
// in mailable class
public function build()
{
return $this->from('example@example.com')
->view('emails.orders.shipped');
}
  • Answer: 在 mailable class 的 build 中, 可以使用 from 來指定由的 from

# Using A Global from address

以下的 Laravel example code 的意思是?

  • Example:
<?php
// config/mail.php
'from' => ['address' => 'example@example.com', 'name' => 'App Name'],
  • Answer: 可在 config/mail.php 定義 global 的 from, 即寄件人信箱, 若需要 ad hoc 可在 mailable class 的 build method 中使用 from() 定義

以下的 Laravel example code 的意思是?

  • Example:
<?php
// config/mail.php
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
  • Answer: 定義 reply_to address 當收到 mail 時, 預設按下 reply 會寄給 FROM address, 若要指定一個跟 FROM address 不同的 reply address, 可在此定義

# Configuring The View

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->view('emails.orders.shipped');
}
  • Answer: 在 mailable class 的 build method 內, 使用 view() 來指定渲染該 mail 的 template

# Plain Text Emails

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->view('emails.orders.shipped')
->text('emails.orders.shipped_plain');
}
  • Answer: 可以同時定義 HTML 以及 plain text version, 當 user 切換為 plain text version 時, 就會顯示該版本

# View Data

# Via Public Properties

以下的 Laravel example code 的意思是?

  • Example:
<?php

namespace App\Mail;

class OrderShipped extends Mailable
{
use Queueable, SerializesModels;

public $order;

public function __construct(Order $order)
{
$this->order = $order;
}

public function build()
{
return $this->view('emails.orders.shipped');
}
}

// on template
<div>
Price: {{ $order->price }}
</div>
  • Answer: 當使用 mailable class 時, 可在 constructor 內定義 property, 此 property 可在 template 中被調用

# Via The with Method:

以下的 Laravel example code 的意思是?

  • Example:
<?php
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;

protected $order;

public function __construct(Order $order)
{
$this->order = $order;
}

public function build()
{
return $this->view('emails.orders.shipped')
->with([
'orderName' => $this->order->name,
'orderPrice' => $this->order->price,
]);
}
}

// template file
<div>
Price: {{ $orderPrice }}
</div>
  • Answer: 如果 mailable 中的 property 使用 protected 或 private 的話, 則必須使用 with() 將 data pass 過去

# Attachments

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->view('emails.orders.shipped')
->attach('/path/to/file', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}
  • Answer: 在 mailable class 的 build method 中, 可以使用 attach() 定義 attachment, arg2 還可以定義檔名以及 mine type

# Attaching Files from Disk

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->view('emails.orders.shipped')
->attachFromStorage('/path/to/file', 'name.pdf', [
'mime' => 'application/pdf'
]);
}
  • Answer: 在 mailable class 的 build method 中, 使用 attachFromStorage, attach filesystem disk 的 file 到 email, arg2 可指定檔名, arg3 可指定 mine type

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->view('emails.orders.shipped')
->attachFromStorageDisk('s3', '/path/to/file');
}
  • Answer: 在 mailable class 的 build method 中, 使用 attachFromStorageDisk, attach filesystem disk 的 file 到 email, arg1 可指定要使用哪一個 disk

# Raw Data Attachment

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->view('emails.orders.shipped')
->attachData($this->pdf, 'name.pdf', [
'mime' => 'application/pdf',
]);
}
  • Answer: attach raw data, 通常是 attach 當次 request 產生的 pdf, 在 memory 中, 並沒有存到 disk 中, arg2 可指定檔名 arg3 可指定 mine type

# Inline Attachments

以下的 Laravel example code 的意思是?

  • Example:
<?php
<body>
Here is an image:

<img src="{{ $message->embed($pathToImage) }}">
</body>
  • Answer: email template 中, 可以使用 $message variable 的 embed method 來實現 inline attachment

# Embedding Raw Data Attachments

以下的 Laravel example code 的意思是?

  • Example:
<?php
<body>
Here is an image from raw data:

<img src="{{ $message->embedData($data, 'example-image.jpg') }}">
</body>
  • Answer: 在 mail template 中, 如果 memory 中已有檔案, 可以使用 embedData method 來實現 inline attachment

# Customizing The SwiftMailer Message

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
$this->view('emails.orders.shipped');

$this->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader(
'Custom-Header', 'Header Value'
);
});
}
  • Answer: 在 mailable class 的 build method 中, 使用 withSwiftMessage(), 可註冊一個 closure, 會跟著 SwiftMailer message instance 被觸發, 可在送出 mail 前做一些客製化

# Markdown Mailables

# Generating Markdown Mailables

以下的 Laravel example command 的意思是?

  • Example:
<?php
php artisan make:mail OrderShipped --markdown=emails.orders.shipped
  • Answer: 建立一個 mailable class, 以及相對應的 markdown template

以下的 Laravel example code 的意思是?

  • Example:
<?php
public function build()
{
return $this->from('example@example.com')
->markdown('emails.orders.shipped', [
'url' => $this->orderUrl,
]);
}
  • Answer: 在 mailable class 的 build method 中, 使用 markdown(), arg1 指定 markdown template, arg2 可帶入 array, 該 array data 會在 template 中可被以變數的方式使用

# Writing Markdown Messages

以下的 Laravel Mailable Markdown Template example code 的意思是?

  • Example:
<?php
@component('mail::message')
# Order Shipped

Your order has been shipped!

@component('mail::button', ['url' => $url])
View Order
@endcomponent

Thanks,<br>
{{ config('app.name') }}
@endcomponent
  • Answer: Laravel 的 Mailable Markdown Template 結合了 Blade 的 component 以及 Markdown 語法, 可使用 Laravel 預設的 email UI component

# Button Component

以下的 Laravel Mailable Markdown Template example code 的意思是?

  • Example:
<?php
@component('mail::button', ['url' => $url, 'color' => 'success'])
View Order
@endcomponent
  • Answer: 使用 Laravel 內建的 email UI component, 為一個 button, 可指定 url 以及 color, color 又可指定 primary, success, 以及 error

# Panel Component

以下的 Laravel Mailable Markdown Template example code 的意思是?

  • Example:
<?php
@component('mail::panel')
This is the panel content.
@endcomponent
  • Answer: 使用 Laravel 內建的 email UI component, panel 的效果會讓該區塊的背景顏色稍為不同, 可以達到引起注意力的效果

# Table Component

以下的 Laravel Mailable Markdown Template example code 的意思是?

  • Example:
<?php
@component('mail::table')
| Laravel | Table | Example |
| ------------- |:-------------:| --------:|
| Col 2 is | Centered | $10 |
| Col 3 is | Right-Aligned | $20 |
@endcomponent
  • Answer: 使用 Laravel 內建的 email UI component, 就是 Markdown table 的效果

# Customizing The Components

以下的 Laravel example commadn 的意思是?

  • Example:
<?php
php artisan vendor:publish --tag=laravel-mail
  • Answer: 可以匯出 Laravel 內建的 mail template, 匯出後可依照自己的需求去做進一步客製化, 會匯出到 resources/views/vendor/mail 資料夾

# Customizing The CSS

Laravel 中, 如果我要客製化 Markdown template 的 CSS, 該怎麼做?

  1. 在 export components 後, 修改 resources/views/vendor/mail/html/themes 資料夾中的 default.css
  2. resources/views/vendor/mail/html/themes 資料夾中建立一個新的 CSS file, 並在 config/mail.php 中修改 theme 要吃的檔案
  3. 若要針對單一 mailable class 修改, 可在 mailable class 中的 $theme property 中, 指定要吃的 theme css file

# Sending Mail

以下的 Laravel example code 的意思是?

  • Example:
<?php
class OrderShipmentController extends Controller
{
public function store(Request $request)
{
$order = Order::findOrFail($request->order_id);

// Ship the order...

Mail::to($request->user())->send(new OrderShipped($order));
}
}
  • Answer: 使用 Mail facade 的 to(), 指定收件人, 會自動到該 User model 尋找 name column 以及 email column, 然後再指定 mailable class

以下的 Laravel example code 的意思是?

  • Example:
<?php
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->send(new OrderShipped($order));
  • Answer: 也可使用 cc (Carbon Copy) 以及 bcc (Blind Carbon Copy) method 來寄信

# Looping Over Recipients

以下的 Laravel example code 的意思是?

  • Example:
<?php
foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
Mail::to($recipient)->send(new OrderShipped($order));
}
  • Answer: 有時會需要使用 loop 來將同一個 mailable class 寄給很多收件人, 這時務必在每一個 iteration 中都要使用 new Mailable Instance, 否則 to() 會將 email append 到 mailable instance, 這樣就會變成每一個 iteration 都會寄給之前的所有收件人

# Sending Mail Via A Specific Mailer

以下的 Laravel example code 的意思是?

  • Example:
<?php
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
  • Answer: Laravel 預設會使用 config/mail.php 中的 default mailer 來寄信, 也可使用 mailer() 來特別指定

# Queueing Mail

# Queueing A Mail Message

以下的 Laravel example code 的意思是?

  • Example:
<?php
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue(new OrderShipped($order));
  • Answer: 使用 queue 來發送 mail

# Delayed Message Queueing

以下的 Laravel example code 的意思是?

  • Example:
<?php
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later(now()->addMinutes(10), new OrderShipped($order));
  • Answer: 使用 later(), 可以發送 delayed queue message

# Pushing To Specific Queues

以下的 Laravel example code 的意思是?

  • Example:
<?php
$message = (new OrderShipped($order))
->onConnection('sqs')
->onQueue('emails');

Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue($message);
  • Answer: 使用 queue 發送 mail, 並指定其 connection 以及 queue

# Queueing By Default

以下的 Laravel example code 的意思是?

  • Example:
<?php
use Illuminate\Contracts\Queue\ShouldQueue;

class OrderShipped extends Mailable implements ShouldQueue
{
//
}
  • Answer: 如果已經確定這個 mailable 會固定使用 queue 來發送, 可以 implement ShouldQueue interface

# Queued Mailables & Database Transaction

以下的 Laravel example code 的意思是?

  • Example:
<?php
use Illuminate\Contracts\Queue\ShouldQueue;

class OrderShipped extends Mailable implements ShouldQueue
{
public $afterCommit = true;
}
  • Answer: 有時我們會在 transaction 中發送 queued mailable, 但在 transaction 完成之前 mailable 就已經 dispatch 了, 這會造成預期外的錯誤, 如要避免此情況, 可將 mailable class 的 $afterCommit property 設為 true, 這樣 queued mail 就會等到當下所有的 open transaction 都完成了才會寄出

# Rendering Mailables

以下的 Laravel example code 的意思是?

  • Example:
<?php
use App\Mail\InvoicePaid;
use App\Models\Invoice;

$invoice = Invoice::find(1);

return (new InvoicePaid($invoice))->render();
  • Answer: render method 會以 string 的方式 return HTML content

# Previewing Mailables In The Browser

以下的 Laravel example code 的意思是?

  • Example:
<?php
Route::get('/mailable', function () {
$invoice = App\Models\Invoice::find(1);

return new App\Mail\InvoicePaid($invoice);
});
  • Answer: 在設計 mailable class 時, 會希望可以即時地看到畫面, 這時可以直接 return mailable instance 需注意這並不會 return inline attachment, 如需測試 inline attachment, 可使用 MailHog 或 HELO

# Localizing Mailables

以下的 Laravel example code 的意思是?

  • Example:
<?php
Mail::to($request->user())->locale('es')->send(
new OrderShipped($order)
);
  • Answer: 使用 locale() 來指定 locale

# User Preferred Locales

以下的 Laravel example code 的意思是?

  • Example:
<?php
use Illuminate\Contracts\Translation\HasLocalePreference;

class User extends Model implements HasLocalePreference
{
public function preferredLocale()
{
return $this->locale;
}
}
// in controller
Mail::to($request->user())->send(new OrderShipped($order));
  • Answer: 當 notifiable model implement HasLocalePreference interface 之後, 可在 preferredLocale() 中定義要使用的 locale, 當使用 to() 發送 mailable 給指定 user 時, 會自動抓取資料庫中的 locale

# Testing Mailables

以下的 Laravel example code 的意思是?

  • Example:
<?php
use App\Mail\InvoicePaid;
use App\Models\User;

public function test_mailable_content()
{
$user = User::factory()->create();

$mailable = new InvoicePaid($user);

$mailable->assertSeeInHtml($user->email);
$mailable->assertSeeInHtml('Invoice Paid');

$mailable->assertSeeInText($user->email);
$mailable->assertSeeInText('Invoice Paid');
}
  • Answer: 可使用 mailable testing method 來驗證 mailable 是否包含指定的 string, assertSeeInHtml 跟 assertSeeInText 差別在於 HTML version mail 跟 plain text version mail

# Mail & Local Development

Laravel 中, 如果要測試 mail 是否有被發送, 可使用哪兩種方式?

Log driver 或一些其他的服務, 像是 HELO, Mailtrap, MailHog

# Events

以下的 Laravel example code 的意思是?

  • Example:
<?php
protected $listen = [
'Illuminate\Mail\Events\MessageSending' => [
'App\Listeners\LogSendingMessage',
],
'Illuminate\Mail\Events\MessageSent' => [
'App\Listeners\LogSentMessage',
],
];
  • Answer: 可註冊 listener 監聽兩種 mail event, MessageSending event 在 mail 發送前被觸發, MessageSent 在發送後觸發, 注意如果只是 queue 的話並不會觸發, 待該 job 真正被 worker pick up 被執行時才會觸發

--

--

Ray Lee | 李宗叡
Learn or Die

It's Ray. I do both backend and frontend, but more focus on backend. I like coding, and would like to see the whole picture of a product.