Continuons notre application de blog et imaginons que nous souhaitions catégoriser nos articles. Nous souhaitons que les catégories soit triées, et pour cela, nous allons utiliser le behavior Tree pour nous aider à organiser les catégories.
Mais d’abord, nous devons modifier nos tables.
Nous voulons utiliser le plugin migrations pour créer une table dans notre base de données. Si vous avez déjà une table articles dans votre base de données, supprimez-la.
Maintenant ouvrez le fichier composer.json de votre application. Normalement
vous devriez voir que le plugin migrations est déjà dans require
. Si ce
n’est pas le cas, ajoutez-le en utilisant:
composer require cakephp/migrations:~1.0
Le plugin migrations va maintenant être dans le dossier plugins de votre
application. Ajoutez aussi Plugin::load('Migrations');
dans le fichier
bootstrap.php de votre application.
Une fois que le plugin est chargé, lancez la commande suivante pour créer un fichier de migration:
bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified
Un fichier de migration sera généré dans le dossier config/Migrations avec ce qui suit:
<?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();
}
}
Exécutez une autre commande pour créer une table categories
. Si vous voulez
spécifier une longueur de champ, vous pouvez le faire entre crochets dans le
type du champ, par exemple:
bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified
Ceci va générer le fichier suivant dans 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();
}
}
Maintenant que les fichiers de migration sont créés, vous pouvez les modifier
avant de créer vos tables. Nous devons changer 'null' => false
pour
le champ parent_id
par 'null' => true
car une catégorie de niveau
supérieur a un parent_id
null.
Exécutez la commande suivante pour créer vos tables:
bin/cake migrations migrate
Avec nos tables définies, nous pouvons maintenant nous focaliser sur la catégorisation de nos articles.
Nous supposons que vous avez déjà les fichiers (Tables, Controllers et Templates des Articles) de la partie 2. Donc nous allons juste ajouter les références aux categories.
Nous devons associer ensemble les tables Articles et Categories. Ouvrez le fichier src/Model/Table/ArticlesTable.php et ajoutez ce qui suit:
// 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');
// Ajoute juste la relation belongsTo avec CategoriesTable
$this->belongsTo('Categories', [
'foreignKey' => 'category_id',
]);
}
}
Créez tous les fichiers en lançant les commandes de bake suivantes:
bin/cake bake model Categories
bin/cake bake controller Categories
bin/cake bake template Categories
De manière alternative, vous pouvez créer la totalité avec une seule ligne:
bin/cake bake all Categories
L’outil bake a créé tous les fichiers en un clin d’œil. Vous pouvez les lire rapidement si vous voulez vous re-familiariser avec le fonctionnement de CakePHP.
Note
Si vous utilisez Windows, pensez à utiliser \ à la place de /.
Vous devrez modifier ce qui suit dans src/Template/Categories/add.ctp et src/Template/Categories/edit.ctp:
echo $this->Form->control('parent_id', [
'options' => $parentCategories,
'empty' => 'Pas de catégorie parente'
]);
Le TreeBehavior vous aide à gérer des structures hiérarchiques en arbre dans une table de base de données. Il utilise MPTT logic pour gérer les données. Les structures en arbre MPTT sont optimisées pour lire des données ce qui les rend souvent pratique pour lire des applications lourdes comme les blogs.
Si vous ouvrez le fichier src/Model/Table/CategoriesTable.php, vous verrez
que le TreeBehavior a été attaché à votre CategoriesTable dans la méthode
initialize()
. Bake ajoute automatiquement ce behavior à toutes les Tables
qui contiennent les colonnes lft
et rght
:
$this->addBehavior('Tree');
Avec le TreeBehavior attaché, vous serez capable d’accéder à quelques fonctionnalités comme la réorganisation de l’ordre des categories. Nous verrons cela dans un moment.
Mais pour l’instant, vous devez retirer les lignes suivantes dans vos fichiers de template add et edit:
echo $this->Form->control('lft');
echo $this->Form->control('rght');
De plus, vous devez désactiver ou retirer les requirePresence du validateur
pour lft
et rght
dans votre model CategoriesTable:
public function validationDefault(Validator $validator)
{
$validator
->add('id', 'valid', ['rule' => 'numeric'])
->allowEmpty('id', 'create');
$validator
->add('lft', 'valid', ['rule' => 'numeric'])
// ->requirePresence('lft', 'create')
->notEmpty('lft');
$validator
->add('rght', 'valid', ['rule' => 'numeric'])
// ->requirePresence('rght', 'create')
->notEmpty('rght');
}
Ces champs sont automatiquement gérés par le TreeBehavior quand une catégorie est sauvegardée.
En utilisant votre navigateur, ajoutez quelques nouvelles catégories en
utilisant l’action du controller /yoursite/categories/add
.
Dans votre fichier de template index des catégories, vous pouvez lister les catégories et les réordonner.
Modifiez la méthode index dans votre CategoriesController.php et ajoutez les
méthodes moveUp()
et moveDown()
pour pouvoir réorganiser l’ordre des
catégories dans l’arbre:
class CategoriesController extends AppController
{
public function index()
{
$categories = $this->Categories->find()
->order(['lft' => 'ASC']);
$this->set(compact('categories'));
$this->set('_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']));
}
}
Remplacez le contenu existant dans src/Template/Categories/index.ctp par ceci:
<div class="actions large-2 medium-3 columns">
<h3><?= __('Actions') ?></h3>
<ul class="side-nav">
<li><?= $this->Html->link(__('Nouvelle Categorie'), ['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(__('Voir'), ['action' => 'view', $category->id]) ?>
<?= $this->Html->link(__('Editer'), ['action' => 'edit', $category->id]) ?>
<?= $this->Form->postLink(__('Supprimer'), ['action' => 'delete', $category->id], ['confirm' => __('Etes vous sur de vouloir supprimer # {0}?', $category->id)]) ?>
<?= $this->Form->postLink(__('Descendre'), ['action' => 'moveDown', $category->id], ['confirm' => __('Etes vous sur de vouloir descendre # {0}?', $category->id)]) ?>
<?= $this->Form->postLink(__('Monter'), ['action' => 'moveUp', $category->id], ['confirm' => __('Etes vous sur de vouloir monter # {0}?', $category->id)]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
Dans notre ArticlesController
, nous allons récupérer la liste de toutes les
catégories. Ceci va nous permettre de choisir une catégorie pour un Article
lorsque l’on va le créer ou le modifier:
// src/Controller/ArticlesController.php
namespace App\Controller;
// Prior to 3.6 use Cake\Network\Exception\NotFoundException
use Cake\Http\Exception\NotFoundException;
class ArticlesController extends AppController
{
// ...
public function add()
{
$article = $this->Articles->newEntity();
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);
// Ajout de la liste des catégories pour pouvoir choisir
// une catégorie pour un article
$categories = $this->Articles->Categories->find('treeList');
$this->set(compact('categories'));
}
}
Le fichier add des articles devrait ressembler à ceci :
<!-- File: src/Template/Articles/add.ctp -->
<h1>Add Article</h1>
<?php
echo $this->Form->create($article);
// Ajout des input (via la méthode "control") liés aux catégories
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();
Quand vous allez à l’adresse /yoursite/categories/add
, vous devriez voir une
liste des catégories à choisir.