image

PHP: Criando um catálogo de imóveis com o Phreeze parte III – Entendendo o Backend

Neste artigo, vamos entender o código gerado pelo Phreeze conforme descrito no primeiro artigo além de realizar alterações na interface gráfica da aplicação para que assim, nosso Backend seja finalizado possibilitando a criação do Frontend e a aplicação para dispositivos móveis.

Abra sua aplicação no navegador utilizando o endereço local http://localhost:[porta]/imoveis e verifique se a mesma está rodando. Note que temos o menu criado com as opções de Imoveis e TipoImoveis localizados a esquerda do menu e também o link para login localizado a direita conforme observamos na Figura 1.

figura1

Ao clicarmos no link de Imóveis ou TipoImoveis, verificamos que a aplicação nos retorna um erro de permissão com a seguinte mensagem: Login requerido para acessar esta página. Note que logo abaixo da mensagem um formulário é exibido para realizarmos a autenticação na aplicação. Veja a Figura 2.

figura2

Porque isto ocorre? Como dito no primeiro artigo, realizei modificações no framework oficial do Phreeze inserindo a autenticação padrão. Sendo assim, todos os CRUDs gerados já possuem por default um nível mínimo de permissão. Se você executou o script sql para geração das tabelas de autenticação padrão você já possui em sua base de dados dois usuários, um com permissão de Leitura e outro com permissão de Administrador.


Usuário Senha
user pass
admin pass

Utilize o usuário de Administrador e realize a autenticação na aplicação. Veja que conforme a Figura 3, um novo item no menu apareceu, ele faz parte da autenticação padrão e permite a modificação de permissões e criação de usuários.

figura3

Se clicarmos no menu Admin Usuários -> Funções, teremos a lista de funções previamente criadas conforme Figura 4.

figura4

Note que a partir do momento em que realizamos a autenticação como Administrador, as opções de Imóveis e Tipo Imóveis ficaram acessíveis, isto porque o Administrador possui permissão de acesso nestes CRUDs.

Agora que temos uma visão geral da autenticação para entendermos e conseguirmos realizar operações na aplicação como inserção de registros por exemplo, vamos detalhar todas as camadas que o framework gerou, identificando onde cada ponto é responsável na aplicação seja para realização da autenticação, busca na base de dados ou camada visual.

Detalhando a arquitetura MVC da aplicação imóveis

Vamos utilizar o nosso cadastro de Tipo de Imóveis para nossos exemplos pelo simples fato de terem poucos atributos facilitando o entendimento. Primeiramente vamos realizar o entendimento da camada Modelo e seus respectivos arquivos.

Entendendo o Model (Modelo)

O modelo representa os dados referentes a sua aplicação disponibilizando meios de acesso (leitura e escrita) à estes dados. Simplificando, nesta camada vamos realizar as operações que se referem a validação, leitura e escrita dos dados no banco de dados, serviços ou até mesmo arquivos. Os Modelos gerados pelo Phreeze são encontrados no diretório /libs/Model/ da aplicação.

Uma boa prática é trazer para dentro desta camada tudo aquilo que se refere a regras de negócio como por exemplo, cálculos ou validações de integridade de dados. Para o entendimento desta camada vamos passar pelas classes listadas abaixo e entender o que cada um faz dentro da nossa aplicação. São eles:

  • TipoImovel.php
  • TipoImovelDAO.php
  • TipoImovelCriteriaDAO.php
  • TipoImovelMap.php

Para aqueles que desejam se aprofundar mais, recomendo a visualização do vídeo Entendendo o Model Phreeze

  • TipoImovel.php

Abra a classe TipoImovel.php e veja o detalhamento da mesma. Na linha número 15, temos a declaração da classe. Note que a mesma estende a classe TipoImovelDAO. Se você não conhece orientação a objetos, recomendo a pesquisa sobre o assunto. Na Wikipédia, há um resumo sobre o assunto, vá até lá e dê uma olhada antes de continuar este artigo. Orientação a Objetos


class TipoImovel extends TipoImovelDAO {

Além da declaração da classe, temos dois métodos disponíveis, o Validate() e o OnSave($insert).

