Extensões são pacotes de software projetados especificamente para serem distribuídos e utilizados em aplicações Yii, fornecendo características prontas para utilização. Por exemplo, a extensão yiisoft/yii2-debug adiciona uma barra de ferramentas de depuração na parte inferior de cada página de sua aplicação, algo muito útil para facilitar o entendimento de como as páginas são geradas.

Você pode utilizar extensões para acelerar seu processo de desenvolvimento e também pode criar seus próprios pacotes de códigos e compartilhá-los como extensões para outros desenvolvedores.

Há duas formas de instalar extensões: a primeira é através do composer, que é a ferramenta de gerenciamento de dependências do PHP; ou manualmente. Veremos as duas formas a seguir.

Instalando o Composer na sua Aplicação

O primeiro passo para a instalação do composer é a instalação do PHP 5.3.2 ou qualquer versão superior para poder funcionar. Veremos como instalar o mesmo no ambiente Windows.

Após fazer o download e executar o arquivo Composer-Setup.exe, que está disponível no link https://getcomposer.org/Composer-Setup.exe, o mesmo instalará a versão mais recente. Dessa forma, você poderá executar o composer a partir do prompt de comando do seu computador.

Pelo próprio prompt entre no diretório raiz de sua aplicação e crie um arquivo chamado composer.json. Nesse arquivo deverá ser colocado as dependências de seu aplicativo.

O arquivo composer.json deverá seguir o esquema mostrado na Listagem 1.

Listagem 1. Esquema de um arquivo composer.json.

  {    
              "name": "Nome do projeto",    
              "description": "Breve descrição do que a aplicação se propoe a fazer",       
              "authors": 
              [        
   
              {            
                          "name": "Seu nome",            
                          "email": "email@seu-dominio.com"        
              }    
              ], 
              "require": {        
                          "php": ">=5.2.8"    
              }
  }

No código, o campo “require” deverá ter explicitado quais são as dependências que sua aplicação deve conter. No nosso exemplo especificamos que para a nossa aplicação ser instalada é necessário o PHP no mínimo na versão 5.2.8. Caso o mesmo não esteja disponível será exibida uma mensagem de erro informando que existe dependências que não estão instaladas.

Utilizando Extensões

Para utilizarmos uma extensão é necessário inicialmente fazermos a sua instalação. A maioria das extensões podem ser instaladas seguindo dois passos:

  1. Modifique o arquivo “composer.json” de sua aplicação e especifique qual ou quais extensões você deseja utilizar;
  2. Entre no diretório raiz da sua aplicação e execute o comando “php composer.phar” para instalar a extensão especificada.

Por exemplo, para instalar a extensão yiisoft/yii2-imagine modifique seu arquivo “composer.json” adicionando as linhas mostradas na Listagem 2.

Listagem 2. Código a ser adicionado no composer.json.

  { 
              // …
              "require": { 
                          // ... outras dependências 
                          "yiisoft/yii2-imagine": "*" 
              } 
  }

Depois da instalação você deverá ver o diretório yiisoft/yii2-imagine dentro do diretório raiz da sua aplicação. Você também deve ver um outro diretório, o imagine/imagine, que contém a instalação das dependências do pacote.

Agora você poderá usar as extensões instaladas como se fossem parte de sua aplicação.

Instalando Extensões Manualmente

Caso você não tenha o composer instalado ou não consiga utilizá-lo, você poderá instalar extensões de forma manual. Para fazer isso, você deve:

  • Baixar o arquivo com a extensão no repositório do Yii (vide seção Links) e descompactar no diretório extensions, que fica na pasta base da sua aplicação;
  • Baixar e instalar todas as dependências da extensão de acordo com as instruções da documentação. Por exemplo, como mostrado na Listagem 1, se você tentar instalar uma extensão que necessite de uma versão superior ao PHP 5.2.8 e esta não estiver instalada, você deverá baixá-la e consultar sua documentação para ver a sua melhor forma de instalar.

Agora veremos as principais formas de utilização de extensão.

Extensões Zii

Zii é uma biblioteca de extensões criadas pelos desenvolvedores do Yii e está disponível em todas as versões do framework a partir da 1.1, podendo também ser encontrada no Google Code (vide seção Links).

Para utilizarmos extensões disponíveis na Zii devemos usar um path alias fazendo correspondência as classes da extensão, da seguinte forma: zii.caminho.para.NomeDaClasse. Veja na Listagem 3 como utilizar a extensão CGridView em uma view.

