miércoles, 21 de enero de 2015

Comportamientos (Behaviors)

Los comportamientos son instancias de  yii\base\Behavior, o de una clase hija. Los comportamientos, también conocidos como mixins, permiten mejorar la funcionalidad de un componente existente sin necesidad de cambiar la herencia de la clase. Adjuntando un comportamiento a un componente se le "inyectan" los métodos y propiedades del comportamiento, se logra que esos métodos y propiedades accesibles como si estuvieran definidos en la propia clase componente. Por otra parte, un comportamiento puede responder a los acontecimientos desencadenados por el componente, que permite a los comportamientos para personalizar también la ejecución normal de código del componente.

Definiendo Comportamientos

Para definir un comportamiento, se debe crear una clase de yii\base\Behavior, o en su defecto, de una clase hija de la misma. Por ejemplo:

namespace app\components;

use yii\base\Behavior;

class MyBehavior extends Behavior{
    public $prop1;

    private $_prop2;

    public function getProp2()
    {
        return $this->_prop2;
    }

    public function setProp2($value)
    {
        $this->_prop2 $value;
    }

    public function foo()
    {
        // ...
    }
}

El código presentado define una clase Comportamiento app\components\MyBehavior con dos propiedades, con dos propiedades y un método (prop1, prop2, foo), Nótese que la propiedad prop2 se define a través del captador getProp2 y el definidor setProp2(). Esto se debe a que  yii\base\Behavior se extiende de yii\base\Object y por lo tanto soporta la definición de propiedades a través de captadores (getter) y definidores (setter).

Debido a que ésta clase es un comportamiento (behavior), cuando sea añadido a un componente, el componente también tendrá las propiedades prop1 y prop2 y el método foo().

Consejo: Dentro del comportamiento, se puede acceder al componente al cual está adjunto, a través de la propiedad yii\base\Behavior::$owner

Gestionando Eventos de Componentes

Si un comportamiento necesita responder a los eventos disparados por el componente al cual está unido, se debe sobrescribir el método yii\base\Behavior::events(). Por ejemplo:

namespace app\components;

use yii\db\ActiveRecord;
use yii\base\Behavior;

class MyBehavior extends Behavior{
    // ...

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
        ];
    }

    public function beforeValidate($event)
    {
        // ...
    }
}

El método events() debe retornar una lista de eventos y sus correspondientes controladores. El ejemplo anterior declara que el evento EVENT_BEFORE_VALIDATE existe y define su controlador  beforeValidate(). Al especificar un controlador de eventos, se puede utilizar uno de los siguientes formatos:

  • una cadena que se refiere al nombre de un método de la clase comportamiento, como en el ejemplo anterior
  • un arreglo de un objeto o nombre de clase, y un nombre de método como una cadena (sin paréntesis), por ejemplo, [$object, 'methodName'];
  • una función anónima

La firma de un controlador de eventos es la siguiente,

function ($event) {
}

donde $event se refiere al parámetro del evento.

Adjuntando Comportamientos

Se puede adjuntar un comportamiento de forma estática o dinámica. La primera opción es la más común.

Para adjuntar un comportamiento de manera estática, se debe sobrescribir el método behaviors() de la clase componente al que se adjunta el comportamiento. El método behaviors()debe devolver una lista de configuraciones del comportamiento. Cada configuración del comportamiento puede ser un nombre de clase comportamiento o un arreglo de configuración:

namespace app\models;

use yii\db\ActiveRecord;
use app\components\MyBehavior;

class User extends ActiveRecord{
    public function behaviors()
    {
        return [
            // comportamiento anónimo, sólo nombre de la clase
            MyBehavior::className(),

            // comportamiento con nombre, sólo nombre de la clase
            'myBehavior2' => MyBehavior::className(),

            // comportamiento anónimo, arreglo de configuración
            [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ],

            // comportamiento con nombre, arreglo de configuración
            'myBehavior4' => [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ]
        ];
    }
}

Se puede asociar un nombre con un comportamiento especificando la clave del arreglo correspondiente a la configuración del comportamiento. En este caso, el comportamiento se denomina un comportamiento con nombre. En el ejemplo anterior, hay dos comportamientos con nombre: myBehavior2 y myBehavior4 . Si un comportamiento no está asociado con un nombre, se llama un comportamiento anónimo.

Para adjuntar un comportamiento dinámicamente, hay que llamar al método yii\base\Component::attachBehavior() de el componente al que se adjunta el comportamiento:

