Table of Contents

La classe SActiveRecord

ActiveRecord est un des designs patterns les plus simples pour faire de l'Object Relational Mapping. La définition qu'en donne Martin Fowler est :

An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.

Dans RubyOnRails, ce pattern est implémenté dans une seule classe (dôtée d'extensions) : afin de rechercher des enregistrements dans la base, on utilise une méthode statique find de la classe ActiveRecord. En raison de problèmes d'héritage avec les méthodes statiques en PHP5, il est difficile de conserver la même approche. Le pattern est ici donc implémenté à l'aide de 2 classes de base : SActiveRecord et SQuerySet, qui fournit une interface pour charger des enregistrements depuis la base. Les instances de classes héritant de SActiveRecord encapsulent les enregistrements dans la base, et possèdent des méthodes de persistence : save(), delete(), …

Concrètement, une classe héritant de SActiveRecord doit donc être créée pour chaque table dans la base.

class Article extends SActiveRecord
{
 
}
 
// Exemple d'utilisation :
$article = new Article();
$article->titre = 'Mon nouvel article';
$article->save();

Lors de son instanciation, la classe Article, qui ne possède pas d'attributs définis, va récupérer la liste des champs directement dans la table, et en déduire ses attributs. Vous pouvez alors créer un nouvel article, accéder à ses propriétés, et le sauver.

Manager

Lorsque vous générez une classe modèle à l'aide du script de génération, une propriété statique $objects est déclarée dans la classe :

class Article extends SActiveRecord
{
    public static $objects;
}

Lorsque votre classe est incluse par Stato (automatiquement via __autoload ou manuellement à l'aide de la classe SDependencies), une instance de la classe SManager sera assignée à cette propriété $objects. Cette classe vous permet notamment de créer de nouveaux objets, ou bien de faire des requêtes :

$article = Article::$objects->create(array('titre' => 'Mon article', 'corps' => 'Bla bla bla...'));
 
$all_articles = Article::$objects->all();

Surcharge des attributs

Tous les champs d'un enregistrement wrappé dans une instance de SActiveRecord sont accessibles par des propriétés publiques, mais vous pouvez dans certains cas avoir besoin de faire certains traitements lors de la lecture/écriture de ces propriétés. Pour cela, il vous suffit de créer 2 méthodes read_<propriété>() et write_<propriété>() qui seront automatiquement appellées lors de l'accès aux propriétés. Ces méthodes devront utiliser write_attribute($key, $value) et read_attribute($key) pour accéder aux valeurs des champs.

class Song extends SActiveRecord
{
    public function write_length($minutes)
    {
        $this->write_attribute('length', $minutes * 60);
    }
 
    public function read_length()
    {
        return $this->read_attribute('length') / 60;
    }
}

Associations

Toujours afin de faciliter le travail du développeur, il vous est possible de définir d'éventuelles associations avec d'autres tables.

class Article extends SActiveRecord
{
    public static $relationships = array
    (
        'comments' => 'has_many',
        'author'   => array('assoc_type' => 'belongs_to', 'class_name' => 'User')
    );
}

En savoir plus

Single table inheritance

SActiveRecord permet l'héritage de classes modèles en stockant le nom de la classe dans un champ nommé type par défaut (vous pouvez le changer en déclarant dans votre classe parente une propriété statique publique $inheritance_field).

class Company extends SActiveRecord { 
    public static $objects;
}
class Firm extends Company { }
class Client extends Company { }

Dans l'exemple ci-dessus, si vous créez une instance de la classe Firm, elle sera sauvée dans la table companies avec Firm comme valeur du champ type. De même, si vous faites un Company::$objects→get(…) et que la compagnie retournée est du type Client, une instance de Client vous sera renvoyée.

Important : la propriété statique $objects ne doit être déclarée que dans la classe parente !

Callbacks

Les callbacks sont des crochets (hooks ?) dans le cycle de vie d'un SActiveRecord qui vous permettent d'exécuter du code avant ou après un changement d'état de l'objet. Cela vous permet par exemple de “préparer” certains attributs avant validation (et ainsi de permettre plusieurs syntaxes pour un même attribut, comme un numéro de téléphone).

Validation

Vous pouvez implémenter une validation des paramètres de vos classes métier en surchargeant les méthodes validate(), validate_on_create() et validate_on_update(). L'appel de ces méthodes se fait automatiquement lors de la sauvegarde d'un SActiveRecord. Si la validation provoque des erreurs, la méthode save() renverra false et les erreurs seront disponibles dans la propriété $errors.

class User extends SActiveRecord
{
    public static $objects;
 
    public function validate()
    {
        $this->validate_presence_of('username', 'password', 'mail');
        $this->validate_format_of('mail', array('pattern' => 'email'));
        $this->validate_format_of('password', array('pattern' => '/^[a-z0-9]{6,12}$/i', 'message' => 'Only alphanumerical characters plz !'));
        $this->validate_length_of('username', array('min_length' => 4, 'max_length' => 20, 'message' => '4 to 20 chars plz !'));
        $this->validate_inclusion_of('sex', array('choices' => array('M', 'F')));
    }
 
    public function validate_on_create()
    {
        $this->validate_confirmation_of('password', array('message' => 'Please confirm your password !'));
        $this->validate_acceptance_of('terms_of_services');
    }
}

Reportez-vous à l'API pour le détail des méthodes de validation disponibles.

Timestamps

SActiveRecord peut enregister automatiquement les dates de création/mise à jour d'un objet si les champs created_on et updated_on sont présents dans la table et si la propriété $record_timestamps est true.

Générer une classe modèle

Vous pouvez utiliser le script generate.php pour générer une classe modèle (héritant de SActiveRecord) et un fichier de migration associé. Placez vous dans le répertoire de votre projet et utilisez la commande suivante :

php scripts/do.php generate model [model_name]

Par exemple :
php scripts/do.php generate model user
-> génèrera un fichier user.php dans app/models/ ainsi qu'un fichier 1_add_users.php dans db/migrate/