CakePHP3のトランザクションとtry/catchと例外処理と。
- 公開日
- 更新日
- カテゴリ:CakePHP
- タグ:PHP,CakePHP
データベースを絡めた処理の場合に、その整合性を保つ為に、エラーが出たら処理前の状態に戻して終了させたい場合があります。そんな時に必要なのは、トランザクションという仕組みです。
また、それを実現する為に基本的には、エラーが発生し例外が投げられた際にそれをキャッチし独自の処理を実装する必要があります。
今回は CakePHP のコントローラ内に try/catch を仕込み、トランザクションの一連の処理を実装します。
Contents
開発環境
今回の開発環境は以下の通りです。
- MySQL 5.7
- PHP 7.2
- CakePHP 3.6
try/catch の基本形
まずは try/catch の基本形から。コントローラに対して以下のように記述します。
<?php
namespace App\Controller;
use App\Controller\AppController;
class SamplesController extends AppController
{
public function index()
{
try {
// ここに通常の処理を書く
} catch(\Exception $e) {
// ここに例外が発生した際の処理を書く
}
}
}
通常の処理を記述し、それらが実行していく中で例外が発生すると、try 内の処理を break し、catch の方に処理が移ります。
これに関しては CakePHP とか PHP フレームワークとかは関係なく、PHP 全体で共通の書式なります。
トランザクションの基本形
トランザクションの基本形を以下に示します。
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Datasource\ConnectionManager;
class SamplesController extends AppController
{
public function index()
{
$connection = ConnectionManager::get('default');
// トランザクション開始
$connection->begin();
try {
// 何らかの処理
$this->hoge();
// コミット
$connection->commit();
} catch(\Exception $e) {
// 例外に対する処理
// ロールバック
$connection->rollback();
}
}
}
トランザクションを使うには ConnectionManager が必要なので use しています。
そしてインスタンスを渡す際に get('default') を行っていますが、この「default 」というのが、cakephp/config/app.php で設定されている Datasources プロパティの default 部分になります。つまり、この deafult部分で設定されたデータベースに対してトランザクションを行う。という事になります。
その他はよくある形です。トランザクションを開始し、例外が発生しなければコミット、例外が発生すればロールバックを行います。
例外処理
ここからはおまけです。例外を catch したら try から抜けて例外処理部分に移行するわけですが、引数で渡される変数 $e に関しては、以下のメソッドがあります。
$e->getCode(); // HTTP ステータスコード
$e->getMessage(); // エラーメッセージ
$e->getFile(); // 例外が発生したファイル(クラス)のフルパス
$e->getLine(); // 例外が投げられた行
$e->getPrevious(); // 前の例外を返す
$e->getTraceAsString(); // エラーの詳細
$e->getTrace(); // エラーの詳細を配列に格納したもの
場合によりますが、例外が発生した際に上記メソッドで取得できる情報は以下のようなものになります。
Array
(
[getCode] => 501
[getMessage] => エラーが発生しました
[getFile] => /var/www/html/cakephp/src/Controller/SamplesController.php
[getLine] => 203
[getPrevious] =>
[getTrace] => Array (
// 長いので省略
)
[getTraceAsString] => // 長いので省略
)
ではこのベースのソースコードにメソッドを追加して例外を発生させてみます。
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Datasource\ConnectionManager;
use Cake\Network\Exception\NotImplementedException;
class SamplesController extends AppController
{
public function index()
{
$connection = ConnectionManager::get('default');
// トランザクション開始
$connection->begin();
try {
// 何らかの処理
$this->hoge();
// コミット
$connection->commit();
} catch(\Exception $e) {
// 例外に対する処理
print_r($e);
// ロールバック
$connection->rollback();
}
}
public function hoge()
{
throw new NotImplementedException(__('例外が発生しました'));
}
}
単純に hoge() メソッドを作成し、その中で例外を投げています。
上記の場合、501 エラーを返すために例外クラスである NotImplementedException クラスを use して例外を投げています。
HTTP ステータスコードによってクラスが存在しているので、詳しくは cookbook を参照して適宜使ってください。
https://book.cakephp.org/3.0/ja/development/errors.html#id6
まとめ
作業は以上になります。データの整合性を保つ必要のあるデータベース処理にはトランザクションは大事なので是非試してみてください。
また、PHP では全ての動きに対して例外が投げられるわけではありません。 CakePHP などの PHP フレームワークでは、エラー等が発生した際に例外を投げそれをキャッチして通知したりロギングしたりしてくれますが、投げられない例外はそもそもキャッチされないので、exec() などは独自に例外を throw する処理を書きましょう。