use app\components\MyBehavior;
// adjunta un objeto comportamiento
$component->attachBehavior('myBehavior1', new MyBehavior);
// adjunta una clase comportamiento
$component->attachBehavior('myBehavior2'MyBehavior::className());
// adjunta una arreglo de configuración
$component->attachBehavior('myBehavior3', [
    'class' => MyBehavior::className(),
    'prop1' => 'value1',
    'prop2' => 'value2',
]);

Se puede adjuntar múltiples comportamientos de una sola vez usando el método yii\base\Component::attachBehaviors():

$component->attachBehaviors([
    'myBehavior1' => new MyBehavior,  // un comportamiento con nombre
    MyBehavior::className(),          // un comportamiento anónimo
]);

También se puede adjuntar comportamientos a través de configuración:

[
    'as myBehavior2' => MyBehavior::className(),

    'as myBehavior3' => [
        'class' => MyBehavior::className(),
        'prop1' => 'value1',
        'prop2' => 'value2',
    ],
]

Utilizando Comportamientos

Para utilizar un comportamiento, primero hay que adjuntarlo a un componente de acuerdo a lo descrito anteriormente. Una vez que el comportamiento se ha adjuntado al componente, la utilización es simple.

Se puede acceder a una variable miembro pública o una propiedad definida por un captador (getter) y/o definidor (setter):

// "prop1" is una propiedad definida en la clase comportamiento
echo $component->prop1;$component->prop1 $value;

También se puede llamar a un método público de manera similar:

// foo() is a método público definido en la clase comportamiento
$component->foo();

Como se puede ver, a pesar que $component no define ni prop1 ni foo(), pueden ser usados como parte de la definición del componente debido al comportamiento (behavior) que se ha adjuntado.

Si dos comportamientos definen la misma propiedad o método y ambos están unidos a un mismo componente, el comportamiento que se une al componente primero tendrá prioridad cuando se accede a la propiedad o método.

Un comportamiento puede estar asociado con un nombre cuando está unido a un componente. Si este es el caso, se puede acceder al objeto comportamiento utilizando el nombre:

$behavior $component->getBehavior('myBehavior');

También se puede obtener todos los comportamientos adjunto a un componente:

$behaviors $component->getBehaviors();

Separando Comportamientos

Para separar un comportamiento, se debe llamar a yii\base\Component::detachBehavior() con el nombre asociado al comportamiento:

$component->detachBehavior('myBehavior1');

También se puede separar todos los comportamientos:

$component->detachBehaviors();



También te puede interesar:
Definición de un mixin
Configuraciones
Eventos
Propiedades
Componentes

martes, 20 de enero de 2015

Eventos

Los eventos permiten introducir código personalizado en el código existente en ciertos puntos de ejecución. Se puede adjuntar código personalizado para un evento de manera que cuando se activa el evento, el código sea ejecutado automáticamente. Por ejemplo, un objeto de anuncio publicitario puede desencadenar un evento messageSent cuando se envía con éxito un mensaje. Si deseamos realizar un seguimiento de los mensajes que se envían con éxito, podríamos simplemente adjuntar el código de seguimiento al evento messageSent.

Yii introduce una clase base llamada yii\base\Component para apoyar eventos. Si una clase necesita disparar eventos, debe extenderse desde yii\base\Component, o desde una clase hija.

Controladores de eventos

Un controlador de eventos es una retroalimentación PHP que se ejecuta cuando el evento que se adjunta se dispara. Puede utilizarse cualquiera de las siguientes retroalimentaciones:

  • una función global PHP especificada como una cadena (sin paréntesis), por ejemplo, 'trim';
  • un método de objeto especificado como una arreglo de un objeto y un nombre de método como una cadena (sin paréntesis), por ejemplo, [$object, 'methodName'];
  • un método de clase estática especificado como un arreglo de un nombre de clase y un nombre de método como una cadena (sin paréntesis), por ejemplo, [$class, 'methodName'] ;
  • una función anónima, por ejemplo, function ($event) { ... } 

La firma de un controlador de eventos es:

function ($event) {
    // $event es un objeto de yii\base\Event o una clase hija
}

A través del parámetro $event, un controlador de eventos puede obtener la siguiente información sobre el evento que ocurrió:

