[Laravel] メールの送信ログをデータベースに保存する方法
Laravelのメールシステムを利用する際に、送信履歴をデータベースへログとして保存しておく方法を紹介します。
メールの送信処理に関わるクラスといえばMailableクラスやNotificationクラスですが、共通して送信処理が行われた際に発火されるIlluminate\Mail\Events\MessageSendingイベントをフックすることで送信済みのメールオブジェクトのハンドリングが可能です。
今回は公開されているパッケージを使ってみます。
shvetsgroup/laravel-email-database-logパッケージ
Composerを使ってshvetsgroup/laravel-email-database-logをインストールします。
composer require shvetsgroup/laravel-email-database-log
Laravelの5.5以上を使っている場合はPackage Auto Discoveryでサービスプロバイダが読み込まれます。
それ以外の場合は下記をconfig/app.phpへ追加しましょう。
'providers' => [
// ...
ShvetsGroup\LaravelEmailDatabaseLog\LaravelEmailDatabaseLogServiceProvider::class,
],
migrationを実行すると、パッケージに同封されているemail_logテーブルが作成されます。
php artisan migrate
メールを送信する通知クラスも作成しておきましょう。
php artisan make:notification TestNotification
作成したTestNotificationクラスを利用してメールを送信してみます。
今回はTinkerを使用してコマンドラインからユーザーを作成し、メールの送信を行います。
php artisan tinker
>>> $user = factory(App\User::class)->make();
=> App\User {#867
name: "Oral Huel",
email: "[email protected]",
}
>>> $user->notify(new \App\Notifications\TestNotification);
=> null
email_logテーブルに送信ログが保存されていることを確認しましょう。
[Laravel] Eloquentモデルのイベントをフックする4つの方法
Laravelでデータベースの追加や変更を行う際に、オリジナルの処理を行いたい場合についてサンプルを踏まえて説明します。
Eloquentモデルでは様々な処理を行う際に、各種のイベントを発火しようとするので、このイベントをフックすることによってオリジナルの処理を追加することができます。
Laravel公式ページには2つの方法が解説されていますが、これに加えて2つの計4つの方法を解説します。
イベントの種類
イベントを使用すると、特定のモデルクラスがデータベースに保存または更新されるたびに、簡単にオリジナルの処理を実行できます。
目的に沿ったイベントをフックしましょう。
retrieved
creating
created
updating
updated
saving
saved
deleting
deleted
restoring
restored
1. dispachesEcentプロパティを使用したイベントのフック
Eloquentモデルの$dispatchesEventsプロパティに必要なイベントと処理するクラスを定義することで各イベントをフックできます。
<?php
namespace App;
use App\Events\UserSaved;
use App\Events\UserDeleted;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The event map for the model.
*
* @var array
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
イベントクラスの引数に対象のモデルクラス(上記サンプルの場合App\Userクラス)が渡されるので、リスナー等で処理を行う事ができます。
Laravelのイベントクラスについてはこちら。
2. オブザーバーを使用したイベントのフック
モデルのイベント処理をまとめて記述したい場合に便利です。
また、Observersはサービスプロバイダでの登録が可能なので、環境の違いによる振る舞いの差を簡単に吸収することが可能です。
<?php
namespace App\Observers;
use App\User;
class UserObserver
{
/**
* Handle to the User "created" event.
*
* @param \App\User $user
* @return void
*/
public function created(User $user)
{
//
}
/**
* Handle the User "updated" event.
*
* @param \App\User $user
* @return void
*/
public function updated(User $user)
{
//
}
/**
* Handle the User "deleted" event.
*
* @param \App\User $user
* @return void
*/
public function deleted(User $user)
{
//
}
}
各イベントに対応したメソッドが呼ばれます。
すべてのメソッドの引数には対象のモデルクラスが渡されます。
作成したオブザーバーをサービスプロバイダへ登録することで、全てのイベントがフックできます。
<?php
namespace App\Providers;
use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
}
3. bootメソッドをoverwriteしたイベントのフック
Eloquentモデルでは、自身の初期化時にboot()メソッドが呼ばれます。
このメソッド内で各イベントが発火された際の処理を記述します。
<?php
namespace App;
use App\Notifications\UserRegistration;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The "booting" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();
self::created(function($user){
$user->notify(new UserRegistration($user));
});
}
}
以前は現在公式で解説されている2つの方法はありませんでしたので、この方法でイベントのフックを行っていました。
今回紹介する4つの中では一番シンプルですが、クラスの継承を多様する場合には注意が必要です。
4. Traitを使用したイベントのフック
個人的にはこの方法をよく使っています。
3.で説明したboot()メソッドですが、内部では以下の様な処理が行われています。
/**
* The "booting" method of the model.
*
* @return void
*/
protected static function boot()
{
static::bootTraits();
}
/**
* Boot all of the bootable traits on the model.
*
* @return void
*/
protected static function bootTraits()
{
$class = static::class;
foreach (class_uses_recursive($class) as $trait) {
if (method_exists($class, $method = 'boot'.class_basename($trait))) {
forward_static_call([$class, $method]);
}
}
}
bootTraits()メソッドに注目すると、自身に使われているTraitの一覧を取得する処理が伺えます。
簡単に説明すると、使用しているTraitの中に、「boot + Traitのクラス名」というメソッドが存在する場合に実行されます。
例えば以下の様なTraitを作成して、Eloquentモデルでuseすることで削除時にログを出力することができます。
<?php
namespace App\Traits;
class DeletedLogger extends Authenticatable
{
/**
* Setup model event hooks
*/
protected static function bootDeletedLogger()
{
static::deleted(function ($model) {
logger('Deleted Model:', ['class' => get_class(model), 'id' => $model->id]);
});
}
}
<?php
namespace App;
use App\Traits\DeletedLogger;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable, DeletedLogger;
}
[Laravel] 5.6.27がリリースされました
laravel/frameworkのバージョン5.6.27がリリースされました。更新された機能について確認していきます。
Added
1. phpredisによるRedisへの接続オプションが追加されました (#24678)
2. Artisanのmigrate:fresh、migrate:refreshコマンドに--realpathオプションが追加されました (#24683)
3. AuthenticatesUsersにloggedout()メソッドが追加されました (#24621)
loggedout()メソッドをoverwriteすることで、authenticated()やregistered()メソッドのようなオリジナルの処理を追加できます。
/**
* The user has logged out.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
protected function loggedOut(Request $request)
{
// ログアウト時の処理を記述
// 記述しない場合は redirect('/') として処理される
}
詳しい変更については以下を確認してください。
Release v5.6.27 · laravel/framework · GitHub
[Laravel] 5.6.26がリリースされました
laravel/frameworkのバージョン5.6.26がリリースされました。更新された機能について確認していきます。
Added
1. Azure SQL Serverとのコネクションが切れた場合の挙動が改善されました (#24566)
public function routeNotificationForMail($notification)
{
return [
$this->email => $this->name,
];
}
// 複数の場合
public function routeNotificationForMail($notification)
{
return [
$this->email => $this->name,
$this->email_2,
$this->email_3 => $this->otherName,
];
}
2. Notificationクラスでメールを送信する際に、受信者の名前を含めることが可能になりました (#24606)
モデルのリレーションから不必要な情報を削除できます。
$model->unsetRelation('user');
3. Post-MigrationのafterCreate()メソッドのコールバックの引数にテーブル名が渡されるようになりました (#24621)
マイグレーション実行時にhookできます。
app('migration.creator')->afterCreate(function ($table) {
//
});
4. Auth::attempt()メソッドに配列、Collectionを渡せるようになりました (#24620)
配列、Collectinを利用した認証例
Auth::attempt([
'email' => $request->get('email'),
'password' => $request->get('password'),
'user_type' => collect([1, 2, 3]),
'active' => 'Y'
], $request->get('remember'))
詳しい変更については以下を確認してください。
Release v5.6.26 · laravel/framework · GitHub
[Laravel] 5.6.25がリリースされました
laravel/frameworkのバージョン5.6.25がリリースされました。更新された機能について確認していきます。
Added
1. SQL ServerにwhereJsonContains()メソッドが追加されました (#24448)
2. EloquentモデルにunsetRelationメソッドが追加されました (#24486)
モデルのリレーションから不必要な情報を削除できます。
$model->unsetRelation('user');
3. Auth::hasUser()メソッドが追加されました (#24518)
4. TestResponseクラスにassertOk()メソッドが追加されました (#24536)
詳しい変更については以下を確認してください。
Release v5.6.25 · laravel/framework · GitHub
[Laravel] 5.6.24がリリースされました
laravel/frameworkのバージョン5.6.24がリリースされました。更新された機能について確認していきます。
Added
1. assertSessionHasNoErrors()メソッドが追加されました (#24308)
2. データベースのSRSが指定可能になりました (#24320)
3. Builder::whereJsonDoesntContain()、Builder::orWhereJsonDoesntContain()メソッドが追加されました (#24367)
4. 全ての通知イベントでQueueable、SerializesModelsトレイトが利用されるようになりました (#24368)
Illuminate\Notifications\Events\NotificationFailed
Illuminate\Notifications\Events\NotificationSending
Illuminate\Notifications\Events\NotificationSent
5. ルーティングの指定が配列で可能になりました (#24385)
Route::get('smth', [SomeController::class, 'methodName']);
6. SQLサーバーでJsonでのSelectが可能になりました (#24397)
詳しい変更については以下を確認してください。
Release v5.6.24 · laravel/framework · GitHub
[Laravel] 5.6.23がリリースされました
laravel/frameworkのバージョン5.6.23がリリースされました。更新された機能について確認していきます。
Added
1. Migration時にテーブルのindexのリネームが可能になりました (#24147)
MySQL, Postgres, MS SQLについてサポートされました。
$table->renameIndex('from', 'to');
2. 特定のイベントを偽装して発火させるEvent::fakeFor()メソッドが追加されました (#24230)
3. Bladeエンジンに@cananyディレクテイブが追加されました (#24137)
Gateクラスのanyメソッドを呼ぶディレクティブです。複数の権限を評価できます。
<table>
<tr>
<th>...</th>
@canany(['edit_post', 'delete_post'])
<th>Actions</th>
@endcanany
</tr>
<tr>
<td>...</td>
@canany(['edit_post', 'delete_post'])
<td>
@can('edit_post')
<button>Edit</button>
@endcan
@can('delete_post')
<button>Delete</button>
@endcan
</td>
@endcanany
</tr>
</table>
4. TestReponse::assertUri()メソッドが追加されました (#24267)
詳しい変更については以下を確認してください。
Release v5.6.23 · laravel/framework · GitHub
[Laravel] 5.6.22がリリースされました
laravel/frameworkのバージョン5.6.22がリリースされました。更新された機能について確認していきます。
Added
1. Collection::loadMissing()メソッドが追加されました (#24166, #24215)
ネストしたリレーションの取得や、コールバックに対応しました。
// before
(new \Illuminate\Database\Eloquent\Collection($posts->pluck('comments')->collapse()))->load('parent');
// after
$posts = Post::with('comments')->get();
$posts->loadMissing('comments.parent');
詳しい変更については以下を確認してください。
Release v5.6.22 · laravel/framework · GitHub
[Laravel] 5.6.21がリリースされました
laravel/frameworkのバージョン5.6.21がリリースされました。更新された機能について確認していきます。
Added
1. FilesystemManager::forgetDisk()メソッドが追加されました (#24057, @cbfb4fb)
アプリケーション内ではFileSystemのDisk設定はキャッシュされるため、再設定が必要な場合に使用します。
2. artisanのdownコマンドに--allowパラメーターが追加されました (#24003)
メンテナンスモードにする際に許可するIPを指定可能になりました。
php artisan down --allow=127.0.0.1 --allow=192.168.0.0/16
3. validationのルールにgt, lt, gte, lteが追加されました (#24091, #24135)
4. TestResponse::assertCookieNotExpired()メソッドが追加されました (#24119)
詳しい変更については以下を確認してください。
Release v5.6.21 · laravel/framework · GitHub
[Laravel] 5.6.20がリリースされました
laravel/frameworkのバージョン5.6.20がリリースされました。更新された機能について確認していきます。
Added
1. abbort()メソッドにResponse (Responsable)クラスを渡せるようになりました (@4e29889)
以下の様なレスポンスのカスタマイズか可能になりました。
abort(response()->json(['thats', 'zonda']))
2. タスクスケジュールにpingBeforeIf、thenPingIfメソッドが追加されました (#24077, @1bf54d2)
特定の条件でpingするサンプル
$schedule->command('something')
->everyMinute()
->pingBeforeIf(app()->environment() == 'production', $url)
->thenPingIf(app()->environment() == 'production', $url);
3. withDefault()メソッドがMorphToリレーションでもサポートされるようになりました (#24061)
詳しい変更については以下を確認してください。
Release v5.6.20 · laravel/framework · GitHub