それでは、削除機能を実装していきます。
といっても実装手順は簡単です。
一覧ページに削除ボタンを作成
削除ボタンを押すと、タスクが削除される
削除ページは作成せず、タスクが完了したら一覧ページに戻す
この方針で実装したいと思います。
一覧ページに削除ボタンを作成
それでは一覧ページを開いてみましょう。(index.ctp)
とはいえ、index.ctpの内容はほとんどエレメント化してしまいました。
そのため、Element/task.ctpを修正しましょう。
// app/View/Elements/task.ctp // 省略 <?php echo $this->Html->link( '削除', '/Tasks/delete/' . $task['Task']['id'], array('class' => 'button') ); ?> </div>
ボタンはHtmlヘルパーでaタグを生成し、そこにCSSのbuttonクラスを適用させます。
リンク先はまだ作成しておりませんが、TasksControllerにdeleteメソッドを追記する予定です。
TasksControllerにdeleteメソッドを追加
それでは、実際に削除するdeleteメソッドを作成します。
大枠はdoneメソッドと同様なので、doneメソッドをベースに作成します。
// app/Controller/TasksController.php <?php class TasksController extends AppController { // 省略 public function delete() { // URLの末尾からタスクのIDを取得してメッセージを作成 $id = $this->request->pass[0]; // 取得したIDのレコードを削除する $this->Task->delete($id); $msg = sprintf( 'タスク %s を削除しました。', $id ); $this->Flash->set($msg); // indexへリダイレクト $this->redirect(['controller'=>'tasks','action'=>'index']); return; } }
ここで新しく、$this->Task->delete($id);が登場しました。
これは指定されたIDのレコードを削除するメソッドです。
裏では、下記のようなSQLがDBに対して実行されています。
$id = 1が入ったと仮定すると $ DELETE FROM tasks WHERE id = 1;
後は、タスク完了時に実装したdoneメソッドと同じなので、説明はしません。
処理の最後では一覧ページにリダイレクトするようredirectメソッドを入れております。
それでは実際に削除ボタンを押してみましょう。
フラッシュが出て、対象のタスクが消えたと思います。
DBにて消えていることを確認する
実際に消えているかどうか、MySQLにログインして確かめましょう。
Vagrantへのログインは済ませておいてください。 パスワードは変更していなければrootです。 [vagrant@localhost ~]$ mysql -u root -p Enter password: mysql>
それではまず使うDBの選択です。
ちなみに、MySQLのコマンドは大文字小文字は関係ありません。
しかし、便宜上大文字で記載しております。
mysql> USE cakephp_task;
それでは、tasksテーブルのレコードを確認します。
mysql> SELECT * FROM tasks;
先ほど削除したレコードはなくなっていますか?
このように、そもそもDBから存在そのものをなくしてしまうことを 物理削除 といいます。
物理削除と論理削除
さて、今はDBからレコードを物理削除してしまいました。
しかし、例えば実際のコンテンツなどで、物理削除してしまうのは果たして良いことでしょうか?
こういったケースを考えてみましょう。
ユーザーAさんがある会員サイトを退会しました。
サイト運営者は退会したAさんの情報をDBから物理削除しました。
その後、何かしらのトラブルがあり、Aさんから問い合わせがありました。
Aさんの情報を調べようにも、すでにDBから消えているので調べようがありません。
実際の運用ではこのようなことが多々起こります。
この場合、 レコード自体はDBに残しておき、削除したかどうかのフラグを用意して、見た目的には削除されているかのように見せる ことが必要です。
このような手法を 論理削除 といいます。
論理削除で削除機能を実装してみよう
前準備
この論理削除は、完了したタスクの件数を取得しように似ています。
完了したタスクの件数を取得しようでは、タスクが完了したかどうかを管理するstatusというカラムをtasksテーブルに持たせておりました。
今回、似たような機能を作成するにあたり、以前のレコードは紛らわしいので、一旦削除してしまいます。
全件削除する mysql> DELETE FROM tasks;
現在のtasksテーブルのカラムを確認します。
mysql> SHOW COLUMNS FROM tasks;
+———-+———-+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———-+———-+——+—–+———+—————-+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | tinytext | NO | | NULL | || body | text | NO | | NULL | |
| status | int(11) | NO | | 0 | || due_date | date | YES | | NULL | |
| created | datetime | NO | | NULL | |
| modified | datetime | NO | | NULL | |
+———-+———-+——+—–+———+—————-+7 rows in set (0.00 sec)
ここに、先ほど述べた 削除フラグ(deleted) を追加します。
カラムの追加に関してはALTER文を使用します。
(ちなみにオルターと読むことが一般的です。)
カラムを追加 mysql> ALTER TABLE tasks ADD COLUMN deleted INT NOT NULL DEFAULT 0;
もう1度tasksテーブルのカラムを確認 mysql> SHOW COLUMNS FROM tasks;
+———-+———-+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———-+———-+——+—–+———+—————-+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | tinytext | NO | | NULL | |
| body | text | NO | | NULL | |
| status | int(11) | NO | | 0 | |
| due_date | date | YES | | NULL | |
| created | datetime | NO | | NULL | |
| modified | datetime | NO | | NULL | |
| deleted | int(11) | NO | | 0 | |
+———-+———-+——+—–+———+—————-+8 rows in set (0.00 sec)
本項ではMySQLの説明は必要最小限にしますが、上記の手順でtasksテーブルにdeletedカラムが追加されたことが分かったと思います。
また、以後の研修でMySQLを直接見てもらうことも多いので、ログインしたままで開いておきましょう。
deletedの使い方を整理
deletedは1の場合は 削除されたもの とみなします。
そのため、削除されていないタスクはdeleted = 0となります。
つまり、基本的にタスクを検索する場合は、条件句に下記を追加することになります。
SELECT * FROM tasks WHERE deleted = 0;
一覧画面の修正
一覧画面で表示させたいタスクは、 削除されていないもの です。
そのため、indexメソッドの条件句に追記します。
// app/Controller/TasksController.php public function index() { $options = array( 'conditions' => array( 'Task.status' => 0, // deletedが0の条件を追加 'Task.deleted' => 0 ) ); // 変数をビューへ渡す $tasks_data = $this->Task->find('all', $options); $this->set('tasks_data', $tasks_data); // app/View/Tasks/index.ctpを表示 $this->render('index'); }
変更されたのは下記です。
複数条件を指定したい場合は、カンマで区切って指定します。
$options = array( 'conditions' => array( 'Task.status' => 0, 'Task.deleted' => 0 ));
この修正を反映した後、再度一覧画面にアクセスしても表示は問題ないと思います。
実は変わっている部分があります。
それはフッターに表示されているSQL文です。
ちゃんと条件句にdeleted = 0が追加されています。
削除機能の修正
それでは削除機能を修正しましょう。
先ほどは、$this->Task->delete($id);で物理削除していました。
今度はdeletedを1に更新すれば良いので、UPDATE文を使用します。
// app/Controller/TasksController.php public function delete() { // URLの末尾からタスクのIDを取得してメッセージを作成 $id = $this->request->pass[0]; // $this->Task->delete($id); // doneと同様の流れで大丈夫です $this->Task->id = $id; $this->Task->saveField('deleted', 1); $msg = sprintf( 'タスク %s を削除しました。', $id ); $this->Flash->set($msg); // indexへリダイレクト $this->redirect(['controller'=>'tasks','action'=>'index']); return; }
変更されたのは下記です。
// $this->Task->delete($id); // doneと同様の流れで大丈夫です $this->Task->id = $id; $this->Task->saveField('deleted', 1);
見た目はあまり変わっていないため、実際に動かして確認してみましょう。
実際に動かしてテスト
それでは適当なタスクを作成してください。
例として、3件のタスクを作成しました。
DBを見てみると、3件できていることが分かります。
それでは、タスク1を削除してみます。
削除されました。
DBはどうでしょうか?
example_table_after_delete.png
deletedが1に変更されていますね。
つまり、削除フラグがたち、 削除されたものとみなされています 。
これで、先ほどの例であげたときもユーザーの情報を調べることができます。
論理削除については便利なComponemtもあるので、気になった方は調べてみても良いと思います。