nombre del evento
evento remitente : el objeto cuyo método trigger() fue llamado
datos personalizados: los datos que se proporcionan al adjuntar el controlador de eventos (que se explica a continuación)

Adjuntando controladores de eventos

Se puede adjuntar un controlador de eventos llamando al método yii\base\Component::on(). Por ejemplo:

$foo = new Foo;
// éste controlador es una función global
$foo->on(Foo::EVENT_HELLO'function_name');
// éste controlador es un método de objeto
$foo->on(Foo::EVENT_HELLO, [$object'methodName']);
// éste controlador es un método estático de una clase
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar''methodName']);
// éste controlador es  una función anónima
$foo->on(Foo::EVENT_HELLO, function ($event) {
    // lógica del controlador de eventos
});

También se pueden adjuntar eventos a través de configuraciones. En una entrada futura lo revisaremos.

Al adjuntar un controlador de eventos, se puede proporcionar datos adicionales como tercer parámetro a yii\base\Component::on(). Los datos estarán disponibles al controlador cuando se active el evento y se llamará al controlador. Por ejemplo:

// El siguiente código despliega "abc" cuando el evento es disparado
// porque $event->data contiene los datos del tercer parámetro de "on"$foo->on(Foo::EVENT_HELLO'function_name''abc');

function function_name($event) {
    echo $event->data;
}

Orden de los controladores de eventos

Se puede adjuntar uno o más controladores a un único evento. Cuando se activa un evento, los controladores adjuntos serán llamados en el orden en que se adjuntaron al evento. Si un controlador tiene que parar la invocación de los controladores que le siguen, puede establecer la propiedad yii\base\Event::$handled del parámetro $event a true:

$foo->on(Foo::EVENT_HELLO, function ($event) {
    $event->handled true;
});

Por defecto, un controlador recién adjunto se anexa a la cola de controladores existente para el evento. Como resultado, el controlador será llamado en el último lugar cuando se activa el evento. Para insertar el nuevo controlador en el comienzo de la cola de controladores para que sea llamado primero, puede llamarse a yii\base\Component::on(), pasando false en el cuarto parámetro de $append.

$foo->on(Foo::EVENT_HELLO, function ($event) {
    // ...}, $datafalse);

Activación de eventos

Los eventos se activan o se disparan mediante una llamada al método yii\base\Component::trigger(). El método requiere un nombre de evento, y opcionalmente un objeto evento que describa los parámetros que se pasa a los controladores de eventos. Por ejemplo:

namespace app\components;

use yii\base\Component;
use yii\base\Event;

class Foo extends Component{
    const EVENT_HELLO 'hola';

    public function bar()
    {
        $this->trigger(self::EVENT_HELLO);
    }
}

Con el código anterior, todas las llamadas a bar() activarán el evento hola.

Consejo: Se recomienda el uso de constantes de clase para representar nombres de eventos. En el ejemplo anterior, la constante de EVENT_HELLO representa el evento hola. Este enfoque tiene tres ventajas. En primer lugar, evita errores tipográficos. En segundo lugar, se puede hacer eventos reconocibles para soporte a IDE auto-completar. En tercer lugar, podemos decir qué eventos se apoyan en una clase con tan sólo observar sus constantes declaraciones.

A veces, cuando se dispara un evento es posible que se desee transmitir información adicional a los controladores de eventos. Por ejemplo, un anuncio publicitario puede querer pasar la información del mensaje al controlador del evento messageSent para que entre controladores puedan conocer los detalles de los mensajes enviados. Para ello, se puede proporcionar un objeto evento como el segundo parámetro del método yii\base\Component::trigger(). El objeto evento debe ser una instancia de la clase yii\base\Event  o una clase hija. Por ejemplo:

namespace app\components;

use yii\base\Component;
use yii\base\Event;

class MessageEvent extends Event{
    public $message;
}

class Mailer extends Component{
    const EVENT_MESSAGE_SENT 'messageSent';

    public function send($message)
    {
        // ...enviando $message...

        $event = new MessageEvent;
        $event->message $message;
        $this->trigger(self::EVENT_MESSAGE_SENT$event);
    }
}

Cuando el método yii\base\Component::trigger() es llamado, se llamará a todos los controladores del evento mencionado.

Exclusión de controladores de eventos

Para separar un controlador de un evento, utilizamos el método yii\base\Component::off(). Por ejemplo:

// el controlador en una función global
$foo->off(Foo::EVENT_HELLO'function_name');
// el controlador en un método de objeto
$foo->off(Foo::EVENT_HELLO, [$object'methodName']);
// el controlador es un método estático de una clase
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar''methodName']);
// el controlador es una función anónima
$foo->off(Foo::EVENT_HELLO$anonymousFunction);

Nótese que en general, no se debe tratar de separar una función anónima hasta almacenarla en algún lugar cuando es asociada al evento. En el ejemplo anterior, se asume que la función anónima se almacena como una variable $anonymousFunction.

Para separar todos los controladores de un evento, simplemente hay que llamar a yii\base\Component::off()  sin el segundo parámetro:

$foo->off(Foo::EVENT_HELLO);

Controladores de eventos a Nivel de Clase (Class-Level)

Hasta el momento, se ha descrito cómo adjuntar los controladores de eventos a nivel de una instancia. Sin embargo, algunas veces necesitamos que un evento se dispare por cada instancia de una clase, y no solo por una instancia específica. En lugar de adjunta un controlador de eventos por cada instancia, podemos adjuntar el controlador a nivel de clase llamando al método estático yii\base\Event::on().

Por ejemplo, un objeto Registro Activo, disparará un evento EVENT_AFTER_INSERT cada vez que se inserte un nuevo registro en la base de datos. Si lo que queremos es realizar un seguimiento a cada una de las inserciones realizadas por el Registro Activo, se puede utilizar el siguiente código:

use Yii;
use yii\base\Event;
use yii\db\ActiveRecord;
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
    Yii::trace(get_class($event->sender) . ' es insertado');
});

