concept-behaviors.md 12.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
Comportamientos
===============

Comportamientos son instancias de [[yii\base\Behavior]] o sus clases "hija". Comportamientos, también conocido como
[mixins](http://en.wikipedia.org/wiki/Mixin), te permiten mejorar la funcionalidad de un [[yii\base\Component|componente]]
existente sin necesidad de modificar su herencia de clases.
Cuando un comportamiento se une a un componente, "inyectará" sus métodos y propiedades dentro del componente, y podrás
acceder a esos métodos y propiedades como si hubieran estado definidos por la clase de componente. Además, un
comportamiento puede responder a [eventos](concept-events.md) disparados por el componente de modo que se pueda personalizar
o adaptar a la ejecución normal del código del componente.


13 14
Definiendo comportamientos <a name="defining-behaviors"></a>
--------------------------
15

16
Para definir un comportamiento, se debe crear una clase que exiende [[yii\base\Behavior]], o se extiende una clase hija. Por ejemplo:
17

18 19
```php
namespace app\components;
20

21
use yii\base\Behavior;
22

23 24 25
class MyBehavior extends Behavior
{
    public $prop1;
26

27
    private $_prop2;
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
    public function getProp2()
    {
        return $this->_prop2;
    }

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

    public function foo()
    {
        // ...
    }
}
44
```
45 46 47
El código anterior define la clase de comportamiento (behavior) app\components\MyBehavior`, con dos propiedades --
`prop1` y `prop2`--y un método `foo()`. Tenga en cuenta que la propiedad `prop2`
se define a través de la getter `getProp2()` y el setter `setProp2()`. Este caso es porque [[yii\base\Behavior]] extiende [[yii\base\Object]] y por lo tanto se apoya en la definición de [propiedades](concept-properties.md) via getters y setters.
48

49
Debido a que esta clase es un comportamiento, cuando está unido a un componente, el componente también tienen la propiedad `prop1` y `prop2` y el método `foo()`.
50

51
> Consejo: Dentro de un comportamiento, puede acceder al componente que el comportamiento está unido a través de la propiedad [[yii\base\Behavior::owner]].
52

53 54 55 56 57

Gestión de eventos de componentes
---------------------------------

Si un comportamiento necesita responder a los acontecimientos desencadenados por el componente al que está unido, se debe reemplazar el método [[yii\base\Behavior::events()]]. Por ejemplo:
58 59

```php
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
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)
    {
        // ...
    }
}
81 82
```

83 84 85 86 87 88 89 90
El método [[yii\base\Behavior::events()|events()]] debe devolver una lista de eventos y sus correspondientes controladores.
El ejemplo anterior declara que el evento [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] existe y esta  exists y define su controlador, `beforeValidate()`. Al especificar un controlador de eventos, puede utilizar uno de los siguientes formatos:

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

La firma de un controlador de eventos debe ser la siguiente, donde `$ event` refiere al parámetro de evento. Por favor, consulte la sección [Eventos](concept-events.md) para más detalles sobre los eventos.
91 92

```php
93 94
function ($event) {
}
95 96 97 98 99 100 101 102 103
```


Vinculando Comportamientos <a name="attaching-behaviors"></a>
--------------------------

Puedes vincular un comportamiento a un [[yii\base\Component|componente]] ya sea estática o dinámicamente. La primera forma
es la más comúnmente utilizada en la práctica.

104 105
Para unir un comportamiento estáticamente, reemplaza el método [[yii\base\Component::behaviors()|behaviors()]] dde la clase de componente a la que se une el comportamiento. El método [[yii\base\Component::behaviors()|behaviors()]]  debe devolver una lista de comportamiento [configuraciones](concept-configurations.md).
Cada configuración de comportamiento puede ser un nombre de clase de comportamiento o un arreglo de configuración:
106 107 108 109 110 111 112 113 114 115 116 117

```php
namespace app\models;

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