Listagem 3. Código necessário para utilização da extensão CGridView.


  $this->widget('zii.widgets.grid.CGridView', array(
      'dataProvider'=>$dataProvider,
  ));

Utilizando a extensão CGridView

A extensão CGridView está disponível no Zii a partir do pacote zii.widgets.grid. Essa extensão possibilita a impressão de uma série de dados em forma de tabela.

Cada linha da tabela representa um item de dado único, e uma coluna representa geralmente um atributo do item.

CGridView oferece suporte a ordenação e paginação dos itens de dados, que podem ser feitas utilizando Ajax ou pelo método normal de solicitação de páginas. Dessa forma, temos uma grande vantagem no CGridView, pois quando o navegador do usuário desativa a utilização de JavaScript, a ordenação e paginação passam automaticamente a serem geradas por meio das solicitações normais de páginas, sem interromper o funcionamento da aplicação.

CGridView deve ser utilizado em conjunto com um data provider, de preferência um CActiveDataProvider. E o código necessário para a utilização do CGridView é o mostrado na Listagem 4.

Listagem 4. Código necessário para utilização do CGridView.

  $dataProvider=new CActiveDataProvider('Post');
   
  $this->widget('zii.widgets.grid.CGridView', array(
      'dataProvider'=>$dataProvider,
  ));

O código inicialmente cria um data provider para a classe ActiveRecord usando o parâmetro POST. Em seguida, usa CGridView para exibir todos os atributos em cada instância Post. A tabela apresentada é equipada com as funcionalidades de ordenação e paginação.

A fim de apresentar de forma seletiva os atributos com diferentes formatos, podemos configurar a propriedade CGridView::columns. Por exemplo, podemos especificar que queremos exibir apenas o title e o create_time. Nós também podemos exibir os atributos dos objetos relacionados usando o ponto, como mostrado na Listagem 5.

Listagem 5. Exibindo atributos de objetos relacionados.

  $this->widget('zii.widgets.grid.CGridView', array(
      'dataProvider'=>$dataProvider,
      'columns'=>array(
          'title',                              // imprime o atributo 'title' 
          'category.name',             // imprime o atributo 'name' da relação 'category' 
          'content:html',                // imprime o atributo 'content' extraido do HTML
          array(                              // imprime 'create_time' usando uma expressão
              'name'=>'create_time',
              'value'=>'date("M j, Y", $data->create_time)',
          ),
          array(                              // imprime 'author.username' usando uma expressão
              'name'=>'authorName',
              'value'=>'$data->author->username',
          ),
          array(                              // imprime uma colunai com os botões "view", "update" e "delete" 
              'class'=>'CButtonColumn',
          ),
      ),
  ));

Componente de Aplicação

A aplicação é responsável por gerenciar um conjunto de componentes que possuem recursos específicos, que podem ser facilmente customizados. Para isso devemos inicialmente alterar a configuração de aplicação implementando uma outra entrada na propriedade components, como na Listagem 6.

Listagem 6. Implementação de uma nova entrada na propriedade components.

  return array(
      // 'preload'=>array('xyz',...),
      'components'=>array(
          'xyz'=>array(
              'class'=>'ext.xyz.XyzClass',
              'property1'=>'value1',
              'property2'=>'value2',
          ),
      ),
  );

Assim conseguiremos acessar o componente em toda a nossa aplicação usando a linha de código Yii::app()->xyz.

Ação

As ações são usadas pelo controle para atender uma determinada requisição do usuário. Suponha que temos a classe de ação XyzClass, que pertence a extensão xyz: poderemos utilizá-la se sobrescrevermos o método CController::actions no nosso controle, como mostrado na Listagem 7.

Listagem 7. Sobrescrita do método Ccontroller:actions.

  class TestController extends CController
  {
      public function actions()
      {
          return array(
              'xyz'=>array(
                  'class'=>'ext.xyz.XyzClass',
                  'property1'=>'value1',
                  'property2'=>'value2',
              ),
              // outras ações
          );
      }
  }

Para acessarmos essa ação devemos utilizar a rota test/xyz.

Filtro

Serve basicamente para pré e pós processar as requisições os usuários executados por uma ação. Seguindo o nosso exemplo, se tivermos uma classe filtro XyzClass, que pertence a extensão xyz, podemos usá-la sobrescrevendo o CController::filters no nosso controle, como na Listagem 8.

Listagem 8. Sobrescrevendo o CController:filters.

  class TestController extends CController
  {
      public function filters()
      {
          return array(
              array(
                  'ext.xyz.XyzClass',
                  'property1'=>'value1',
                  'property2'=>'value2',
              ),
              // outros filtros
          );
      }
  }