El controlador de eventos será invocado cuando una instancia del Registro Activo, o una de sus clases hijas, disparen el evento EVENT_AFTER_INSERT. En el controlador, se puede obtener el objeto que disparó el evento a través de $event->sender.

Cuando un objeto dispara un evento, primero se llama al controlador a nivel de instancia, seguido del controlador a nivel de clase.

Se puede disparar un evento a nivel de clase llamando al método estático yii\base\Event::trigger(). Un evento a nivel de clase no está asociado con un objeto en particular. Como resultado, causará sólo la invocación de los controladores de eventos de nivel de clase. Por ejemplo:

use yii\base\Event;
Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {
    echo $event->sender;  // despliega "app\models\Foo"
});
Event::trigger(Foo::className(), Foo::EVENT_HELLO);

Nótese que en este caso, $event->sender se refiere al nombre de la clase que activa el evento en lugar de una instancia de objeto.

Nota: Debido a que un controlador a nivel de clase responderá a un evento disparado por cualquier instancia de esa clase o, a su vez de una clase hija, se debe utilizar con cuidado, especialmente si la clase es una clase base de bajo nivel, como yii\base\Object.

Para separar un controlador a nivel de clase se debe llamar a yii\base\Event::off(). Por ejemplo:

// separando $handler
Event::off(Foo::className(), Foo::EVENT_HELLO$handler);
// separando todos los controladores de Foo::EVENT_HELLO
Event::off(Foo::className(), Foo::EVENT_HELLO);

Eventos globales

Yii soporta la llamada a un evento global, que en realidad es un truco basado en el mecanismo de eventos descrito anteriormente. El evento global requiere un único acceso global, tal como la instancia de la aplicación en sí misma.

Para crear un evento global, un evento debe enviar la llamada en evento trigger() único para disparar el evento, en lugar de llamar a su propio método trigger(). De manera similar, los controladores deben añadirse al evento único, por ejemplo:

use Yii;
use yii\base\Event;
use app\components\Foo;
Yii::$app->on('bar', function ($event) {
    echo get_class($event->sender);  // despliega "app\components\Foo"
});
Yii::$app->trigger('bar', new Event(['sender' => new Foo]));

Una ventaja de utilizar los eventos mundiales es que no se necesita de un objeto cuando se adjunta un controlador al evento que se desencadena por por parte del objeto. En su lugar, el controlador adjunto y el evento de activación son completados a través del acceso global (por ejemplo, la instancia de la aplicación).

Sin embargo, debido a que el espacio de nombres (namespace) de los eventos globales es compartido por todas las partes, se deberá nombrar a los eventos globales sabiamente, como la introducción de algún tipo de espacio de nombres adecuado (por ejemplo, "frontend.mail.sent", "backend.mail.sent").


También te puede interesar:
Configuraciones
Comportamientos (Behaviors)
Propiedades
Componentes