 public function Validate() {
   return parent::Validate();
 }

 public function OnSave($insert) {
   if (!$this->Validate()) throw new Exception('Unable to Save TipoImovel: ' . implode(', ', $this->GetValidationErrors()));
     return true;
   }

O método Validate() será responsável pelas validações customizadas da aplicação ou seja, nele você poderá aplicar as validações de suas regras de negócio por exemplo. Note que há um código comentado com um pequeno exemplo de validação. Para o método OnSave($insert), realizar validações neste ponto se tornam redundantes, portanto, deixe o mesmo como está e realize as validações no método Validate().

  • TipoImovelDAO.php

Visitamos esta classe para entender o que ela faz já que nossa classe TipoImovel.php estende a mesma. Basicamente esta classe possui os atributos do banco de dados e estende a classe Phreezable que é a classe principal do framework. Aprofundando a classe Phreezable, podemos observar que a mesma é responsável pelos métodos de tratamento dos dados sejam eles para leitura, escrita, atualização e remoção dos registros ou então, validações e tratamento de erros.

  • TipoImovelCriteriaDAO.php

Esta classe é responsável pelos critérios de busca ao realizar uma pesquisa, ela cria para cada atributo, vários tipos de critérios específicos para busca. São criados buscas utilizando “=”, “Like”, “Maior”, “Menor”, Intervalo de valores e outros tipos de critérios.

 public $Id_Equals;
 public $Id_NotEquals;
 public $Id_IsLike;
 public $Id_IsNotLike;
 public $Id_BeginsWith;
 public $Id_EndsWith;
 public $Id_GreaterThan;
 public $Id_GreaterThanOrEqual;
 public $Id_LessThan;
 public $Id_LessThanOrEqual;
 public $Id_In;
 public $Id_IsNotEmpty;
 public $Id_IsEmpty;
 public $Id_BitwiseOr;
 public $Id_BitwiseAnd;

 

  • TipoImovelMap.php

Está é uma classe muito importante dentro da aplicação. Ela é gerada automaticamente com base nas tabelas do banco de dados. Vamos analisar o trecho a seguir, ele é importante para nós em caso de possíveis alterações e modificações no banco de dados.

 public static function GetFieldMaps()
{
   if (self::$FM == null)
   {
     self::$FM = Array();
     self::$FM["Id"] = new FieldMap("Id","tipo_imovel","id",true,FM_TYPE_INT,11,null,true,false);
     self::$FM["Descricao"] = new FieldMap("Descricao","tipo_imovel","descricao",false,FM_TYPE_VARCHAR,255,null,false,true);
   }
 return self::$FM;
}

O trecho abaixo, é responsável pelo mapeamento do atributo no banco.

self::$FM["Descricao"] = new FieldMap("Descricao","tipo_imovel","descricao",false,FM_TYPE_VARCHAR,255,null,false,true);

 

Analisando o construtor do método, temos o seguinte:

/**
 * Initializes the FieldMap
 *
 * @param string $pn Nome da Propriedade
 * @param string $tn Nome da Tabela no Banco de Dados
 * @param string $cb Nome da coluna no banco de dados
 * @param bool $pk Verdadeiro ou falso se é uma chave primária
 * @param int $ft Tipo do campo no banco de dados
 * @param variant $fs Tamanho do campo
 * @param variant $dv Valor padrão
 * @param bool $iai Insere automaticamente (Auto increment)
 * @param bool $rq o campo é requerido.
 */
 public function __construct($pn, $tn, $cn, $pk = false, $ft = FM_TYPE_UNKNOWN, $fs = 0, $dv = null, $iai = null, $rq)
 {
   $this->PropertyName = $pn;
   $this->TableName = $tn;
   $this->ColumnName = $cn;
   $this->IsPrimaryKey = $pk;
   $this->FieldType = $ft;
   $this->FieldSize = $fs;
   $this->DefaultValue = $dv;
   $this->IsAutoInsert = $iai;
   $this->IsRequired = $rq;
 }

Veja que temos várias propriedades que podemos estar modificando de acordo com a necessidade, um exemplo disto é a propriedade IsRequired que quando verdadeira, exige que o usuário digite um valor na tela. Veja que estas propriedades são geradas a partir do banco de dados portanto, se seu atributo no banco for “Not Null”, ele será obrigatório aqui.

Com isso fechamos de forma resumida a camada de modelo gerada pelo Phreeze. É necessário se aprofundar mais e para isso, verificar a documentação do framework ajuda muito.

Entendendo o Controller (Controle ou Controlador)

O Controller é responsável pela integração entre a camada de modelo (Model) e a camada de visão (View). Basicamente, a “view” irá realizar uma solicitação para o “Controller” como por exemplo uma coleção de dados ou então a solicitação de remover algum item do banco e o “Controller” por sua vez, irá enviar a instrução para que a camada “Model” execute.

Nesta camada, também podemos realizar verificações que não se referem a regras de negócio, visto que a boa prática é manter as essas regras na camada “Model”. Vamos então analisar o artefato que o nosso framework gerou na camada de “Controller”.

Vamos abrir a classe TipoImoveisController.php no diretório  /libs/Controller/. Vamos entender cada parte do código gerado pelo framework.

Veja que temos o método Init(). Este método é acessado toda vez que uma view ou um serviço é requisitado.

protected function Init()
 {
 parent::Init();
 
   /**
   * Informe o tipo de permissao
   */
   $this->RequirePermission(User::$PERMISSION_READ, 
   'Secure.LoginForm', 
   'Login requerido para acessar esta pagina',
   'Permissao de leitura e obrigatoria');
 }

Veja que conforme dito anteriormente, o código de verificação de usuário autenticado foi inserido neste método por padrão na de autenticação. Ele exige que o usuário esteja autenticado e que possua permissão de leitura. O método é composto de:

  • Permissão: neste caso a permissão solicitada é a de leitura conforme código User::$PERMISSION_READ.Você pode modificar o tipo de permissão para:
    • $PERMISSION_READ: Leitura
    • $PERMISSION_WRITE: Gravação (Novo registro)
    • $PERMISSION_EDIT: Edição
    • $PERMISSION_ADMIN: Administrador
  • Rota: Neste caso, se o usuário não estiver autenticado e com a permissão necessária, a aplicação enviará o usuário para rota ‘Secure.LoginForm’.
  • Mensagem de erro: É a mensagem que irá aparecer para o usuário.
  • Mensagem de Permissão: Reflete a mensagem de erro com a descrição da permissão necessária para acessar a página.

Por enquanto, vamos manter o aplicativo assim. Vamos voltar neste assunto quando realizarmos a construção do Frontend no próximo artigo.

O próximo método a ser explicado é o Query(). Este método é responsável pela busca dos dados na base utilizando paginação, critérios de busca e ordenação.

Basicamente o que é gerado aqui é um método padrão de lista. Mais para frente, vamos realizar alguns testes utilizando possibilidades de busca e critérios mantendo a estrutura do método Query sem alterações. O ponto principal a ser observado aqui, é o filtro. Por padrão, o Builder insere todos os campos da tabela na busca. Se você observar a visualização da lista gerada, verá que a tela possuí apenas um campo de busca conforme Figura 4.

O que acontece é que, quando você insere um valor no campo de busca, a pesquisa é realizada buscando aquele valor em todos os campos da tabela com o uso do “like” que busca em qualquer parte do campo na base de dados, aquilo que você digitou. Caso você queira realizar a pesquisa de apenas um determinado campo, é necessário alterar estas linhas para os campos desejados.

 $filter = RequestUtil::Get('filter');
 if ($filter) $criteria->AddFilter(
 new CriteriaFilter('Id,Descricao'
 , '%'.$filter.'%')
 );

Neste momento, não vamos entrar nos outros métodos visto que basicamente eles são responsáveis por exibir, inserir, atualizar e remover os registros da base de dados. Não há muito segredo aqui mas em caso de dúvidas você pode utilizar os comentários no final deste artigo.

Entendendo a camada View (Visão)

A camada de visão como o próprio nome diz, é a camada responsável por tudo aquilo que é visual ou seja, páginas de inclusão, listagem, menu rodapé etc. Tudo aquilo que interage com o usuário, deve estar presente nesta camada.

A View, não realiza operações no banco de dados, ela as solicita e exibe através da camada Controller que conforme dito anteriormente, realiza a intermediação entre a View e Model.

Nesta camada temos dois arquivos:

  • tipoimovellistview.blade.php
  • tipoimovels.js

Não iremos detalhar os arquivos aqui, visto que basicamente eles possuem a lista de registros, campos para inclusão e botões referentes a ações do registro. Sempre que tiver uma dúvida, utilize os comentários pois o mesmo será respondido o mais breve possível.

Note que o arquivo tipoimovellistview.blade.php representa o conteúdo a ser apresentado na tela, com tags de html e componentes baseados no template que utilizamos, o blade. Procure entender todo o conteúdo do arquivo e realize a leitura da documentação do blade.

O arquivo tipoimovels.js representa o javascript da página. Nele as chamadas ajax são utilizadas para paginação, ordenação, remoção, inserção ou atualização do registro. Lembre-se que nesta camada não temos acesso direto a base de dados portanto, este javascript é responsável apelas pela passagem de parâmetros e ações para o controller.

Incluindo uma Imagem

Agora que já entendemos o padrão MVC, vamos inserir as imagem no nosso registro. Disponibilizei aqui mesmo algumas imagens para colocarmos nos imóveis. Estarei disponibilizando alguns métodos de upload de imagens, há possibilidade de inserir múltiplas imagens ou uma imagem única, você deverá implementar o que for mais adequado para você. Nosso banco de dados está preparado para apenas uma imagem, portanto para inserir mais de uma por registro, será necessário melhorar a modelagem do banco.

Para melhorar a interface de inclusão de imóvel, modifiquei a combo de Tipo de imóvel para abaixo do Id e também realizei a mudança do nome. O trecho ficou assim:

 <div id="idInputContainer" class="control-group">
 <label class="control-label" for="id">Id</label>
 <div class="controls inline-inputs">
 <span class="input-xlarge uneditable-input" id="id"><%= _.escape(item.get('id') || '') %></span>
 <span class="help-inline"></span>
 </div>
 </div>
 <div id="tipoImovelIdInputContainer" class="control-group">
 <label class="control-label" for="tipoImovelId">Tipo Imovel Id</label>
 <div class="controls inline-inputs">
 <select id="tipoImovelId" name="tipoImovelId"></select>
 <span class="help-inline"></span>
 </div>
 </div>

Se você quiser, pode motificar as labels deste arquivo conforme desejar, no meu caso alterei a label de Tipo Imovel Id para Tipo de Imóvel. As acentuações também podem ser inseridas.

Para inserir as imagens, não realizei o upload por enquanto. Informei apenas a url da mesma e criei uma div para a pré-visualização da imagem após o registro ser salvo. Ficou assim:

 <div id="imagemInputContainer" class="control-group">
 <label class="control-label" for="imagem">Preview</label>
 <div class="controls inline-inputs">
 <img src="<%= _.escape(item.get('imagem') || 'http://localhost:9999/imoveis/images/na.png') %>" />
 <span class="help-inline"></span>
 </div>
 </div>

Esta div foi colocada logo após a div de imagem. Note que em caso da imagem vir em branco, a imagem selecionada será uma outra, no caso, criei uma imagem com o texto “não há imagem para exibição”. O resultado obtido ficou conforme a Figura 5.

figura5

Nota: Se você for “expor” as APIs para a criação do App Mobile, você deverá liberar o acesso de todas as origens para o serviço. Para isto, abra o arquivo AppBaseController.php e insira o trecho CORS conforme código abaixo.

require_once("verysimple/Phreeze/Controller.php");

/** CORS */
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");

Por fim, finalizamos o entendimento da aplicação gerada com uma implementação básica de Backend para um catálogo de imóveis. Nos próximos dois tutoriais, vamos realizar a criação do Frontend utilizando AngularJS e a criação de um aplicativo mobile para exibição do catalogo permitindo a busca dos imóveis utilizando o Ionic Framework.

As imagens que utilizei neste tutorial podem ser baixadas clicando aqui.

Baixar Imagens

Tiago Carpanese
Tiago Carpanese
Criador deste site, apaixonado por tecnologia, busco contribuir para a segregação do conhecimento. Não deixe de curtir este site no Facebook clicando em Curtir na Barra da Direita. Siga-me no Twitter para ficar por dentro das novidades. Não deixe de comentar !
  • http://www.rangelweb.net46.net Rangel Prado

    Show, muito bom!

    Valeu

  • http://twitter.com/joaoricardo_rm JoaoRicardo_RM

    Tiago, obrigado por esse texto. Ajudou a esclarecer alguns detalhes.

    Achei o Phreeze um dos frameworks mais fáceis de entender. Tanto que usei ele no meu projeto de TCC, um sistema de gerenciamento de eventos e certificados.

    Para quem quiser dar uma olhada, ele está disponível em certifica-me.tk (url que redireciona para uma hospedagem sem domínio, já que é de testes)
    Login: administrador, senha: 123

    O código fonte do sistema está no meu GitHub se alguém se interessar.
    https://github.com/joaoricardorm/tcc

    • Tiago Carpanese

      Muito Bom João Ricardo.

      Parabéns pelo trabalho. E obrigado. Em breve estarei realizando posts com maior frequência.

      Fico feliz em ter contribuído, esta é a intenção dos artigos.

      Forte abraço.

  • Yure Antunes Fernandes

    Queria saber como deixo os campos obrigatórios, com exceção de alguns

    • Tiago Carpanese

      No map do seu campo, no último atributo o mesmo deve estar como true!

      Ex:
      self::$FM[“Descricao”] = new FieldMap(“Descricao”,”tipo_imovel”,”descricao”,false,FM_TYPE_VARCHAR,255,null,false,true);

      • Yure Antunes Fernandes

        Ainda estou conseguindo fazer o registro sem preencher nada, o que eu faço?

      • Yure Antunes Fernandes

        Pelo o que eu entendi, meu banco de dados em seus respectivos campos devem estar NOT NULL, pra me não conseguir inserir dados no banco e da uma mensagem de erro certo? Será que tem como fazer de forma gráfica o not null ou só em linha de comando?

      • Yure Antunes Fernandes

        Infelizmente eu não consegui fazer funcionar, eu tentei deixar o banco de dados como not null, e coloquei esse true no final, mesmo assim consigo registrar no banco sem preencher nada nos campos. O que será que estou fazendo errado?

  • Guilherme Gielow

    Uma dúvida eu preciso mostrar o campo descrição da tabela pessoa que é FK na tabela de representantes e este é FK na tabela de clientes. No view eu fiz no ClienteReporter mas para inserir/alterar ele traz somente o campo id do representante. Aonde tudo que preciso alterar?

    • Tiago Carpanese

      Basta alterar o controller.

  • Diego

    Ola, Tiago…Primeiramente parabéns pelo post, muito bom. Sou bem nub em php e queria saber como se faz o upload da imagem para a pasta imagem..vlw

    • Tiago Carpanese

      Tem que usar base 64 para fazer o upload. Dai no controller você pode tratar ela e colocar numa pasta.

  • Marcio Seabra

    Muita gente me indicou o Phreeze, mas agora tô apanhando pra conseguir fazer o upload das imagens.
    Tentei de tudo mas não consigo.
    Alguém tem uma dica?

    • Tiago Carpanese

      Tem que usar Base 64. O Phreeze é muito bom, porém há outros frameworks que se mantém em evolução. Procure Laravel e Yii2. O Yii2 Possui interface de geração de telas, código etc.

  • Luiz Almeida Junior

    Olá, to seguindo teu tutorial, mas fiquei com duvida na paginação, me surgiu uma ideia de diminuir o numero de linhas na tabela, por definição vem 10, mas gostaria de apenas 3.

    Sabe me responder como faço isso ?

    Desde já grato

    • Amaury Gomes Ibanez

      Paginação ta scripts/view.js

      getPaginationHtml: function(page)
      {

      da uma olhada…

  • Thiago Queiroz

    Tiago meu xará, parabéns pelo blog com ótimos tutoriais, para ficar completo você deveria postar como fazer o Upload da Imagem aqui pra gente. Pleaseeee!!!!