Entities
-
class
Cake\ORM\
Entity
Alors que les objets Table représentent et
fournissent un accès à une collection d’objets, les entities représentent des
lignes individuelles ou des objets de domaine dans votre application. Les
entities contiennent des propriétés et des méthodes persistantes pour manipuler
et accéder aux données qu’ils contiennent.
Les entities sont créées pour vous par CakePHP à chaque fois que vous utilisez
find()
sur un objet table.
Créer des Classes Entity
Vous n’avez pas besoin de créer des classes entity pour utiliser l’ORM dans
CakePHP. Cependant si vous souhaitez avoir de la logique personnalisée dans
vos entities, vous devrez créer des classes. Par convention, les classes
entity se trouvent dans src/Model/Entity/. Si notre application a une
table articles
, nous pourrions créer l’entity suivante:
// src/Model/Entity/Article.php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Article extends Entity
{
}
Pour l’instant cette entity ne fait pas grand chose. Cependant quand nous
chargeons les données de notre table articles, nous obtenons des instances
de cette classe.
Note
Si vous ne définissez pas de classe entity, CakePHP va utiliser la classe
de base Entity.
Créer des Entities
Les Entities peuvent être instanciées directement:
use App\Model\Entity\Article;
$article = new Article();
Lorsque vous instanciez une entity, vous pouvez lui passer des propriétés avec
les données que vous voulez y stocker:
use App\Model\Entity\Article;
$article = new Article([
'id' => 1,
'title' => 'New Article',
'created' => new DateTime('now')
]);
Une autre approche pour récupérer une nouvelle entity est d’utiliser la méthode
newEntity()
de l’objet Table
:
use Cake\ORM\TableRegistry;
// Prior to 3.6 use TableRegistry::get('Articles')
$article = TableRegistry::getTableLocator()->get('Articles')->newEntity();
$article = TableRegistry::getTableLocator()->get('Articles')->newEntity([
'id' => 1,
'title' => 'New Article',
'created' => new DateTime('now')
]);
Accéder aux Données de l’Entity
Les entities fournissent quelques façons d’accéder aux données qu’elles
contiennent. La plupart du temps, vous accéderez aux données dans une entity
en utilisant la notation objet:
use App\Model\Entity\Article;
$article = new Article;
$article->title = 'Ceci est mon premier post';
echo $article->title;
Vous pouvez aussi utiliser les méthodes get()
et set()
:
$article->set('title', 'Ceci est mon premier post');
echo $article->get('title');
Quand vous utilisez set()
, vous pouvez mettre à jour plusieurs propriétés
en une fois en utilisant un tableau:
$article->set([
'title' => 'Mon premier post',
'body' => "C'est le meilleur de tous!"
]);
Avertissement
Lors de la mise à jour des entities avec des données requêtées, vous
devriez faire une liste des champs qui peuvent être définis par
assignement de masse.
Accesseurs & Mutateurs
En plus de l’interface simple get/set, les entities vous permettent de fournir
des méthodes accesseurs et mutateurs. Ces méthodes vous laissent personnaliser
la façon dont les propriétés sont lues ou définies.
Les accesseurs utilisent la convention _get
suivi par la version en camel
case du nom du champ.
-
Cake\ORM\Entity::
get
($field)
Ils reçoivent la valeur basique stockée dans le tableau _properties
pour
seul argument.
Les accesseurs seront utilisés lors de la sauvegarde des entities. Faites donc
attention lorsque vous définissez des méthodes qui formatent les données car ce
sont ces données formatées qui seront sauvegardées. Par exemple:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Article extends Entity
{
protected function _getTitle($title)
{
return ucwords($title);
}
}
Les accesseurs seront utilisés quand vous récupérerez la propriété via une de
ces deux manières:
echo $article->title;
echo $article->get('title');
Vous pouvez personnaliser la façon dont les propriétés sont récupérées/définies
en définissant un mutateur:
-
Cake\ORM\Entity::
set
($field = null, $value = null)
Les méthodes mutateurs doivent toujours retourner la valeur qui doit être
stockée dans la propriété. Comme vous pouvez le voir au-dessus, vous pouvez
aussi utiliser les mutateurs pour définir d’autres propriétés calculées. En
faisant cela, attention à ne pas introduire de boucles, puisque CakePHP
n’empêchera pas les méthodes mutateur de faire des boucles infinies. Les
mutateurs vous permettent de convertir les propriétés lorsqu’elles sont définies
ou de créer des données calculées. Les mutateurs et accesseurs sont appliqués
quand les propriétés sont lues en utilisant la notation objet ou en utilisant
get() et set(). Par exemple:
namespace App\Model\Entity;
use Cake\ORM\Entity;
use Cake\Utility\Text;
class Article extends Entity
{
protected function _setTitle($title)
{
return Text::slug($title);
}
}
Les mutateurs seront utilisés lorsque vous définirez une propriété via une de
ces deux manières:
$user->title = 'foo' // slug sera aussi défini
$user->set('title', 'foo'); // slug sera aussi défini
Avertissement
Les accesseurs sont également appelés avant que l’entity ne soit persistée
en base. Si vous souhaitez transformer un champ mais ne pas persister la
transformation, il est recommandé d’utiliser les propriétés virtuelles car
ces dernières ne seront pas persistées.
Créer des Propriétés Virtuelles
En définissant des accesseurs, vous pouvez fournir un accès aux
champs/propriétés qui n’existent pas réellement. Par exemple si votre table
users a first_name
et last_name
, vous pouvez créer une méthode pour le
nom complet:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity
{
protected function _getFullName()
{
return $this->_properties['first_name'] . ' ' .
$this->_properties['last_name'];
}
}
Vous pouvez accéder aux propriétés virtuelles puisqu’elles existent sur
l’entity. Le nom de la propriété sera la version en minuscule et en underscore
de la méthode:
Souvenez-vous que les propriétés virtuelles ne peuvent pas être utilisées dans
les finds. Si vous voulez que les propriétés virtuelles fassent parties des
données affichées lorsque vous affichez les représentations JSON ou en tableau
de vos entités, reportez-vous à la section Montrer les Propriétés Virtuelles.
Vérifier si une Entity a été Modifiée
-
Cake\ORM\Entity::
dirty
($field = null, $dirty = null)
Vous pourriez vouloir écrire du code conditionnel basé sur si oui ou non
les propriétés ont été modifiées dans l’entity. Par exemple, vous pourriez
vouloir valider uniquement les champs lorsqu’ils ont été modifiés:
// Vérifie si le champ title n'a pas été modifié.
$article->dirty('title');
Vous pouvez également marquer un champ comme ayant été modifié. C’est pratique
lorsque vous ajoutez des données dans un tableau de propriétés:
// Ajoute un commentaire et marque le champ comme modifié.
$article->comments[] = $newComment;
$article->dirty('comments', true);
De plus, vous pouvez également baser votre code conditionnel sur les valeurs
initiales des propriétés en utilisant la méthode getOriginal()
. Cette
méthode retournera soit la valeur initiale de la propriété si elle a été
modifiée soit la valeur actuelle.
Vous pouvez également vérifier si une des propriétés de l’entity a été
modifiée:
// Vérifier si l'entity a changé
$article->dirty();
Pour retirer le marquage dirty des champs d’une entity, vous pouvez utiliser
la méthode clean()
:
Lors de la création d’un nouvelle entity, vous pouvez empêcher les champs
d’être marqués dirty en passant une option supplémentaire:
$article = new Article(['title' => 'New Article'], ['markClean' => true]);
Pour récupérer la liste des propriétés dirty (modifiées) d’une Entity
,
vous pouvez utiliser la méthode getDirty()
:
$dirtyFields = $entity->getDirty();
Nouveau dans la version 3.4.3: getDirty()
a été ajoutée.
Erreurs de Validation
-
Cake\ORM\Entity::
errors
($field = null, $errors = null)
Après avoir sauvegardé une entity toute erreur de
validation sera stockée sur l’entity elle-même. Vous pouvez accéder à toutes
les erreurs de validation en utilisant les méthodes getErrors()
et
getError()
:
// Récupère toutes les erreurs
$errors = $user->getErrors();
// Avant 3.4.0
$errors = $user->errors();
// Récupère les erreurs pour un champ unique.
$errors = $user->getError('password');
// Avant 3.4.0
$errors = $user->errors('password');
Les méthodes setErrors()
et setError()
peuvent aussi être utilisées
pour définir les erreurs sur une entity, facilitant les tests du code qui
fonctionne avec les messages d’erreur:
$user->setError('password', ['Password is required.']);
$user->setErrors(['password' => ['Password is required'], 'username' => ['Username is required']]);
// Avant 3.4.0
$user->errors('password', ['Password is required.']);
Assignement de Masse
Alors que la définition des propriétés des entities en masse est simple et
pratique, elle peut créer des problèmes importants de sécurité.
Assigner en masse les données d’utilisateur à partir de la requête dans une
entity permet à l’utilisateur de modifier n’importe quelles (voir toutes) les
colonnes. Quand vous utilisez les classes entity anonymes, CakePHP ne protège
pas contre l’assignement en masse. Vous pouvez vous protéger de l’assignement de
masse en utilisant la commande bake pour générer vos entities.
La propriété _accessible
vous permet de fournir une liste des champs et
si oui ou non ils peuvent être assignés en masse. Les valeurs true
et
false
indiquent si un champ peut ou ne peut pas être assigné massivement:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Article extends Entity
{
protected $_accessible = [
'title' => true,
'body' => true,
];
}
En plus des champs réels, il existe un champ spécial *
qui définit le
comportement par défaut si un champ n’est pas nommé spécifiquement:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Article extends Entity
{
protected $_accessible = [
'title' => true,
'body' => true,
'*' => false,
];
}
Note
Si la propriété *
n’est pas définie, elle sera par défaut à false
.
Eviter la Protection Contre l’Assignement de Masse
lors de la création d’un nouvelle entity via le mot clé new
vous pouvez
lui spécifier de ne pas se protéger contre l’assignement de masse:
use App\Model\Entity\Article;
$article = new Article(['id' => 1, 'title' => 'Foo'], ['guard' => false]);
Modifier les Champs Protégés à l’Exécution
Vous pouvez modifier la liste des champs protégés à la volée en utilisant la
méthode accessible
:
// Rendre user_id accessible.
$article->accessible('user_id', true);
// Rendre title protégé.
$article->accessible('title', false);
Note
Modifier des champs accessibles agit seulement sur l’instance de la
méthode sur laquelle il est appelé.
Lorsque vous utilisez les méthodes newEntity()
et patchEntity()
dans
les objets Table
vous avez également le contrôle sur la protection de
masse. Référez vous à la section to the Changer les Champs Accessibles
pour plus d’information.
Outrepasser la Protection de Champ
Il arrive parfois que vous souhaitiez permettre un assignement en masse aux
champs protégés:
$article->set($properties, ['guard' => false]);
En définissant l’option guard
à false
. vous pouvez ignorer la liste des
champs accessibles pour un appel unique de set()
.
Vérifier si une Entity a été Sauvegardée
Il est souvent nécessaire de savoir si une entity représente une ligne qui
est déjà présente en base de données. Pour cela, utilisez la méthode
isNew()
:
if (!$article->isNew()) {
echo 'Cette article a déjà été sauvegardé!';
}
Si vous êtes certains qu’une entity a déjà été sauvegardée, vous pouvez
utiliser isNew()
en tant que setter:
$article->isNew(false);
$article->isNew(true);
Lazy Loading des Associations
Alors que les associations chargées en eager loading sont généralement la
façon la plus efficace pour accéder à vos associations, il peut arriver que
vous ayez besoin d’utiliser le lazy loading des données associées. Avant de
voir comment utiliser le Lazy loading d’associations, nous devrions
discuter des différences entre le chargement des associations eager et lazy:
- Eager loading
Le Eager loading utilise les joins (si possible) pour récupérer les
données de la base de données avec aussi peu de requêtes que possible.
Quand une requête séparée est nécessaire comme dans le cas d’une
association HasMany, une requête unique est émise pour récupérer toutes
les données associées pour l’ensemble courant d’objets.
- Lazy loading
Le Lazy loading diffère le chargement des données de l’association jusqu’à
ce que ce soit complètement nécessaire. Alors que ceci peut sauver du temps
CPU car des données possiblement non utilisées ne sont pas hydratées dans
les objets, cela peut résulter en plus de requêtes émises vers la base de
données. Par exemple faire des boucles sur un ensemble d’articles et leurs
commentaires va fréquemment émettre N requêtes où N est le nombre d’articles
étant itérés.
Bien que le lazy loading n’est pas inclus dans l’ORM de CakePHP, vous pouvez
utiliser un des plugins de la communauté. Nous recommandons le plugin LazyLoad
Après avoir ajouté le plugin à votre entity, vous pourrez le faire avec ce qui
suit:
$article = $this->Articles->findById($id);
// La propriété comments a été chargé en lazy
foreach ($article->comments as $comment) {
echo $comment->body;
}
Créer du Code Réutilisable avec les Traits
Vous pouvez vous retrouver dans un cas où vous avez besoin de la même logique
dans plusieurs classes d’entity. Les traits de PHP sont parfaits pour cela.
Vous pouvez mettre les traits de votre application dans src/Model/Entity.
Par convention, les traits dans CakePHP sont suffixés avec Trait
pour
qu’ils soient discernables des classes ou des interfaces. Les traits sont
souvent un bon allié des behaviors, vous permettant de fournir des
fonctionnalités pour la table et les objets entity.
Par exemple si nous avons un plugin SoftDeletable, il pourrait fournir un trait.
Ce trait pourrait donner des méthodes pour rendre les entities comme
“supprimé”, la méthode softDelete
pourrait être fournie par un trait:
// SoftDelete/Model/Entity/SoftDeleteTrait.php
namespace SoftDelete\Model\Entity;
trait SoftDeleteTrait
{
public function softDelete()
{
$this->set('deleted', true);
}
}
Vous pourriez ensuite utiliser ce trait dans votre classe entity en l’intégrant
et en l’incluant:
namespace App\Model\Entity;
use Cake\ORM\Entity;
use SoftDelete\Model\Entity\SoftDeleteTrait;
class Article extends Entity
{
use SoftDeleteTrait;
}
Convertir en Tableaux/JSON
Lors de la construction d’APIs, vous avez peut-être besoin de convertir des
entities en tableaux ou en données JSON. CakePHP facilite cela:
// Obtenir un tableau.
// Les associations seront aussi converties avec toArray().
$array = $user->toArray();
// Convertir en JSON
// Les associations seront aussi converties avec le hook jsonSerialize.
$json = json_encode($user);
Lors de la conversion d’une entity en JSON, les listes de champ virtuel & caché
sont utilisées. Les entities sont converties aussi de façon récursive en JSON.
Cela signifie que si les entities et leurs associations sont chargées en eager
loading, CakePHP va correctement gérer la conversion des données associées dans
le bon format.
Montrer les Propriétés Virtuelles
Par défaut, les propriétés virtuelles ne sont pas exportées lors de la
conversion des entities en tableaux ou JSON. Afin de montrer les propriétés
virtuelles, vous devez les rendre visibles. Lors de la définition de votre
classe entity, vous pouvez fournir une liste de propriétés virtuelles qui
doivent être exposées:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity
{
protected $_virtual = ['full_name'];
}
Cette liste peut être modifiée à la volée en utilisant virtualProperties
:
$user->virtualProperties(['full_name', 'is_admin']);
Cacher les Propriétés
Il arrive souvent que vous ne souhaitiez pas exporter certains champs dans
des formats JSON ou tableau. Par exemple il n’est souvent pas sage de montrer
les hashs de mot de passe ou les questions pour retrouver son compte. Lors
de la définition d’une classe entity, définissez les propriétés qui doivent
être cachées:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity
{
protected $_hidden = ['password'];
}
Cette liste peut être modifiée à la volée en utilisant hiddenProperties
:
$user->hiddenProperties(['password', 'recovery_question']);
Stocker des Types Complexes
Les méthodes « accesseurs » et « mutateurs » n’ont pas pour objectif de contenir de
la logique pour sérialiser et desérialiser les données complexes venant de la
base de données. Consultez la section Sauvegarder les Types Complexes pour
comprendre la façon dont votre application peut stocker des types de données
complexes comme les tableaux et les objets.