lunes, 19 de enero de 2015

Propiedades

En PHP, las variables miembro de clase también se denominan propiedades. Estas variables son parte de la definición de la clase, y se utilizan para representar el estado de una instancia de clase (es decir, para diferenciar una instancia de la clase de otra). En la práctica, es posible que a menudo se requiera manejar la lectura o escritura de las propiedades en forma especial. Por ejemplo, es posible que necesitemos recortar una cadena cuando está siendo asignado a una propiedad label (etiqueta). Podríamos utilizar el siguiente código para dicho objetivo:

$object->label trim($label);

El inconveniente del código anterior es que tendríamos que llamar a trim() en todas partes del código en el que necesitemos la propiedad label. Si, en el futuro, la propiedad label tiene un nuevo requisito, como que la primera letra debe ser mayúscula, deberemos tener que volver a modificar en cada parte de nuestro código donde se utiliza la propiedad label. El repetir código nos lleva a los bugs (en unas partes actualizamos y en otras no), y es una práctica que debemos evitar en la medida de lo posible.

Para solventar este problema, Yii introduce una clase base llamada yii\base\Object que soporta la definición de propiedades basado en los métodos getter y setter. Si una clase necesita esa funcionalidad, debe extenderse desde yii\base\Object, o desde una clase hija.

Información: Casi todas las clases del núcleo de Yii se extienden de yii\base\Object o una clase hija. Esto significa que cada vez que veamos un getter o setter en una clase principal, se puede utilizar como una propiedad.

Un método de obtención es un método cuyo nombre comienza con la palabra get; un método para establecer un valor comienza con set. El nombre después de el prefijo get o set define el nombre de una propiedad. Por ejemplo, un obtenedor getLabel() y/o un establecedor setLabel() define una propiedad denominada label, como se muestra en el siguiente código:

namespace app\components;

use yii\base\Object;

class Foo extends Object{
    private $_label;

    public function getLabel()
    {
        return $this->_label;
    }

    public function setLabel($value)
    {
        $this->_label trim($value);
    }
}

Para estar claros, en este caso, los métodos get y set crean la propiedad label, que en este caso se refiere internamente a un atributo privado llamado _label.

Propiedades definidas por los captadores (get) y definidores (set) se pueden utilizar como variables miembro de clase. La principal diferencia es que cuando esas propiedades se está leyendo, se llama al método getter correspondiente; cuando la propiedad se le asigna un valor, se llama al método setter correspondiente. Por ejemplo:

// equivalente a $label = $object->getLabel();$label $object->label;
// equivalente a $object->setLabel('abc');$object->label 'abc';

Una propiedad definida por un captador (get) sin un definidor (set) es de sólo lectura. Tratar de asignar un valor a una propiedad causará un InvalidCallException. De manera similar, una propiedad definida por un definidor (set) sin un captador (get) es solamente de escritura, y tratar de leer este tipo de propiedad también provocará una excepción. No es común tener propiedades de sólo escritura.

Hay varias reglas especiales y limitaciones para las propiedades definidas a través de captadores (get) y definidores (set):

  • Los nombres de esas propiedades no son sensibles a mayúsculas. Por ejemplo, $object->label y $object->Label son los mismos. Esto se debe a que los nombres de métodos en PHP no son sensibles a mayúsculas.
  • Si el nombre de una propiedad de este tipo es el mismo que una variable miembro de la clase, este último tendrá prioridad. Por ejemplo, si lo clase Foo tiene una variable miembro label, entonces, la asignación de $object->label = 'abc' afectará a la variable miembro 'label'; esa línea no llamaría al método setLabel().
  • Estas propiedades no son compatibles con la visibilidad. No hace ninguna diferencia con el método captador (get) o definidor (set) especificar si la propiedad es pública, protegida o privada.
  • Las propiedades sólo pueden ser definidas por los captadores y / o emisores no estáticos. Los métodos estáticos no serán tratados de la misma manera.

Volviendo de nuevo al problema descrito al inicio de esta entrada, en lugar de llamar trim() en todas partes de un label donde se le asigna valor, ahora sólo necesitamos invocar a trim() en el definidor setLabel(). Y si un nuevo requisito hace necesario que la etiqueta inicie con mayúscula, el método setLabel() lo podemos modificar rápidamente para adecuarlo a los nuevos requisitos sin tener que tocar el resto de nuestro código. El cambio afectará universalmente a cada asignación de label.


