Créer un behaviour permet d’appliquer un même comportement sur plusieurs modèles différents, en partageant la même base de code réutilisable.
Les behaviours sont une extension du mécanisme d’observers présents dans FuelPHP. Ils permettent notamment (comme pour les observers), d’écouter les notifications before_* et after_* déclenchées par FuelPHP.
Les behaviours permettent deux fonctionnalités supplémentaires :
Voici quelques exemples pour illustrer ces propos :
On citera :
Pour créer un behaviour, il faut créer une classe qui étend Nos\Orm_Behaviour, puis implémenter :
Vous pouvez ajouter n’importe quelle classe en tant que Behaviour en ajoutant son nom de classe complet dans la propriété $_behaviours de votre modèle.
Comme avec un observer FuelPHP, les modèles peuvent spécifier un tableau de configuration dans leur définition des behaviours. Cette configuration sera disponible dans la variable $this->_properties à l’intérieur du behaviour.
<?php
class Model_Monkey extends Nos\Orm\Model
{
protected static $_behaviours = array(
'My_Behaviour' => array(
'my_key' => 'my_value',
),
);
}
<?php
class My_Behaviour extends Nos\Behaviour
{
/*
* Dans n'importe quelle méthode, $this->_properties aura la valeur :
* array(
* 'my_key' => 'my_value',
* )
*/
}
Ce sont les évènements de type before_* et after_*.
Par exemple, Behaviour_Author stocke l’ID des utilisateurs ayant créé / modifié un item dans une colonne du modèle grâce (respectivement) aux évènements before_insert et before_save fournis par l’ORM de FuelPHP.
<?php
class Orm_Behaviour_Author extends Orm_Behaviour
{
public function before_insert(\Nos\Orm\Model $item)
{
$created_by_property = \Arr::get($this->_properties, 'created_by_property', null);
if ($created_by_property === null) {
return;
}
$user = \Session::user();
if (!empty($user)) {
$item->{$created_by_property} = $user->user_id;
}
}
}
De la même manière que pour les observers, il faut implémenter une méthode qui porte le même nom de l’évènement déclenché.
Par exemple, pour écouter l’évènement form_processing, on implémentera la méthode form_processing().
La différence avec les évènements déclenchés par FuelPHP réside dans les paramètres envoyées à ces méthodes :
Là où les méthodes d’Observer (before_* et after_*) prennent un unique paramètre $item (instance du modèle), les évènements déclenchés par Novius OS peuvent en prendre plusieurs, et dépendent du type d’évènement.
Il existe deux types d’évènements :
La liste des évènements (d’instance et statiques) est disponible dans la documentation d’API.
Un évènement est appelé sur tous les Behaviour qui ont implémenté la méthode correspondante. La valeur de retour de ces méthodes n’a pas d’importance : les évènements utilisent les arguments passés par référence pour agir.
Exemple avec l’évènement d’instance form_processing (déclenché lors de la sauvegarde d’un item via le CRUD).
<?php
class My_Behaviour extends Nos\Behaviour
{
public function form_processing(Nos\Orm\Model $item, $data, &$json_repsonse)
{
// Exemples :
// On remplit des valeurs à sauvegarder dans l'item
// On rajoute une clé dans le tableau JSON
}
}
// Pour information : en interne, Novius OS fait appel à cet évènement via ce code suivant :
$item->event('form_processing', array($data, &$json_response));
Exemple avec l’évènement statique crudConfig
<?php
class My_Behaviour extends Nos\Behaviour
{
public function crudConfig(&$config, $controller)
{
// Exemples :
// On rajoute un champ en modifiant $config['fields']
}
}
// Pour information : en interne, Novius OS fait appel à cet évènement via ce code suivant :
Model_Class::eventStatic('crudConfig', $config, $controller);
De la même manière que les évènements déclenchés par FuelPHP et les évènements d’instance, les méthodes dynamiques portent le même nom que la méthode à rajouter sur le modèle et prennent en premier paramètre $item, l’instance du modèle.
Contrairement aux évènements, une méthode retourne généralement une valeur.
Par exemple, le Behaviour_Contextable dans Novius OS rajoute la méthode get_context() sur les modèles qui l’utilisent :
<?php
// Model file
class Model_Monkey extends Nos\Orm\Model
{
protected static $_behaviours = array(
'Orm_Behaviour_Contextable' => array(
'context_property' => 'monk_context',
),
);
}
// Behaviour file
class Orm_Behaviour_Contextable extends Nos\Behaviour
{
public function get_context(Orm\Model $item)
{
return $item->get($this->_properties['context_property']);
}
}
// Use case
$monkey = Model_Monkey::find('first');
// Cette méthode est disponible parce que Model_Monkey utilise Behaviour_Contextable, qui la rajoute
$context = $monkey->get_context();
De la même façon que pour une méthode d’instance mais plus besoin du premier paramètre $item.
<?php
// Model file
class Model_Monkey extends Nos\Orm\Model
{
protected static $_behaviours = array(
'Orm_Behaviour_Twinnable' => array(
'context_property' => 'monk_context',
'common_id_property' => 'monk_context_common_id',
'is_main_property' => 'monk_context_is_main',
'common_fields' => array('monk_species_common_id', 'monk_birth_year'),
),
);
}
// Behaviour file
class Orm_Behaviour_Twinnable extends Nos\Behaviour
{
public function hasCommonFields()
{
$class = $this->_class;
return count($this->_properties['common_fields']) > 0 ||
static::sharedWysiwygsContext($class) > 0 ||
static::sharedMediaContext($class) > 0;
}
}
// Use case
Model_Monkey::hasCommonFields();