RitoLabo

CakePHP3のトランザクションとtry/catchと例外処理と。

  • 公開:
  • カテゴリ: PHP CakePHP
  • タグ: PHP,CakePHP,3.5,3.6,Transaction,Exception

データベースを絡めた処理の場合に、その整合性を保つ為に、エラーが出たら処理前の状態に戻して終了させたい場合があります。そんな時に必要なのは、トランザクションという仕組みです。

また、それを実現する為に基本的には、エラーが発生し例外が投げられた際にそれをキャッチし独自の処理を実装する必要があります。

今回はCakePHPのコントローラ内にtry/catchを仕込み、トランザクションの一連の処理を実装します。

アジェンダ
  1. 開発環境
  2. try/catchの基本形
  3. トランザクションの基本形
  4. 例外処理

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • MySQL 5.7
  • PHP 7.2/7.1
  • CakePHP 3.6/3.5

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する処理を書きましょう。