Nesse exemplo poderíamos ter utilizado os operadores + e - no primeiro elemento do vetor para limitar as ações que serão aplicadas através de cada filtro.

Comando de Controle

Existem também extensões do tipo comando de controle que são usadas para adicionar comandos no yiic, uma ferramenta de linha de comando. Por exemplo, se tivermos o comando de console XyzClass que pertence a uma extensão chamada xyz, poderemos usá-la implementando nas configurações da aplicação de console, como mostrada na Listagem 9.

Listagem 9. Implementando as configurações da aplicação de controle.

  return array(
      'commandMap'=>array(
          'xyz'=>array(
              'class'=>'ext.xyz.XyzClass',
              'property1'=>'value1',
              'property2'=>'value2',
          ),
          // outros comandos
      ),
  );

Assim teremos disponível o comando xyz na ferramenta yiic.

Criando Extensões

Como uma extensão é criada visando sua utilização por outros desenvolvedores, isso faz com que tenhamos alguns esforços adicionais para criá-las. A seguir veremos alguns princípios que devem ser seguidos ao criar uma nova extensão:

  • Uma extensão deve ser autossuficiente, ou seja, a sua dependência externa deve ser mínima. Seria uma dor de cabeça para seus usuários se uma extensão necessitar de pacotes adicionais, classes ou arquivos de recursos, mas como já vimos na seção de instalação de extensões, infelizmente isso não acaba acontecendo com muita frequência;
  • Arquivos pertencentes a uma extensão devem ser organizados dentro de um mesmo diretório cujo nome deve ser o mesmo da extensão;
  • Classes em uma extensão devem possuir prefixos com alguma letra para evitar conflito de nomes com classes de outras extensões;
  • Uma extensão deve vir com a documentação da API, possuindo detalhes da sua instalação. Isso reduzirá o tempo e o esforço empregados por outros desenvolvedores quando forem usá-la;
  • Uma extensão deve estar usando uma licença apropriada. Se você quiser que a sua extensão seja usada por projetos de código aberto e de código fechado, você pode considerar o uso de licenças como BSD, MIT, etc., mas não GPL;

Uma extensão pode ser classificada como: comportamento, componente de aplicação, widget, controle, ação, filtro, comando de controle e validador. Nos próximos tópicos iremos ver como criar cada uma delas.

Comportamento

Para criar um comportamento (Behavior), deve-se implementar a interface IBehavior. Por conveniência, o Yii fornece uma classe de controle chamada CBehavior que já implementa essa interface e fornece alguns métodos adicionais bem convenientes.

Ao implementar comportamentos para o CModel e o CActiveRecord, também se pode herdar de CModelBehavior e CActiveRecordBehavior, respectivamente. Estas classes base oferecem recursos adicionais que foram feitos especificamente para o CModel e o CActiveRecord. Por exemplo, a classe CActiveRecordBehavior implementa um conjunto de métodos para responder aos eventos de um objeto do ActiveRecord. A classe filha pode, assim, sobrescrever esses métodos para personalizar o código que irá responder o objeto do ActiveRecord.

O código da Listagem 10 mostra um exemplo de um comportamento do ActiveRecord. Quando esse comportamento está ligado a um objeto ActiveRecord e quando este está chamando o método save(), ele irá automaticamente definir os atributos create_time e update_time como a hora atual.

Listagem 10. Exemplo de comportamento do ActiveRecord.

  class TimestampBehavior extends CActiveRecordBehavior
  {
      public function beforeSave($event)
      {
          if($this->owner->isNewRecord)
              $this->owner->create_time=time();
          else
              $this->owner->update_time=time();
      }
  }

Widget

Um widget deve herdar da classe CWidget ou de uma de suas classes filhas.

A maneira mais fácil de criar um novo widget é estendendo um widget existente e substituindo seus métodos ou alternando os valores de suas propriedades padrão. Por exemplo, se você quiser usar um estilo CSS mais atrativo para o CTabView, você pode configurar a propriedade CTabView::cssFile ao usar esse widget. Você também pode estender o CtabView, como na Listagem 11, de modo que não precise mais configurar nenhuma propriedade ao usar o widget.

Listagem 11. Criando um Widget que estende a class CTabView.

  class MyTabView extends CTabView
  {
      public function init()
      {
          if($this->cssFile===null)
          {
              $file=dirname(__FILE__).DIRECTORY_SEPARATOR.'tabview.css';
              $this->cssFile=Yii::app()->getAssetManager()->publish($file);
          }
          parent::init();
      }
  }

