Instanciar clase hija basada en campo de modelo padre (CActiveRecord)

You are viewing revision #2 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.

« previous (#1)

EN(http://www.yiiframework.com/wiki/413/) [ES]

A veces nos encontramos en circunstancias como ésta:

class TipoAnimal extends CActiveRecord {} // Tabla con los "tipos" de Animales

class Animal extends CActiveRecord { // Clase del modelo padre
  // Modelo basado en una tabla con un campo: tipo_id
  // que guarda el "tipo" de Animal
}

class Gato extends Animal {} // Clase hija

class Perro extends Animal {}

Y nos gustaría hacer algo como:

// Esta instrucción devuelve una clase "Gato"
$algunAnimal = Animal::model()->findByPk(1);

Ya que Yii implementa un modelo de desarrollo llamado "Factory", es muy fácil implementar una solución rápida a este escenario.

Paso 1 (y único) Sobrecargar la función instantiate(). Incluí los comentarios originales de la función CActiveRecord

/**
     * Creates an active record instance.
     * This method is called by {@link populateRecord} and {@link populateRecords}.
     * You may override this method if the instance being created
     * depends the attributes that are to be populated to the record.
     * For example, by creating a record based on the value of a column,
     * you may implement the so-called single-table inheritance mapping.
     * @param array $attributes list of attribute values for the active records.
     * @return MiClase the active record
     */
    protected function instantiate($attributes)
    {
        if (!isset($attributes['tipo_id'])) { // Cuando se crea and $tipo_id no se ha definido
            return parent::instantiate($attributes);
        }
        $classRecord = AnimalType::model()->findByPk($attributes['tipo_id']); // Cargar el "Tipo"
        if($classRecord === null)
            throw new CException('Tipo de Animal no encontrado');
        $className = $classRecord->nombre; // Asumiendo que el campo "nombre" almacena el nombre de la clase hija
        $model = new $className(null); // El parámetro NULL es obligatorio
        return $model;
    }

Bastante sencillo, ¿verdad? A disfrutar y compartir :)