CakePHP 3 と 4 で連鎖削除(親を消すと子も消える)を実装する方法

はじめに

CakePHP 3、4 では、親データを消すと子データを自動的に削除する機能(連鎖削除)を簡単に実装することが出来ます。

今日はその方法を、動作確認用のテーブルやデータの SQL と併せてご紹介します。

今回は CakePHP 3.8.84.0.2 で動作確認をしています。

目次
  1. テスト用データベースの準備
  2. モデルに連鎖削除の設定追加
  3. 動作確認
  4. おわりに

1. テスト用データベースの準備

動作確認に使用したテーブルとデータの SQL は以下の通りです。アンケートに複数の質問がぶら下がっているものになります。

-- アンケートテーブル
CREATE TABLE `questionnaires` (
  `id` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `questionnaires`
  ADD PRIMARY KEY (`id`),
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

-- 質問テーブル
CREATE TABLE `questions` (
  `id` int(11) NOT NULL,
  `questionnaire_id` int(11) NOT NULL,
  `body` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `questions`
  ADD PRIMARY KEY (`id`),
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

-- テスト用データ
INSERT INTO `questionnaires` (`id`, `title`, `created`, `modified`) VALUES
  (NULL, 'アンケート(1)', NOW(), NOW()),
  (NULL, 'アンケート(2)', NOW(), NOW()),
  (NULL, 'アンケート(3)', NOW(), NOW());

INSERT INTO `questions` (`id`, `questionnaire_id`, `body`, `created`, `modified`) VALUES
  (NULL, '1', '質問 1-1', NOW(), NOW()),
  (NULL, '1', '質問 1-2', NOW(), NOW()),
  (NULL, '1', '質問 1-3', NOW(), NOW()),
  (NULL, '2', '質問 2-1', NOW(), NOW()),
  (NULL, '3', '質問 3-1', NOW(), NOW()),
  (NULL, '3', '質問 3-2', NOW(), NOW()),
  (NULL, '3', '質問 3-3', NOW(), NOW());

2. モデルに連鎖削除の設定追加

今回の例ではアンケート(Questionnaires)を削除した際に、それにぶら下がる質問(Questions)を自動的に削除するようにします。

実装方法は Questionnaires モデルにある Questions のアソシエーション設定に、下記オプションを追加するだけです。

/src/Model/Table/QuestionnairesTable.php
$this->hasMany('Questions', [
  ...
  // ▼ 下記2行を追加
  'dependent' => true,
  'cascadeCallbacks' => true,
]);

dependent」を true にすれば連鎖削除が有効になります。

cascadeCallbacks」を true すると、連鎖削除する際に子モデルの beforeDelete、afterDelete を実行します。デフォルトでは発火しないのでご注意ください。

3. 動作確認

今回は下記のようにアクションを実装して動作確認しました。

/src/Controller/QuestionnairesController.php
public function index()
{
  $questionnaire = $this->Questionnaires->get(1);
  $result = $this->Questionnaires->delete($questionnaire);
  debug($result);
  exit;
}

また、Questions モデルに下記関数を追加すれば、「cascadeCallbacks」オプションの挙動を確認できます。

/src/Model/Table/QuestionsTable.php
public function beforeDelete($event, $entity, $options)
{
  echo "<h1>Before<h1>";
  var_dump($entity);
}

public function afterDelete($event, $entity, $options)
{
  echo "<h1>After</h1>";
  echo '<hr>';
}

4. おわりに

CakePHP 3、4 ではモデルにオプションを追加するだけで連鎖削除を実装することが出来ます。これを知らないと、親モデルの afterDelete に子モデルの削除を…なんて実装しちゃいそうですね。

たまに公式ガイドを読み直して、見落としている機能がないかを確認することも大事だなと思いました。