Le modèle : ActiveRecord

Introduction

Les modèles permettent d’accéder aux données et gèrent la logique de l’application. Les modèles sont responsables de la récupération, de la validation et de l’enregistrement des données.

ActiveRecord est l'ORM de Bluebird. Si votre modèle est lié à une table de la base de données, vous pouvez étendre la classe de base ActiveRecord pour définir votre classe de modèle..

Démarrage rapide

Imaginons une base de données avec les tables suivantes :

post table
CREATE TABLE `post` (
  `post_id` INT NULL AUTO_INCREMENT DEFAULT NULL,
  `title` VARCHAR(100) NULL DEFAULT NULL,
  `body` MEDIUMTEXT NULL DEFAULT NULL,
  `publication_date` DATE NULL DEFAULT NULL,
  PRIMARY KEY (`post_id`)
);

La déclaration d'un modèle est très simple :

namespace models;

class Post extends ActiveRecord {}

Le fichier doit être nommé post.class.php et être placé dans le répertoire application\models.

A présent, vous pouvez accéder aux billets via le modèle Post :

use models\Post;

//Création
$post = new Post( ['title' => 'Hello', 'body' => 'How are you ?', 'publication_date' => date( 'Y-m-d' )] );
$post->save();

//Lecture
$post = Post::findOne( 1 );

//Mise à jour
$post->publication_date = date( 'Y-m-d', strtotime( '+ 1 week' ) );
$post->save();

//Suppression
$post->delete();

Conventions

Par défaut le nom de la table de la base de données est automatiquement déduit à partir du nom de la classe.

use \ActiveRecord;

class User extends ActiveRecord {}

class Post extends ActiveRecord {}

Par défaut le nom de la clé primaire est automatiquement déduit à partir du nom de la table. Le format attendu est <tablename>_id.

$user = User::findOne( 1 );

exécutera la requête suivante :

SELECT * FROM `user` where `user_id` = 1;

Surcharger les conventions

Il est possible de préciser le nom de la table, s'il est différent de celui de la classe :

class Post extends ActiveRecord {

    protected static $tableName = 'blog_post';

}

Il est possible de préciser le nom de la clé primaire :

class Post extends ActiveRecord {

    protected static $tableName = 'blog_post';

    protected static $primaryKey = 'post_id';

}

CRUD : lire et écrire des données

Création

Voici comment créer un nouveau billet en instanciant un nouvel objet et en appelant la méthode save() :

//Création
$post = new Post();
$post->title = 'Hello';
$post->body = 'How are you ?'
$post->publication_date = date( 'Y-m-d' );
$post->save();

La requête SQL suivante sera exécutée :

INSERT INTO `post` (`title`, `body`, `publication_date`)
VALUES ('Hello', 'How are you ?', '2022-12-21');

Deuxième méthode :

$post = new Post( ['title' => 'Hello', 'body' => 'How are you ?', 'publication_date' => date( 'Y-m-d' )] );
$post->save();

Lecture

findOne

La méthode statique findOne retourne un seul enregistrement. Elle peut prendre en paramètre l'identifiant (valeur de la clé primaire) :

$post = Post::findOne( 1 );
echo $post->title; //Hello

Elle accepte également un ensemble de critères :

$post = Post::findOne( ['title' => 'Hello'] );

findMany

La méthode statique findMany retourne une liste d'enregistrements correspondant aux critères fournis.

$posts = Post::findMany( ['is_published' => true, 'publication_date[<]' => date( 'Y-m-d' )] );

Voire la section Requêtes pour plus de détails.

Mise à jour

Pour la mise à jour, il suffit de récupérer un enregistrement, de modifier ses attributs puis d'appeler la méthode save(). L'objet conserve la liste des attributs modifiés. Seuls les attributs réellement modifiés seront mis à jour.

$post = Post::findOne( 1 );
echo $post->title; //Hello
$post->title = 'Hello, everybody !';
$post->save();

La requête SQL suivante sera exécutée :

UPDATE `post` SET `title` = 'Hello, everybody !' WHERE `post_id` = '1';

Suppression

L'appel de la méthode delete() permet de supprimer l'enregistrement en base de données :

$post = Post::findOne( 1 );
$post->delete();

La requête SQL suivante sera exécutée :

DELETE FROM `post` WHERE ((`post_id` = '1'));

findOrNew

La méthode statique findOrNew() récupère un enregistrement correspondant aux critères fournis, et crée un nouvel objet s'il n'existe pas. Le premier argument est un tableau associatif contenant les critères de la requête. Le second argument permet de renseigner les attributs lors de la création.

$page = Page::findOrNew( ['name' => 'Home'], ['body' => 'Welcome !'] );

updateOrCreate

La méthode statique updateOrCreate() met à jour un enregistrement correspondant aux critères fournis ou crée un enregistrement s'il n'existe pas. Le premier argument est un tableau associatif contenant les critères de la requête. Le second argument permet de renseigner les attributs lors de la création.

Page::updateOrCreate( ['name' => 'Home'], ['body' => 'Welcome !'] );
SELECT * FROM `page` where `name` = 'Home';
UPDATE `page` SET `body` = 'Hello, everybody !' WHERE `name` = 'Home';
INSERT INTO `page` (`name`, `body`) VALUES ('Home', 'Welcome !');

Validations

use \ActiveRecord;

class User extends ActiveRecord {

    protected function validate() {
        $this->validateNotEmpty( 'username', 'Username cannot be empty.' );
    }
}

$user = new User();
echo $user->save(); //false

$user->getErrors();

Array
(
    [username] => Array
        (
            [0] => Username cannot be empty.
        )

)

Vous pouvez en savoir plus dans le guide Validations.

Horodatage automatique

Si la table contient des champs created_at et updated_at ceux-ci sont automatiquement renseignés lors des opérations de création ou de mise à jour.

Il est possible de désactiver ce comportement en passant la variable statique $recordTimestamps à false :

$post = Post::findOne( 1 );
$post::$recordTimestamps = false;
$post->increment( 'view_count' );
$post::$recordTimestamps = true;

Sérialisation JSON

ActiveRecord implémente l'interface JsonSerializable. Les données du modèle peuvent donc être sérialisées en JSON par appel à JSONHandler::encode() (ou avec la fonction PHP native json_encode()) :

$json = JSONHandler::encode( $post );
echo $json;
{
    "post_id":"1",
    "title":"Hello",
    "body":"How are you ?",
    "publication_date":"2022-12-28"
}

Attention: par défaut, seuls les champs de la table sont présents dans le JSON. Les attributs virtuels ne sont pas retournés. Pour retourner des informations supplémentaires, il est nécessaire de surcharger la méthode jsonSerialize().