第14章 · 初心者向け
TODO機能を一から作る
今までの部品を、1つのTODOアプリに組み立てます。完成イメージを持って進みましょう。
1. TODOテーブルを理解
最低限の列: id / user_id / title / done / created / modified
CREATE TABLE todos (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(100) NOT NULL,
done TINYINT(1) NOT NULL DEFAULT 0,
created DATETIME,
modified DATETIME
);
意味
user_id=誰のTODOか。done=終わったか(0/1)。
2. TODO一覧取得
ログイン中ユーザーの分だけ、新しい順でページングします。
public function index()
{
$userId = $this->request->getSession()->read('Auth.User.id');
$query = $this->Todos->find()
->where(['user_id' => $userId])
->orderBy(['id' => 'DESC']);
$todos = $this->paginate($query);
$this->set(compact('todos'));
}
3. TODO詳細
public function view($id = null)
{
$todo = $this->Todos->get($id);
$this->assertOwned($todo); // 自分のものか確認
$this->set(compact('todo'));
}
4. TODO登録
忘れやすい点
フォームに無くても、user_id はサーバー側で必ずセットします(改ざん防止)。
$data = $this->request->getData();
$data['user_id'] = $this->request->getSession()->read('Auth.User.id');
$todo = $this->Todos->patchEntity($todo, $data);
5. TODO編集
get → 権限チェック → patch → save、の流れです(第6章+第11章)。
6. TODO削除
POSTのみ許可 → get → 権限チェック → delete → 一覧へ。
7. 検索
$q = $this->request->getQuery('q');
if ($q) {
$query->where(['title LIKE' => '%' . $q . '%']);
}
8. Pagination
public array $paginate = [
'limit' => 10,
'order' => ['Todos.id' => 'desc'],
];
9. ログインユーザーとの紐付け
登録時に user_id を付ける / 一覧は自分のIDで絞る
10. 権限チェック
private function assertOwned($todo): void
{
$userId = $this->request->getSession()->read('Auth.User.id');
$role = $this->request->getSession()->read('Auth.User.role');
if ($todo->user_id !== $userId && $role !== 'admin') {
throw new \Cake\Http\Exception\ForbiddenException();
}
}
11. Transaction
「TODO保存+操作ログ」などを1セットにするとき(第10章)に使います。
12. エラー処理
失敗したら Flash(短い)+ Log(詳しい)。
13. CSV出力
自分のTODOだけ export(第13章)すれば完成です。
ここまで動けば「業務CRUD」の最低ラインはクリアです
FINAL
完成チェック(はい/いいえ)
- 未ログインで一覧に入れない
- 他人のTODOをURL直打ちしても編集できない
- 検索とページングが動く
- 登録・更新・削除ができる
- CSVがダウンロードできる
- 失敗時にログが残る