No exemplo descrito sobrescrevemos o método CWidget::init e atribuímos a CTabView::cssFile a URL para o nosso novo estilo padrão CSS. Nós colocamos o novo arquivo de estilo CSS no mesmo diretório que contém o arquivo da classe MyTabView, de modo que eles podem estar em um mesmo pacote com a mesma extensão.

Para criar um novo widget do zero é necessário implementar dois métodos: CWidget::init e CWidget::run. O primeiro método é chamado quando usamos o $this->beginWidget para inserir um elemento em uma view, e o segundo método é chamado quando usamos o $this->endWidget. Se quiséssemos capturar e processar o conteúdo exibido entre estas duas invocações de método podemos iniciar um buffer de saída em CWidget::init e recuperar a saída do buffer em CWidget::run para posterior processamento.

Um widget geralmente utiliza arquivos CSS, JavaScript e outros arquivos de recursos na página que o usa. A fim de tornar esses arquivos acessíveis pela internet, precisamos publicá-los usando o CWebApplication::assetManager, como mostrado na Listagem 10. Além disso, se você quiser incluir um arquivo CSS e/ou JavaScript na sua página atual é necessário registrá-lo usando CClientScript, como na Listagem 12.

Listagem 12. Utilizando o CClientScript para registrar nossos arquivos CSS e JavaScript.

  class MyWidget extends CWidget
  {
      protected function registerClientScript()
      {
          // ...publica os arquivos CSS e/ou JavaScript...
          $cs=Yii::app()->clientScript;
          $cs->registerCssFile($cssFile);
          $cs->registerScriptFile($jsFile);
      }
  }

Um widget também pode ter seus próprios arquivos de view. Para isso é necessário criar um diretório chamado view dentro do diretório que contém o arquivo da classe widget e colocar todos os arquivos de view lá. Na classe widget, a fim de usar a view widget, use o $this->render('ViewName'), que é semelhante ao que fazemos em um controller.

AÇÃO

Uma ação deve estender da classe CAction ou de uma de suas classes filhas. O principal método que precisa ser implementado para uma ação é o IAction::run.

FILTRO

Um filtro deve estender da classe CFilter ou de uma de suas classes filhas. Os principais métodos que precisam ser implementados para um filtro são CFilter::preFilter e CFilter::postFilter. O primeiro é chamado antes da ação ser executada e o segundo depois, como na Listagem 13.

Listagem 13. Implementação de um Filtro.

  class MyFilter extends CFilter
  {
      protected function preFilter($filterChain)
      {
          // lógica a ser aplicada ates da ação ser executada
          return true; // falso se a ação não deve ser executada
      }
   
      protected function postFilter($filterChain)
      {
          // lógica a ser aplicada após a ação ser executada
      }
  }

O parâmetro $filterChain é do tipo CFilterChain que contém informações sobre a ação que está sendo filtrada.

CONTROLLER

Um Controller distribuído como uma extensão deve estender de CExtController, ao invés de CController. A principal razão é porque CController assume o ponto de vista de controlador de arquivos que estão localizados na application.views.ControllerID, enquanto CExtController assume os arquivos de view que estão localizados no subdiretório do diretório que contém o arquivo de classe do controlador. Por isso, é mais fácil para redistribuir o controlador, uma vez que seus arquivos de visão estão hospedados junto com o arquivo de classe do controlador.

VALIDADOR

Um validador deve se estender de CValidator e implementar o método Cvalidator::validateAttribute, como o exemplo da Listagem 14.

Listagem 14. Implementação de um Validador.

  class MyValidator extends CValidator
  {
      protected function validateAttribute($model,$attribute)
      {
          $value=$model->$attribute;
          if($value has error)
              $model->addError($attribute,$errorMessage);
      }
  }

COMANDO DE CONSOLE

Um comando de console deve estender da classe CConsoleCommand e implementar o método CConsoleCommand::run. Opcionalmente, pode-se sobrescrever o método CConsoleCommand::getHelp para fornecer informações de ajuda sobre o comando. Veja um exemplo de sua implementação na Listagem 15.

Listagem 15. Implementação de um Comando de Console.

   
  class MyCommand extends CConsoleCommand
  {
      public function run($args)
      {
          // $args série de argumentos de linha de comando para este comando
   
      }
   
      public function getHelp()
      {
          return 'Ajuda: como usar este comando';
      }
  }