Admin - Voltando ao Gii (CRUD) — Parte 2

Cálcio Heavy Metal
PHPRio
Published in
6 min readMay 29, 2018

No artigo anterior basicamente movemos os CRUDs para o Admin do site e customizamos o CRUD Category. No post de hoje vamos customizar o CRUD de produtos (Product).

Customizando o CRUD Product

Nosso CRUD de produtos tem uma peculiaridade, ela está relacionada com uma categoria, isso quer dizer que precisamos utilizar nossas famosas relations (relações).

Vamos começar a customizar o formulário de cadastro/alteração e fazer também o relacionamento com nossa categoria.

Como podem ver o formulário padrão não exibe nenhum relacionamento e exibem campos desnecessários como Created At e Updated At, que foram tratados no post anterior.

Criando o ComboBox — dropdownList() de Categoria no formulário de produto

Abra o arquivo \modules\admin\models\Category.php para criamos o método getAllCategories() e getAllCategoriesInArray() para usamos em nosso cadastro de produtos posteriormente.

use yii\helpers\ArrayHelper;...public function getAllCategories()
{
return Category::find()
->select('id, name')
->orderBy('name')
->where(['status' => self::STATUS_ACTIVE])
->all();
}
public function getAllCategoriesAsArray()
{
return ArrayHelper::map($this->getAllCategories(), 'id', 'name');
}

NOTA: O ArrayHelper é helper muito importante e utilizado no Yii2, uma vez que ele vai facilitar nossas vidas quando se trata de arrays no PHP. E o método map é utilizado para ele fazer o mapeamento de índice => valor no array que utilizamos para construir nossas combos dinâmicas.

Veja o código completo para possível comparação com o anterior.

Você deve estar se perguntando. Por que estou alterando o model Category se vamos alterar o formulário de Produtos?

Muito simples. Isso é devido ao SRP - Single Responsability Principle (Princípio da Responsabilidade Única), onde diz que uma classe e método deve ter somente uma responsabilidade. Ele é o S do S.O.L.I.D. Esse é o princípio mais fácil de se aplicar.

Um outro ponto importante a ser lembrado que no MVC acesso a dados e regras de negócios devem ficar na camada Model (Modelo).

Em outras palavras, em produtos só nos interessa dados referentes a produtos. As categorias fazem parte do model Category e não do model Product, por isso nosso método de pesquisar as categorias estão no model Category.

Agora imagine um sistema maior onde essa categoria fosse usada e diversos outros formulários. O que faria? Iria criar a mesma consultas várias vezes em modelos diferentes? Ou pior ainda iria criar essas consultas em cada formulário que precisasse? Isso não faz sentido nenhum. Não é verdade?

Vocês podem encontrar muitos tutoriais inclusive na doc do Yii 2 sendo usado da seguinte maneira (arquivo ./modules/admin/views/product/_form.php):

<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
use app\modules\admin\models\Category;
?>
...<?= $form->field($model, ‘category_id’)->dropdownList(
ArrayHelper::map(Category::find()->where([‘status’ => Category::STATUS_ACTIVE])->orderBy(‘name’)->all(), ‘id’, ‘name’),
[‘prompt’ => Yii::t(‘app’, ‘Select an item’)]);
?>
...

Essa forma, considero errada pelo ponto de vista das separação das responsabilidades descritas acima. Estamos fazendo uma consulta de banco de dados em uma view e também tratando dados em uma view. A camada view só tem a responsabilidade de exibir os dados e mais nada. A forma que mostrarei logo abaixo já traz a consulta pronta e formatada pronta para o objeto dropdownList() usar.

Acredito que os manuais e outros tutoriais mostram dessa forma para simplificar.

Enviando o mapa de array de categorias para o formulário

Abra o arquivo \modules\admin\controllers\ProductController.php e vamos chamar o model \modules\admin\models\Category.php e vamos recuperar nossa listagem de categorias para passá-las para nossos formulários.

Tanto nos métodos actionIndex(), actionCreate() e actionUpdate($id) foi passado o parâmetro listCategories no $this->render() que contem o nosso array mapeado chamado do objeto $category->getAllCategoriesInArray().

Precisamos passar o valor de listCategories para as views .\modules\admin\views\product\create.php e .\modules\admin\views\product\update.php. Basta adicionar no array o índice listCategories recebendo o parametro $listCategories.

<?= $this->render(‘_form’, [
‘model’ => $model,
‘listCategories’ => $listCategories,
]) ?>

Agora nosso formulário está pronto para criarmos nosso dropdownList().

O código para adicionar essa combo (dropdownList()) no Yii é muito simples.

<?= $form->field($model, ‘category_id’)->dropdownList(
$listCategories,
[‘prompt’ => Yii::t(‘app’, ‘Select an item’)]);
?>

O dropdownList() recebe como parâmetro a variável $listCategories que foi passada pelo controller para as views modules\admin\views\create.php e modules\admin\views\update.php, criamos um array de parâmetros passando o prompt.