class User extends ActiveRecord
{
    public function behaviors()
    {
        return [
118
            // anonymous behavior, behavior class name only
119 120
            MyBehavior::className(),

121
            // named behavior, behavior class name only
122 123
            'myBehavior2' => MyBehavior::className(),

124
            // anonymous behavior, configuration array
125 126 127 128 129 130
            [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ],

131
            // named behavior, configuration array
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
            'myBehavior4' => [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ]
        ];
    }
}
```

Puedes asociciar un nombre a un comportamiento especificándolo en la clave de la matriz correspondiente a la configuración
del comportamiento. En este caso, el comportamiento puede ser llamado un *comportamiento nombrado* (named behavior). En
el ejemplo anterior, hay dos tipos de comportamientos nombrados: `myBehavior2` y `myBehavior4`. Si un comportamiento
no está asociado con un nombre, se le llama *comportamiento anónimo* (anonymous behavior).

Para vincular un comportamiento dinámicamente, llama al método [[yii\base\Component::attachBehavior()]] desde el componente al
148
que se le va a unir el comportamiento:
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

```php
use app\components\MyBehavior;

// vincular un objeto comportamiento "behavior"
$component->attachBehavior('myBehavior1', new MyBehavior);

// vincular una clase comportamiento
$component->attachBehavior('myBehavior2', MyBehavior::className());

// asociar una matriz de configuración
$component->attachBehavior('myBehavior3', [
    'class' => MyBehavior::className(),
    'prop1' => 'value1',
    'prop2' => 'value2',
]);
```
166
Puede vincular múltiples comportamientos a la vez mediante el uso del método [[yii\base\Component::attachBehaviors()]]. Por ejemplo,
167 168 169

```php
$component->attachBehaviors([
170 171
    'myBehavior1' => new MyBehavior,  // un comportamiento nombrado
    MyBehavior::className(),          // un comportamiento anónimo
172 173 174
]);
```

175
También puedes asociar comportamientos a traves de [configuraciones](concept-configurations.md) como el siguiente:
176 177 178 179 180 181 182 183 184 185 186 187 188

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

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

189
Para más detalles, por favor visita la sección [Configuraciones](concept-configurations.md#configuration-format).
190 191


192 193
Usando comportamientos <a name="using-behaviors"></a>
----------------------
194

195
Para poder utilizar un comportamiento, primero tienes que unirlo a un [[yii\base\Component|componente]] según las instrucciones anteriores. Una vez que un comportamiento ha sido vinculado a un componente, su uso es sencillo.
196

197 198
Puedes usar a una variable *pública* o a una [propiedad](concept-properties.md) definida por un `getter` y/o un `setter`
del comportamiento a través del componente con el que se ha vinculado:
199 200

```php
201 202 203
// "prop1" es una propiedad definida en la clase comportamiento
echo $component->prop1;
$component->prop1 = $value;
204 205
```

206
También puedes llamar métodos *públicos* del comportamiento de una forma similar:
207 208

```php
209 210 211
// foo() es un método público definido dentro de la clase comportamiento
$component->foo();
```
212

213 214
Como puedes ver, aunque `$component` no tiene definida `prop1` y `bar()`, que se pueden utilizar como si son parte
de la definición de componentes debido al comportamiento vinculado.
215

216 217
Si dos comportamientos definen la misma propiedad o método y ambos están vinculados con el mismo componente, el
comportamiento que ha sido vinculado *primero* tendrá preferencia cuando se esté accediendo a la propiedad o método.
218

219 220
Un comportamiento puede estar asociado con un nombre cuando se une a un componente. Si este es el caso, es posible
acceder al objeto de comportamiento mediante el nombre, como se muestra a continuación,
221

222 223
```php
$behavior = $component->getBehavior('myBehavior');
224 225
```

226
También puedes acceder a todos los comportamientos vinculados al componente:
227 228

```php
229 230
$behaviors = $component->getBehaviors();
```
231 232


233 234
Desasociar Comportamientos <a name="detaching-behaviors"></a>
--------------------------
235

236 237
Para desasociar un comportamiento, puedes llamar el método [[yii\base\Component::detachBehavior()]] con el nombre con el
que se le asoció:
238

239 240
```php
$component->detachBehavior('myBehavior1');
241 242
```

243
También puedes desvincular *todos* los comportamientos:
244 245

```php
246
$component->detachBehaviors();
247 248 249 250 251 252
```


Utilizando `TimestampBehavior` <a name="using-timestamp-behavior"></a>
-----------------------------

253 254 255
Para terminar, vamos a echar un vistazo a [[yii\behaviors\TimestampBehavior]]. Este comportamiento soporta de forma
automática la actualización de atributos timestamp de un modelo [[yii\db\ActiveRecord|Registro Activo]]
(Active Record) en cualquier momento donde se guarda el modelo (ej., en la inserción o actualización).
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

Primero, vincula este comportamiento a la clase [[yii\db\ActiveRecord|Active Record]] que desees utilizar.

```php
namespace app\models\User;

