This document is for CakePHP's development version, which can be significantly different
from previous releases.
You may want to read
current stable release documentation instead.
ブログアプリケーションを進めましょう。投稿記事をカテゴライズすることを想像してください。 順に並べられたカテゴリーが必要で、カテゴリーの作成のために Tree behavior を使いましょう。
しかしまずはじめに、テーブルを編集する必要があります。
データベース内にテーブルを作るのに migrations プラグイン を利用します。 もし articles テーブルが既にデータベースに存在する場合、削除してください。
composer.json
ファイルを開いてください。通常の場合、
require
の下に既にマイグレーションプラグインが記述されているでしょう。
もし無かったら以下を実行して追加してください。
composer require cakephp/migrations:~1.0
マイグレーションプラグインはアプリケーションの plugins
フォルダーに存在します。
そして、 Application の bootstrap
メソッドに $this->addPlugin('Migrations');
を追加してください。
プラグインを読み込んだら、次のコマンドを実行してマイグレーションファイルを作成しましょう。
bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified
/config/Migrations
フォルダーにマイグレーションファイルが以下のように生成されます。
<?php
use Migrations\AbstractMigration;
class CreateArticles extends AbstractMigration
{
public function change()
{
$table = $this->table('articles');
$table->addColumn('title', 'string', [
'default' => null,
'limit' => 255,
'null' => false,
]);
$table->addColumn('body', 'text', [
'default' => null,
'null' => false,
]);
$table->addColumn('category_id', 'integer', [
'default' => null,
'limit' => 11,
'null' => false,
]);
$table->addColumn('created', 'datetime', [
'default' => null,
'null' => false,
]);
$table->addColumn('modified', 'datetime', [
'default' => null,
'null' => false,
]);
$table->create();
}
}
categories
テーブルも同じようにコマンドを実行して作成してください。
もしフィールドに対して特定の長さを指定する場合、カッコを使って以下のように設定することができます。
bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified
これによって、 config/Migrations
内に以下が作成されます。
<?php
use Migrations\AbstractMigration;
class CreateCategories extends AbstractMigration
{
public function change()
{
$table = $this->table('categories');
$table->addColumn('parent_id', 'integer', [
'default' => null,
'limit' => 11,
'null' => false,
]);
$table->addColumn('lft', 'integer', [
'default' => null,
'limit' => 10,
'null' => false,
]);
$table->addColumn('rght', 'integer', [
'default' => null,
'limit' => 10,
'null' => false,
]);
$table->addColumn('name', 'string', [
'default' => null,
'limit' => 100,
'null' => false,
]);
$table->addColumn('description', 'string', [
'default' => null,
'limit' => 255,
'null' => false,
]);
$table->addColumn('created', 'datetime', [
'default' => null,
'null' => false,
]);
$table->addColumn('modified', 'datetime', [
'default' => null,
'null' => false,
]);
$table->create();
}
}
ではマイグレーションファイルが作成されたので、テーブルを作成する前にこれを編集しましょう。
parent_id
フィールドを 'null' => false
を 'null' => true
に変更してください。
トップレベルのカテゴリーでは parent_id
は null になるからです。
テーブルを作る際は以下のコマンドを実行してください。
bin/cake migrations migrate
テーブルの設定ができたので、投稿記事のカテゴライズに注力しましょう。
既にパート2でファイル (Articles の Table, Controller および Template) ができあがっているはずです。 カテゴリーへ参照を追記していきましょう。
Articles と Categories テーブルとを結びつける必要があります。 src/Model/Table/ArticlesTable.php ファイルを開き、以下のように追加してください。
// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
// Just add the belongsTo relation with CategoriesTable
$this->belongsTo('Categories', [
'foreignKey' => 'category_id',
]);
}
}
bake コマンドを利用してすべてのファイルを作成しましょう。
bin/cake bake model Categories
bin/cake bake controller Categories
bin/cake bake template Categories
代わりに、1行で bake することもできます。
bin/cake bake all Categories
bake ツールによって、すぐにすべてのファイルが作成されました。もし CakePHP が どのように動いているかを知りたかったら簡単に bake ツールを読んでおきましょう。
注釈
Windows を利用している場合は / の代わりに \ を使用してください。
templates/Categories/add.php
および templates/Categories/edit.php
を以下のように修正してください。
echo $this->Form->control('parent_id', [
'options' => $parentCategories,
'empty' => 'No parent category'
]);
データベーステーブルの階層的なツリー構造を管理するのに TreeBehavior が役に立つでしょう。データ管理の際に MPTT logic を利用しています。 MPTT ツリー構造は読み込みに最適化されているので、 ときにブログのような読み込みが重いアプリケーションに適しています。
src/Model/Table/CategoriesTable.php
ファイルの initialize()
メソッド内で、
TreeBehavior が CategoriesTable と紐づけられていることがお分かりいただけるでしょう。
bake は lft
列と rght
列が存在するあらゆるテーブルにこのビヘイビアーを追加します。
$this->addBehavior('Tree');
紐づけられた TreeBehavior によって、カテゴリーの順を並べ直すような機能にアクセスすることが可能になります。 すぐにわかるでしょう。
しかし今は、カテゴリーの中にある add と edit のテンプレートファイル内の次の control を削除てください。
echo $this->Form->control('lft');
echo $this->Form->control('rght');
さらに、Categories テーブルモデルの lft
列と rght
列のバリデーターの中の
requirePresense を無効にするか削除してください。
public function validationDefault(Validator $validator): Validator
{
$validator
->add('id', 'valid', ['rule' => 'numeric'])
->allowEmptyString('id', 'create');
$validator
->add('lft', 'valid', ['rule' => 'numeric'])
// ->requirePresence('lft', 'create')
->notEmpty('lft');
$validator
->add('rght', 'valid', ['rule' => 'numeric'])
// ->requirePresence('rght', 'create')
->notEmpty('rght');
}
カテゴリーが保存される際に、これらのフィールドは TreeBehavior によって自動的に管理されます。
ブラウザーを用いて、 /yoursite/categories/add
コントローラーアクションから
いくつかの新しいカテゴリーを登録してください。
categories の index テンプレートファイルでは、categories を一覧したり並べ替えたりすることができます。
CategoriesController.php
の index メソッドを編集して、ツリーでカテゴリーを並べ替えるために
moveUp()
および moveDown()
メソッドを追加してください。
class CategoriesController extends AppController
{
public function index()
{
$categories = $this->Categories->find()
->order(['lft' => 'ASC'])
->all();
$this->set(compact('categories'));
$this->viewBuilder()->setOption('serialize', ['categories']);
}
public function moveUp($id = null)
{
$this->request->allowMethod(['post', 'put']);
$category = $this->Categories->get($id);
if ($this->Categories->moveUp($category)) {
$this->Flash->success('The category has been moved Up.');
} else {
$this->Flash->error('The category could not be moved up. Please, try again.');
}
return $this->redirect($this->referer(['action' => 'index']));
}
public function moveDown($id = null)
{
$this->request->allowMethod(['post', 'put']);
$category = $this->Categories->get($id);
if ($this->Categories->moveDown($category)) {
$this->Flash->success('The category has been moved down.');
} else {
$this->Flash->error('The category could not be moved down. Please, try again.');
}
return $this->redirect($this->referer(['action' => 'index']));
}
}
templates/Categories/index.php
を以下のように置き換えてください。
<div class="actions large-2 medium-3 columns">
<h3><?= __('Actions') ?></h3>
<ul class="side-nav">
<li><?= $this->Html->link(__('New Category'), ['action' => 'add']) ?></li>
</ul>
</div>
<div class="categories index large-10 medium-9 columns">
<table cellpadding="0" cellspacing="0">
<thead>
<tr>
<th>Id</th>
<th>Parent Id</th>
<th>Lft</th>
<th>Rght</th>
<th>Name</th>
<th>Description</th>
<th>Created</th>
<th class="actions"><?= __('Actions') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($categories as $category): ?>
<tr>
<td><?= $category->id ?></td>
<td><?= $category->parent_id ?></td>
<td><?= $category->lft ?></td>
<td><?= $category->rght ?></td>
<td><?= h($category->name) ?></td>
<td><?= h($category->description) ?></td>
<td><?= h($category->created) ?></td>
<td class="actions">
<?= $this->Html->link(__('View'), ['action' => 'view', $category->id]) ?>
<?= $this->Html->link(__('Edit'), ['action' => 'edit', $category->id]) ?>
<?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $category->id], ['confirm' => __('Are you sure you want to delete # {0}?', $category->id)]) ?>
<?= $this->Form->postLink(__('Move down'), ['action' => 'moveDown', $category->id], ['confirm' => __('Are you sure you want to move down # {0}?', $category->id)]) ?>
<?= $this->Form->postLink(__('Move up'), ['action' => 'moveUp', $category->id], ['confirm' => __('Are you sure you want to move up # {0}?', $category->id)]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
ArticlesController
ではすべてのカテゴリーを一覧できます。
投稿記事を作成あるいは編集するときに、カテゴリーを選ぶことができるようになります。
// src/Controller/ArticlesController.php
namespace App\Controller;
// 3.6 より前は Cake\Network\Exception\NotFoundException を使用
use Cake\Http\Exception\NotFoundException;
class ArticlesController extends AppController
{
// ...
public function add()
{
$article = $this->Articles->newEmptyEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->getData());
if ($this->Articles->save($article)) {
$this->Flash->success(__('Your article has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add your article.'));
}
$this->set('article', $article);
// Just added the categories list to be able to choose
// one category for an article
$categories = $this->Articles->Categories->find('treeList')->all();
$this->set(compact('categories'));
}
}
投稿記事の add ファイルは以下のようになっているはずです。
<!-- File: templates/Articles/add.php -->
<h1>Add Article</h1>
<?php
echo $this->Form->create($article);
// ここにカテゴリーのコントロールを追加
echo $this->Form->control('category_id');
echo $this->Form->control('title');
echo $this->Form->control('body', ['rows' => '3']);
echo $this->Form->button(__('Save Article'));
echo $this->Form->end();
/yoursite/categories/add のアドレスに行くと、カテゴリーを選ぶための一覧を見れるでしょう。