Veja abaixo o código completo do formulário.

Ajustando o Model e Criando uma validação customizada

Para nosso formulário funcionar como deve, precisamos alterar algumas coisas no nosso model de Produtos. Abra o modules\admin\models\Product.php. Veja o código completo abaixo.

Como podemos perceber criamos algumas constantes e adicionamos os métodos getStatusItems() e getStatusHighlighted(), cujo os nomes são bem sugestivos. Um gera os status ativos e inativos e o outro indica se o produto é um destaque.

Para que o campo price funcione corretamente com os valores digitados no formato brasileiro, pois se entrarmos com um valor 20,55 ou 135,49 a validação irá informar que o campo price precisa ser um número.

Criamos o método brazilianNumber() para usamos na nossa validação (rules).

Formatando a GridView de produtos

Abra o arquivo modules\admin\views\product\index.php e vamos fazer as mesmas alterações feitas na GridView de categorias. Veja o código abaixo:

O que quero destacar são as mudanças feita nesse código, e nesse post a mais importante aqui foi a mudança feita na coluna (attribute) Category da nossa grid. Pois ela por padrão traz o valor id do model Category, mas fazendo uso da relation (relação) do Yii facilmente altero esse valor (value) para o campo que eu quiser (no nosso caso o campo name). Outra alteração, foi a adição do atributo filter (visto no post anterior) trazendo a lista de Categorias (a mesma usada no formulário). Como exibido no trecho abaixo:

[
‘attribute’ => ‘category_id’, //Campo necessário para que a pesquisa seja habilitada no cabeçalho da tabela.
‘value’ => ‘category.name’, //Relation sedo usada.
’filter’ => $listCategories, //Cria a dropdownList com as categorias
],

Repare que nosso atributo value recebe como parâmetro category.name, onde category representa o nome da nossa relation (abra o model Product e verá o método getCategory() que cuida dessa relation) e name representa o atributo name da tabela Category.

NOTA: Se o método da relation fosse getXpto() ao invés de getCategory() na grid seria chamado xpto.name ao invés de category.name. Outro ponto a observar é que por convenção do Yii, as relations precisam começar com o nome get.

Outra mudança feita, foi no campo (attribute) price cujo usamos o formatter currency. Se consultar a doc sobre os formatters verá que já existem um monte de opção já disponíveis. Confira a documentação.

’format’ => ‘currency’,

NOTA: Precisei editar o arquivo ./config/web.php no parâmetro (índice) formatter > currencyCode. Após o ‘R$’ precisei adicionar um espaço em branco para evitar um erro. Ficando ‘R$ ’. Veja o Código abaixo:

...‘formatter’ => [
‘class’ => ‘app\components\formatters\BrazilianFormatter’,

‘currencyCode’ => ‘R$ ‘, //aqui tem um espaço depois do cifrão
],
...

As outras mudanças foram explicadas no post anterior.

Ajustando o ModelSearch

Como não vamos fazer filtro pelos campos created_at e updated_at podemos remover eles das rules e do andFilterWhere(). Criamos e adicionamos o $this->numberFormat() para que a pesquisa por preço seja feito no padrão brasileiro.

Formatando a DetailView de produtos

Abra o arquivo modules\admin\views\product\view.php e vamos fazer as mesmas alterações feitas na GridView de categorias. Veja o código abaixo:

O que quero destacar são as mudanças feita nesse código. Algumas das modificações que foram feitas também foram feitas na view (detalhes) das categorias, como mudança da largura da coluna de labels, utilização do Status, formatação de data etc. Logo abiaxo vou mostrar como fazer a relação do Model Categoy para trazer a relation que já existe no Model Product, e também utilizar mais um item customizado para exibição do preço no padrão Brasileiro.

Vamos começar com a relation, diferente do index, na view precisamos usar o $model->category->name ficando:

[
‘attribute’ => ‘category_id’,
‘value’ => $model->category->name,
],

Onde $model é o atributo passado para essa view, category é o nome da relation e name é o atributo da tabela que quero exibir.

Sinceramente não sei por que caralhas eles não fizerem da mesma forma que no index.

Para formatar o atribulo price simplesmente usamos o :currency.

Com isso finalizamos as formatações e ajustes no CRUD Product e basicamente todos os CRUDs vão ser formatados e configurados da mesma maneira. Vendo isso é notório que no Yii se desenvolve muito mais rápido que qualquer outro framework do mercado e de forma elegante e respeitando as boas práticas de desenvolvimento.

Mais uma etapa concluída do projeto. Acompanhe o projeto e vejas os arquivos no GitHub: https://github.com/Calcio/vitrine

Espero que tenham gostado desse post. A partir daqui, você já está apto a criar qualquer projeto no Yii. O Yii não para por aqui, ele oferece mt mais recursos, estabilidade etc. Calma não estou dizendo que finalizei o projeto, ainda tem bastante coisa interessante a ser abordada e feita.

--

--