use yii\db\ActiveRecord;
use yii\behaviors\TimestampBehavior;

class User extends ActiveRecord
{
    // ...

    public function behaviors()
    {
        return [
            [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
                ],
            ],
        ];
    }
}
```

La configuración del comportamiento anterior especifica que

* cuando el registro está siendo insertado, el comportamiento debe asignar el sello de tiempo actual a los atributos
  `created_at` y `updated_at`;
* cuando el registro está siendo actualizado, el comportamiento debe asignar el sello de tiempo actual al atributo
289
  `updated_at`.
290 291 292 293 294 295 296 297 298 299 300 301

Ahora si tienes un objeto `User` e intentas guardarlo, descubrirás que sus campos `created_at` y `updated_at` están
automáticamente actualizados con el sello de tiempo actual:

```php
$user = new User;
$user->email = 'test@example.com';
$user->save();
echo $user->created_at;  // muestra el sello tiempo actual (timestamp)
```

El comportamiento [[yii\behaviors\TimestampBehavior|TimestampBehavior]] también ofrece un método muy útil llamado
302
[[yii\behaviors\TimestampBehavior::touch()|touch()]], que asigna el sello de tiempo actual a un atributo especificado y lo guarda automáticamente en la base de datos:
303 304 305 306 307 308 309 310 311 312 313

```php
$user->touch('login_time');
```


Comparación con Traits <a name="comparison-with-traits"></a>
----------------------

Mientras que los comportamientos son similares a [traits](http://www.php.net/traits) en cuanto que ambos "inyectan" sus
métodos  y propiedades a la clase primaria, son diferentes en muchos aspectos. Tal y como se describe abajo, los dos
314
tienen sus ventajas y desventajas. Son más como complementos el uno al otro en lugar de alternativas.
315 316


317
### Razones para utilizar comportamientos <a name="pros-for-behaviors"></a>
318

319 320
Las clases de comportamientos, como todas las clases, soportan herencias. Traits, por otro lado, pueden ser
considerados como un copia-y-pega de PHP. Ellos no soportan la herencia de clases.
321 322 323 324 325 326 327 328 329 330

Los comportamientos pueden ser asociados y desasociados a un componente dinámicamente sin necesidad de que la clase del
componente sea modificada. Para usar un trait, debes modificar la clase que la usa.

Los comportamientos son configurables mientras que los traits no.

Los comportamientos pueden personalizar la ejecución de un componente al responder a sus eventos.

Cuando hay un conflicto de nombre entre los diferentes comportamientos vinculados a un mismo componente, el conflicto es
automáticamente resuelto respetando al que ha sido asociado primero.
331
El conflicto de nombres en traits requiere que manualmente sean resueltos cambiando el nombre de las propiedades o métodos afectados.
332 333


334
### Razones para utilizar los Traits <a name="pros-for-traits"></a>
335 336 337 338

Los Traits son mucho más eficientes que los comportamientos debido a que los últimos son objetos que consumen tiempo y
memoria.

339
Los IDEs (Programas de desarrollo) son más amigables con traits ya que son una construcción del lenguaje nativo.
340