Behaviors (Comportamentos) ¶
Behaviors são instâncias de yii\base\Behavior, ou de uma classe-filha, também conhecido como mixins, permite melhorar a funcionalidade de uma classe componente existente sem a necessidade de mudar a herança dela. Anexar um behavior a um componente "introduz" os métodos e propriedades do behavior dentro do componente, tornando esses métodos e propriedades acessíveis como se estes fossem definidos na própria classe do componente. Além disso, um behavior pode responder a um evento disparado pelo componente, o que permite a customização do código normal.
Definindo Behaviors ¶
Para definir um behavior, crie uma classe estendendo yii\base\Behavior, ou de uma classe-filha. Por exemplo:
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()
{
// ...
}
}
O código acima define a classe behavior app\components\MyBehavior
, com duas propriedades --prop1
e prop2
-- e um método foo()
. Note que a propriedade prop2
É definida através do método getter getProp2()
e setter setProp2()
. Isto é possível porque yii\base\Behavior estende de yii\base\BaseObject e portanto suporta definição de propriedades através de propriedades getters e setters.
Como essa classe é um behavior, quando ela está anexada a um componente, então este componente terá as propriedades prop1
e prop2
e o método foo()
.
Dica: Em um behavior, você pode acessar o componente que o behavior está anexado através da propriedade yii\base\Behavior::$owner.
Manuseando Eventos de Componente ¶
Se um behavior precisar responder a eventos disparados pelo componente ao qual está ligado, este deve sobrescrever o método yii\base\Behavior::events(). Por exemplo:
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)
{
// ...
}
}
O método events() deve retornar uma lista de eventos e seus manipuladores correspondentes.
O exemplo acima declara o evento EVENT_BEFORE_VALIDATE existente e define seu manipulador, beforeValidate()
. Ao especificar um manipulador de evento, você pode utilizar um dos seguintes formatos:
- uma string que refere-se ao nome do método da classe behavior, como o exemplo acima
- um array com o nome do objeto ou classe, e um nome de método como string (sem parênteses), por exemplo,
[$object, 'methodName']
; - uma função anônima
A assinatura de um manipulador de eventos deve ser como o exemplo abaixo, onde $event
refere-se ao parâmetro do evento. Por favor, consulte a seção Eventos para mais detalhes sobre eventos.
function ($event) {
}
Anexando Behaviors (Comportamentos) ¶
Você pode anexar um behavior a um componente de forma estática ou dinâmica. Na prática a forma estática é a mais comum.
Para anexar um behavior de forma estática, sobrescreva o método behaviors() da classe componente para o behavior que está sendo anexado. O método behaviors() deve retornar uma lista de configurações de behaviors. Cada configuração de behavior pode ser tanto um nome da classe behavior ou um array de configuração:
namespace app\models;
use yii\db\ActiveRecord;
use app\components\MyBehavior;
class User extends ActiveRecord
{
public function behaviors()
{
return [
// behavior anônimo, somente o nome da classe
MyBehavior::className(),
// behavior nomeado, somente o nome da classe
'myBehavior2' => MyBehavior::className(),
// behavior anônimo, array de configuração
[
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
],
// behavior nomeado, array de configuração
'myBehavior4' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
]
];
}
}
Você pode associar um nome com um behavior especificando a chave do array correspondente à configuração do behavior. Neste caso o behavior é chamado behavior nomeado. No exemplo acima existem dois behaviors nomeados: myBehavior2
e myBehavior4
. Se um behavior não está associado a um nome, ele é chamado de behavior anônimo.
Para anexar um behavior dinamicamente, execute o método yii\base\Component::attachBehavior() do componente para o behavior que está sendo anexado:
use app\components\MyBehavior;
// anexando um objeto behavior
$component->attachBehavior('myBehavior1', new MyBehavior);
// anexando uma classe behavior
$component->attachBehavior('myBehavior2', MyBehavior::className());
// anexando através de um array de configuração
$component->attachBehavior('myBehavior3', [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
]);
Você pode anexar vários behaviors de uma só vez usando o método yii\base\Component::attachBehaviors():
$component->attachBehaviors([
'myBehavior1' => new MyBehavior, // um behavior nomeado
MyBehavior::className(), // um behavior anônimo
]);
Você também pode anexar behaviors através de configurações conforme o exemplo a seguir:
[
'as myBehavior2' => MyBehavior::className(),
'as myBehavior3' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
],
]
Para mais detalhes, por favor, consulte a seção Configurações.
Usando Behaviors ¶
Para usar um behavior, primeiro este deve ser anexado à um componente conforme as instruções mencionadas anteriormente. Uma vez que o behavior está anexado ao componente, seu uso é simples.
Você pode acessar uma variável pública ou uma propriedade definida por um getter e/ou um setter do behavior através do componente ao qual ele está anexado:
// "prop1" é uma propriedade definida na classe behavior
echo $component->prop1;
$component->prop1 = $value;
Você também pode executar um método público do behavior de forma parecida:
// foo() é um método público definido na classe behavior
$component->foo();
Como você pode ver, embora $component
não defina prop1
e nem foo()
, eles podem ser utilizados como se eles fizessem parte da definição do componente, isto se deve ao behavior anexado.
Se dois behaviors definem a mesma propriedade ou método e ambos são anexados ao mesmo componente, o behavior que for anexado primeiramente ao componente terá precedência quando a propriedade ou método for acessada.
Um behavior pode estar associado a um nome quando ele for anexado a um componente. Sendo esse o caso, você pode acessar o objeto behavior usando o seu nome:
$behavior = $component->getBehavior('myBehavior');
Você também pode pegar todos os behaviors anexados a um componente:
$behaviors = $component->getBehaviors();
Desvinculando Behaviors (Comportamentos) ¶
Para desvincular um behavior, execute yii\base\Component::detachBehavior() com o nome associado ao behavior:
$component->detachBehavior('myBehavior1');
Você também pode desvincular todos os behaviors:
$component->detachBehaviors();
Usando TimestampBehavior
¶
Para encerrar, vamos dar uma olhada no yii\behaviors\TimestampBehavior. Este behavior suporta atualização automática dos atributos timestamp de um Active Record toda vez que o model (modelo) for salvo (por exemplo, na inserção ou na alteração).
Primeiro, anexe este behavior na classe Active Record que você planeja usar:
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'],
],
],
];
}
}
A configuração do behavior acima especifica que o registro ao ser:
- inserido, o behavior deve atribuir o timestamp atual para os atributos
created_at
eupdated_at
- atualizado, o behavior deve atribuir o timestamp atual para o atributo
updated_at
Com esse código no lugar, se você tem um objeto User
e tenta salvá-lo, você encontrará seus created_at
e updated_at
automaticamente preenchidos com a data e hora atual:
$user = new User;
$user->email = 'test@example.com';
$user->save();
echo $user->created_at; // mostra a data atual
O TimestampBehavior também oferece um método útil touch(), que irá atribuir a data e hora atual para um atributo específico e o salva no banco de dados:
$user->touch('login_time');
Comparando Behaviors com Traits ¶
Apesar de behaviors serem semelhantes a traits em que ambos "injetam" suas propriedades e métodos para a classe principal, eles diferem em muitos aspectos. Tal como explicado abaixo, ambos têm prós e contras. Eles funcionam mais como complemento um do outro.
Razões para usar Behaviors ¶
Classes Behavior, como classes normais, suportam herança. Traits, por outro lado, pode ser só suporta a programação “copia e cola”. Eles não suportam herança.
Behaviors podem ser anexados e desvinculados a um componente dinamicamente sem necessidade de modificação da classe componente. Para usar um trait, você deve modificar o código da classe.
Behaviors são configuráveis enquanto traits não são.
Behaviors podem customizar a execução do código do componente respondendo aos seus eventos. Quando houver nomes conflitantes entre diferentes behaviors anexados ao mesmo componente, o conflito é automaticamente resolvido priorizando o behavior anexado primeiramente ao componente. Nomes conflitantes causados por diferentes traits requer resolução manual renomeando as propriedades ou métodos afetados.
Razões para usar Traits ¶
Traits são muito mais eficientes do que behaviors, estes são objetos e requerem mais tempo e memória.
IDEs são mais amigáveis com traits por serem nativos do PHP.