También te puede interesar:
Configuraciones
Comportamientos (Behaviors)
Eventos
Componentes

Componentes

Los componentes son los principales bloques de construcción de aplicaciones Yii. Los componentes son instancias de yii\base\Component o una clase extendida. Las tres características principales que los componentes proporcionan a otras clases son:

  • Propiedades
  • Eventos
  • Comportamientos (Behaviors)

Por separado y en combinación, estas características hacen clases Yii mucho más personalizables y fáciles de utilizar. Por ejemplo, si requerimos presentar en nuestra vista un selector de fechas, podemos incluir el widget de selección de fecha.

use yii\jui\DatePicker;

echo DatePicker::widget([
    'language' => 'es',
    'name'  => 'fecha',
    'clientOptions' => [
        'dateFormat' => 'yy-mm-dd',
    ],
]);

Las propiedades de los widgets se pueden modificar fácilmente la clase se extiende de yii\base\Component.

Los componentes son muy potentes, pero son un poco más pesados que los objetos normales, debido al hecho de que se necesita memoria adicional y tiempo de CPU para apoyar el evento y el comportamiento de la funcionalidad en particular. Si nuestros componentes no necesitan estas dos características (eventos y comportamientos), se puede considerar la ampliación de nuestro componente a partir de yii\base\Object en lugar de yii\base\Component. Al hacer esto, nuestros componentes serán tan eficientes como objetos normales de PHP, pero con mayor soporte para propiedades.

Cuando hagamos una clase que se extiende de yii\base\Component o yii\base\Object, se recomiendan las siguientes convenciones:

Si se sobrescribe el constructor, especificar el parámetro $config como el último parámetro del constructor, y luego pasar este parámetro al constructor padre.
Siempre llamar al constructor padre al final del constructor sobrescrito.
Si se sobrescribe el método yii\base\Object::init() hay que asegurarse de llamar a la implementación padre de init al principio de nuestro método init.

namespace yii\components\MyClass;

use yii\base\Object;

class MyClass extends Object{
    public $prop1;
    public $prop2;

    public function __construct($param1$param2$config = [])
    {
        // ... inicialización antes de que la configuración esté aplicada

        parent::__construct($config);
    }

    public function init()
    {
        parent::init();

        // ... inicialización después de que la configuración es aplicada
    }
}

Siguiendo estas directrices, haremos que nuestros componentes sean configurables cuando sean creados. Por ejemplo:

$component = new MyClass(12, ['prop1' => 3'prop2' => 4]);
// alternativamente$component = \Yii::createObject([
    'class' => MyClass::className(),
    'prop1' => 3,
    'prop2' => 4,
], [12]);

Información: Si bien el enfoque de llamar a Yii::createObject() parece más complicado, es más potente porque se implementa en la parte superior de un contenedor de inyección de dependencias.

La clase yii\base\Object forza al siguiente ciclo de vida:

  1. Pre-inicialización dentro del constructor. Se puede establecer valores de propiedades por defecto aquí.
  2. Configuración de objetos a través de $config . La configuración puede sobrescribir los valores predeterminados establecidos en el constructor.
  3. Post-inicialización dentro de init(). Se puede reemplazar este método para realizar comprobaciones de validez y la normalización de las propiedades.
  4. Llamadas de los métodos del objeto.


Los tres primeros pasos suceden en el constructor del objeto. Esto significa que una vez que se tiene una instancia de clase (es decir, un objeto), el objeto ha sido inicializado a un estado adecuado y fiable.


También te puede interesar:
Configuraciones
Comportamientos (Behaviors)
Eventos
Propiedades

jueves, 1 de enero de 2015

Gii, generación automática de código

Si hemos trabajamos anteriormente con Yii (la versión 1) recordaremos que hay una herramienta muy útil llamada Gii, la cual nos permitía generar rápidamente el código de nuestros modelos, vistas y controladores.

En ésta versión de Yii 2, también tenemos el asistente Gii. A continuación veremos cómo:

  • Habilitar Gii
  • Generar la clase Active Record 
  • Generar el código para implementar las operaciones CRUD
  • Personalizar el código generado por Gii


Configurar Gii

Gii es un módulo de Yii. Para habilitarlo debemos configurar la propiedad modules de la aplicación en el archivo \config\web.php

Si abrimos este archivo veremos que en un inicio tenemos un arreglo $config, y luego la configuración del módulo gii de manera similar a:

