タスクの削除機能を実装しよう

それでは、削除機能を実装していきます。
といっても実装手順は簡単です。

一覧ページに削除ボタンを作成
削除ボタンを押すと、タスクが削除される
削除ページは作成せず、タスクが完了したら一覧ページに戻す
この方針で実装したいと思います。

一覧ページに削除ボタンを作成
それでは一覧ページを開いてみましょう。(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もあるので、気になった方は調べてみても良いと思います。

カテゴリー

アーカイブ

Close Bitnami banner
Bitnami