<?php

$params = require(__DIR__ . '/params.php');

$config = [...];

if (YII_ENV_DEV) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = 'yii\debug\Module';

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = 'yii\gii\Module';
}

return $config;


Las últimas líneas de código nos dicen que si nos encontramos en ambiente de desarrollo, se incluya el módulo debug y el módulo gii, el cual es una clase de yii\gii\Module

Si revisamos el script de entrada, \web\index.php tenemos la siguiente línea, la cual habilita el modo de desarrollo:

defined('YII_ENV') or define('YII_ENV''dev');

Para acceder a Gii en nuestro navegador web escribimos:

http://hostname/index.php?r=gii

Veremos una página como la de la imagen siguiente:


En la entrada anterior, Trabajando con bases de datos, utilizamos la tabla pais. Utilizaremos la misma tabla para generar nuestro código con Yii.



Generando el modelo

Una vez que accedamos a Gii, presionamos el botón Start de la opción Model Generator. Con ello podemos generar nuestra clase Active Record. En Table Name escribimos el nombre de nuestra tabla; en este caso, pais. En Model Class aparecerá automáticamente Pais, aunque podemos escribir nosotros mismos con el nombre que deseemos.



Por lo general, las demás opciones las podemos dejar tal cual están. Si nuestras tablas tienen un prefijo (por ejemplo, tbl_ ) podemos marcar la opción Use Table Prefix, siempre y cuando definamos en nuestra conexión a base de datos el prefijo tablePrefix. También podemos marcar la opción Enable I18N si nuestra aplicación va a ser multi-idioma.

Al presionar el botón Preview podremos ver el archivo que se va a generar, y si ya existe, la opción para sobrescribir el archivo antiguo. En este versión tenemos un botón diff junto al nombre del archivo, el cual nos permite ver las diferencias en el código entre el archivo ya existente y el que se generará.

Al presionar el botón Generate se crearán los archivos que hemos marcado en la columna final.



Generando el código CRUD (o ABM)

El código CRUD se refiere al código de las operaciones Create (Crear), Read (Leer), Update (Actualizar) y Delete (Borrar). En español, también se lo conoce como ABM (Alta, Baja, Modificación).

Desde nuestra página de Gii, presionamos el botón Start de la opción CRUD Generator. Si ya generamos nuestro modelo, en el menú de la izquierda también tenemos la opción  CRUD Generator.

Siguiendo con nuestro ejemplo, los diferentes campos los llenamos de la siguiente manera:

  • Model Class: El nombre de nuestro modelo, pero hay que escribirlo con la ruta completa. Es decir: app\models\Pais
  • Search Model Class: Es el nombre de nuestro modelo de búsqueda. También debe escribirse el nombre completo con la ruta, es decir: app\models\PaisSearch
  • Controller Class: Es el nombre de nuestro controlador. También debe ser escrito el nombre completo con la ruta. Además, debe escribirse con la primera letra de cada palabra en Mayúscula y las demás en minúsculas, es decir: app\controllers\PaisController




Los demás campos los podemos dejar tal cual. La opción Widget Used in Index Page nos permite indicar el widget que se utilizará en la página index para desplegar los datos. En la versión 1 siempre era CListView, pero en ésta versión podemos escoger entre ListView o GridView. También podemos marcar Enable I18N si nuestra aplicación va a ser multi-idioma.

Al presionar el botón Preview, podremos ver el archivo que se va a generar, y si ya existe, la opción para sobrescribir el archivo antiguo. En este versión tenemos un botón diff junto al nombre del archivo, el cual nos permite ver las diferencias en el código entre el archivo ya existente y el que se generará.

Al presionar el botón Generate se crearán los archivos que hemos marcado en la columna final.



Probando

Para probar el resultado de nuestro trabajo utilizando Gii, en nuestro navegador web debemos escribir algo similar a:

http://hostname/index.php?r=pais/index

El resultado será similar a las imágenes siguientes:






Si queremos revisar el código generado por Gii, podemos revisar los archivos:
  • Controlador: controllers/PaisController.php
  • Modelo: models/Pais.php y models/PaisSearch.php
  • Vistas: views/pais/*.php
Al igual que en la versión 1, las vistas se generan en inglés. Esta versión también se puede personalizar. En las siguientes entradas veremos cómo personalizar Gii a nuestro gusto.


También te puede interesar: