Upload
diumberg-c-durval
View
280
Download
2
Embed Size (px)
DESCRIPTION
Apostila Simfony
Citation preview
Advanced Web DeveloperPHP e MySQL
www.4linux.com.br
- 2
SumárioCapítulo 1 Introdução......................................................................................................................................6
1.1. MVC: Model – View – Controller .......................................................................................71.2. Object Relational Map........................................................................................................91.3. AJAX: Asynchronous Javascript And XML.......................................................................101.4. Symfony: Framework de desenvolvimento em PHP.......................................................111.5. Eclipse: Integrated Development Environment..............................................................12
Capítulo 2 Instalando o ambiente de desenvolvimento...............................................................................13
2.1. Gerenciando pacotes com o apt-get................................................................................132.2. Instalando servidores e aplicativos básicos....................................................................142.3. Instalando a IDE Eclipse com o EasyEclipse..................................................................15
Capítulo 3 Instalando o Symfony..................................................................................................................16
3.1. Configurando um domínio virtual para a aplicação........................................................163.2. Porque é tudo tão complicado?.......................................................................................18
Capítulo 4 Criando uma aplicação rápida com o Symfony..........................................................................19
4.1. Preparando o banco de dados.........................................................................................204.2. Criando o projeto no Symfony.........................................................................................214.3. Criando uma aplicação....................................................................................................214.4. Lendo o banco de dados e criando classes de acesso....................................................224.5. Configurando o Doctrine para o scaffolding...................................................................234.6. Ajustando rótulo de chaves estrangeiras........................................................................254.7. Criando um menu para navegação..................................................................................274.8. Criando uma página inicial..............................................................................................30
Capítulo 5 Entendendo o Symfony................................................................................................................32
5.1. Introdução teórica............................................................................................................325.2. Estrutura de diretórios....................................................................................................325.3. O MVC do Symfony..........................................................................................................345.4. Camada Model..................................................................................................................355.5. Camada View....................................................................................................................35
- 3
5.6. Camada Controller...........................................................................................................365.7. Aplicações, Módulos e Ações...........................................................................................375.8. Ambientes e Handlers......................................................................................................385.9. Configurando Ambientes.................................................................................................395.10. A URL no Symfony.........................................................................................................42
Capítulo 6 Usando o EasyEclipse..................................................................................................................45
6.1. Introdução teórica............................................................................................................456.2. Iniciando um projeto........................................................................................................456.3. Criando um projeto..........................................................................................................466.4. Visão Geral da ferramenta...............................................................................................466.5. Configurações Úteis.........................................................................................................47
Capítulo 7 Camada Controller: controlando as requisições e respostas.....................................................48
7.1. Ações (Actions)................................................................................................................497.2. Término da ação...............................................................................................................527.3. Redirecionamento............................................................................................................537.4. ilizando sessões de usuário..............................................................................................577.5. Atributos Flash – variáveis de sessão descartáveis........................................................577.6. Repassando variáveis para a View..................................................................................58
Capítulo 8 Camada Model: acesso ao banco de dados................................................................................59
8.1. Introdução teórica............................................................................................................598.2. Classes de Tabelas...........................................................................................................628.3. Acessando dados através do Model.................................................................................628.4. Utilizando critérios no Doctrine......................................................................................628.5. Métodos personalizados...................................................................................................63
8.5.1. Método especial __toString.....................................................................................................638.6. Sobrescrita de métodos...................................................................................................64
Capítulo 9 Camada View: Apresentação.......................................................................................................66
9.1. Introdução teórica............................................................................................................669.2. A montagem da View.......................................................................................................669.3. Partials (Parciais).............................................................................................................699.4. Variáveis do Symfony.......................................................................................................709.5. Helpers.............................................................................................................................71
- 4
Capítulo 10 Forms: criando formulários.........................................................................................................73
10.1. Introdução teórica..........................................................................................................7410.2. Gerando formulários automaticamente........................................................................7410.3. Widgets...........................................................................................................................7710.4. Validators.......................................................................................................................7910.5. Labels.............................................................................................................................8210.6. Controlando o formulário...............................................................................................8310.7. Exibindo o formulário....................................................................................................87
10.7.1. Método automatizado...........................................................................................................8710.7.2. Método detalhado.................................................................................................................90
Capítulo 11 AJAX.............................................................................................................................................92
11.1. Introdução teórica..........................................................................................................9211.2. Incluindo Javascript.......................................................................................................9311.3. Criando um link para uma função.................................................................................9411.4. Alterado o conteúdo de um elemento HTML................................................................9411.5. Chamando uma função remota......................................................................................95
11.5.1. Atualizações Periódicas........................................................................................................9811.5.2. Formulários AJAX..................................................................................................................9911.5.3. Callbacks.............................................................................................................................101
Apêndices...................................................................................................................................103
Capítulo 12 ApêndiceBanco de dados "livraria"..........................................................................................................104
Capítulo 13 ApêndicesfDoctrineGuard plugin.............................................................................................................111
13.1.1. Implementando segurança em sua aplicação....................................................................11213.1.2. Administrando seus usuários, permissões e grupos..........................................................11413.1.3. Personalizando os templates do módulo sfGuardAuth......................................................11413.1.4. Personalizando os templates do módulo sfGuardAuth......................................................11513.1.5. Classe sfGuardSecurityUser...............................................................................................11513.1.6. Flag superadministrador....................................................................................................11613.1.7. Validators (Validadores).....................................................................................................11613.1.8. Personalizando o Model sfGuardUser................................................................................11713.1.9. Checar a senha do usuário com um método externo.........................................................118
- 5
13.1.10. Alterar o algoritmo utilizado para armazenar senhas.....................................................11913.1.11. Alterando o nome ou o período de expiração do cookie "Remember Me"......................11913.1.12. Personalizando a manipulação de redirecionamento de sfGuardAuth...........................12013.1.13. Configurando o formulário de login.................................................................................120
Capítulo 1 Introdução - 6
Capítulo 1
Introdução
Objetivo do Curso
Este curso de Advanced Web Developer PHP e MySQL - 444 - visa dar bases sólidas ao aluno para a construção de sistemas Web modernos, robustos, seguros e fundamentados em técnicas dedesenvolvimento atuais e produtivas.
Cabe lembrar que este nao é um curso de Webdesign não há preocupação com
a apresentação visual (layout) das páaginas criadas, e sim com sua funcionalidade. Não obstante, os arquivos são criados obedecendo os padrões da W3C (World Wide Web Consortium) em XHTML válido ou muito próximo disso, possibilitando uma ampla liberdade de criação de padrões visuais (templates) com o uso de folhas de estilo em
cascata (CSS). Isto sem que a funcionalidade seja abalada em qualquer momento.
O curso vai trabalhar com os seguintes conceitos, tecnologia, framework e IDE:
• MVC - Model-View-Controller
• ORM - Object-Relational Mapping
Capítulo 1 Introdução - 7
• AJAX - Asynchronous Javascript And XML
• Symfony - Framework de desenvolvimento em PHP
• Eclipse - Integrated Development Environment
1.1. MVC: Model – View – Controller
Model-View-Controller (MVC) é um padrão de arquitetura de software. Sua intenção é facilitar o desenvolvimento e manutenção de códigos em projetos de médio e grande porte, separando os dados(Model) e o layout, apresentação (View) da lógica de interação com o usuário final.
Deste modo facilita também o trabalho entre equipes mistas de DBA (administradores de Banco de Dados), programadores e webdesigners, no caso aplicações web.
• Model: Lógica de acesso a dados. Esta camada encapsula em classes toda lógica de acesso a dados. Normalmente a persistência de uma aplicação é feita num banco de dados, mas a camada Model não obrigatoriamente é específica para banco de dados, ela se ocupa de manter consistente a interação com uma base de persistência que normalmente é um banco de dados.
• View: Lógica de Apresentação. Representa a parte a apresentação visual das informações. E também onde o usuário interage com o sistema. Geralmente trata de apresentar os dados do Model.
• Controller: Lógica da aplicação. Recebe as ações do usuário, processa, valida dados e realiza operações no Model. No sentido contrário, devolve informações a serem visualizadas através da View.
Capítulo 1 Introdução - 8
Figura1.1 :Exemplo de Funcionamento do MVC
Capítulo 1 Introdução - 9
1.2. Object Relational Map
ORM é uma metodologia de acesso a banco de dados relacionais através de orientação a objeto. É feito um mapeamento entre as tabelas do banco de dados e classes que a acessam de forma transparente os dados destas tabelas.
Geralmente a utilização do ORM implica alguma forma de “varredura” no banco de dados para reconstruir todas as tabelas e suas relações em forma de classes. Uma vez realizada, a linguagem SQL é praticamente eliminada da programação, apesar de ainda ser possível na maioria dos casos. Desta forma a abstração de base de dados (um código, multiplos bancos de dados possíveis) chega a um nível bastante elevado.
O Symfony em sua versão 1.4 utiliza o Doctrine (http://www.doctrine-project.org) como ORM principal, que é um projeto baseado no Hibernate do Java, mas ainda dispõe a opção de utilizar o Propel como ORM (http://propel.phpdb.org).
Capítulo 1 Introdução - 10
1.3. AJAX: Asynchronous Javascript And XML
AJAX (Asynchronous Javascript And XML) é uma metodologia de uso de HTML ou XHTML, Cascading Style Sheets, Javascript, Document Object Model, XML, XSLT e o objeto XMLHttpRequest, providas por navegadores, para tornar páginas mais interativas com o usuário, utilizando-se de solicitações assíncronas de informações.
O AJAX não é um modelo, não é uma tecnologia, mas um conjunto de tecnologias que juntas criam para aplicativos web um novo padrão de usabilidade, muitas vezes semelhante a aplicativos desktop.
Curiosamente o AJAX nasceu de uma violação de padrões que a Microsoft costuma fazer em seus produtos. Em 1998 ela introduziu uma biblioteca que permitia fazer consultas usando o protocolo HTTP de maneira autônoma e assíncrona ( XMLHttpRequest). Inicialmente não houve utilidade para este recurso, que ficou um pouco esquecido.
Tempos depois é que se percebeu que em conjunto com outras tecnologias podia prover uma experiência mais rica ao usuário Web.
Inicialmente o AJAX era implementado “na mão grande”, com dificuldades para ser executado em vários navegadores. Atualmente existem vários frameworks que são utilizados em conjunto com linguagens de programação no lado do servidor, como jQuery, Prototype, Dojo, Mootools, ExtJs. O Symfony possui plugins para alguns deles.
Capítulo 1 Introdução - 11
1.4. Symfony: Framework de desenvolvimento em PHP
É um conjunto de classes que cooperam entre si, fornecendo soluções integradas para problemas comuns no desenvolvimento de software.
As linguagens de programação utilizadas simplesmente com seus recursos básicos não fornecem capacidade de solução imediata de diversos problemas corriqueiros de programação – aplicações multibanco, telas básicas de CRUD ( Create, Retrieve, Update, Delete), etc.
O uso de soluções avulsas rapidamente cria uma “colcha de retalhos”, e o framework se propõe a resolver isto.
Um framework necessita das seguintes características:
• Reutilização: possibilidade de reuso de componente.
• Extensibilidade: possibilidade de ampliar recursos.
• Segurança: confiabilidade de execução e implementação
• Completude: atender todas as necessidades dentro de um escopo de atuação.
Um framework não precisa reinventar a roda, ainda mais se tratando de software livre. É muito mais interessante atuar com uma “cola” que junta tecnologias já sedimentadas e as reune sob ma metodologia unificada de trabalho.
Symfony é um framework web escrito em PHP5 que segue o paradigma MVC. Tem como objetivo acelerar a criação e manutenção de aplicações web. Utiliza-se de várias tecnologias sedimentadas em sua base ( como os ORMs Doctrine e Propel, entre outras), extrai muitas de suas ideias do consagrado framework Ruby-On-Rails.
Além de ser um dos mais robustos, eficientes e documentados frameworks em PHP da atualidade, conta com um desenvolvimento e comunidade muito ativos. Poderia-se dizer que juntamente com o CakePHP e o ZendFramework são os três mais influentes frameworks em PHP no momento.
Capítulo 1 Introdução - 12
1.5. Eclipse: Integrated Development Environment
Eclipse é uma IDE de código aberto para o desenvolvimento de softwares.
O projeto Eclipse foi patrocinado pela IBM que desenvolveu a primeira versão do produto e doou-o como software livre para a comunidade. Hoje o Eclipse é a IDE Java mais utilizada no mundo.
Entretanto, o Eclipse é uma IDE altamente customizável e expansível. Assim não só aplicativos Java podem ser desenvolvidos utilizando o Eclipse, mas diversas outras linguagens. O PHP possui atualmente dois plugins livres para o Eclipse ( PHPEclipse e PDT), além da versão do consagrado Zend Studio( a Zend é a empresa que coordena o projeto PHP, fundada por dois de seus principais desenvolvedores).
Capítulo 2 Instalando o ambiente de desenvolvimento - 13
Capítulo 2
Instalando o ambiente de
desenvolvimento
2.1. Gerenciando pacotes com o apt-get
Nosso curso será realizado numa plataforma GNU/Linux utilizando a distribuição Debian 5 ( Lenny).O gerenciador de pacotes a ser utilizado é o apt-get. Para o utilizarmos, precisamos inicialmente configurar os repositórios no arquivo /etc/apt/sources.list. No nosso caso, para agilizar a instalação e evitar o tráfego intenso para o link externo, usaremos um repositório interno.
1. Configurando as fontes
1. Logamos como root
su root
2. Entramos no editor VIM
vim /etc/apt/sources.list
3. Adicionamos as seguintes linhas no arquivo
deb http://192.168.1.1/debian lenny main contrib non-freedeb http://packages.dotdeb.org lenny all
4. Agora atualizaremos a base de dados local
apt-get update
Capítulo 2 Instalando o ambiente de desenvolvimento - 14
2.2. Instalando servidores e aplicativos básicos
1. Instalando o servidor web Apache2
# apt-get install apache2
2. Instalando o PHP 5
# apt-get install php5 php5-cli
3. Instalando o SGDB MySQL
# apt-get install mysql-server
4. Instalando o suporte XSL no PHP 5
# apt-get install php5-xsl
5. Instalando um gerenciador Web para o MySQL
# apt-get install phpmyadmin
6. Reiniciando o servidor web
# /etc/init.d/apache2 restart
Capítulo 2 Instalando o ambiente de desenvolvimento - 15
2.3. Instalando a IDE Eclipse com o EasyEclipse
O Eclipse tem várias formas de instalação: download direto, instalação via pacotes, etc. A forma mais simples de ter um ambiente rapidamente configurado e ainda poder levar aonde se quiser, é fazer uso dos pacotes pré-configurados do EasyEclipse, encontrados em:
http://www.easyeclipse.org
A grande vantagem é que os pacotes vem prontos, com a versão do Java necessária e um conjunto de plugins afins de cada linguagem de programação. Ele não muda o Eclipse, apenas o distribui com diferentes conjuntos de plugins.
1.Baixando o EasyEclipse
Acesse:
http://www.easyeclipse.org/site/distributions/php.html
E realize o download conforme o sistema operacional desejado (em nosso caso, versão Linux).
2.Instalando o EasyEclipse
O EasyEclipse é um pacote compactado pronto. Basta descompactar num diretório e rodar o executável. Para facilitar, vamos criar um atalho para seu acesso (chamado no Gnome de Lançador).
cd /optTar -xvzf /home/aluno/Downloads/easyeclipse-php-1.2.2.2.tar.gz
Para criar o atalho/lançador, clique com o botão direto na Área de Trabalho e selecione Criar Lançador.
Capítulo 3 Instalando o Symfony - 16
Capítulo 3
Instalando o Symfony
O método utilizado será a instalação via php-pear, que nos permite economizar espaço e facilita atualizações do Symfony além de backups das aplicações.
1. Atualizando canal do Symfony no PEAR
pear channel-discover pear.symfony-project.com
2. Instalando o Symfony através do PEAR
pear install symfony/symfony-1.4.4
3.1. Configurando um domínio virtual para a aplicação
Primeiro vamos criar um domínio virtal no Apache criando o arquivo:
/etc/apache2/sites-available/livraria444
Estre arquivo terá o seguinte conteúdo (o arquivo está comentado para melhor entendimento)
<VirtualHost *> # Nome do domínio virtual ServerName livraria.local # Diretório dos arquivos do domínio virtual DocumentRoot “/home/aluno/livraria444/web” # Padrão do índice de diretório (apenas o index.php)
Capítulo 3 Instalando o Symfony - 17
DirectoryIndex index.php # Permissões do diretório dos arquivos <Directory “/home/aluno/livraria444/web/”> AllowOverride All Allow from All </Directory></VirtualHost>
Agora ativamos o domínio virtual com o comando
a2ensite livraria444
O Symfony faz uso do módulo rewrite do Apache para criar URLs amigáveis (endereços sem .php, ponto de interrogação ou “E” Comercial).
Para ativá-lo, faz-se uso do comando:
a2enmod rewrite
Agora podemos recarregar o Apache para que estas alterações tenham efeito:
/etc/init.d/apache2 reload
Feito isso, vamos criar a estrutura de diretórios padrão do nosso aplicativo:
mkdir /home/aluno/livraria444mkdir /home/aluno/livraria444/web
Para que seja possível a navegação, precisamos ajustar nosso arquivo “hosts”:
echo “127.0.0.1 livraria.local”>>/etc/hosts
Para testar nosso novo “endereço” podemos acessar a URL:
http://livraria.local
Capítulo 3 Instalando o Symfony - 18
3.2. Porque é tudo tão complicado?
A instalação do Symfony é detalhada porque se destina a aplicações PROFISSIONAIS, que irão rodar em ambientes empresariais, websites de médio e grande porte.
Todos os passos garantem a segurança, portabilidade e facilidade na criação de aplicações.
Outras formas de instalação – inclusive pelo código fonte ou no Windows – podem ser encontradas no site do Symfony:
http://www.symfony-project.org
Capítulo 3 Instalando o Symfony - 19
Capítulo 4
Criando uma aplicação rápida com o
Symfony
Uma das capacidades de um framework é a possibilidade de criação de aplicações rapidamente, em especial com integração com o banco de dados.
Sistemas que interagem com banco de dados possuem uma característica interessante: a maioria das tabelas é acessada para quatro operações básicas:
• Criar um registro na tabela
• Recuperar um registro (ou vários) da tabela
• Atualizar um registro da tabela
• Excluir um registro da tabela
Estas operações são conhecidas pela sigla em inglês CRUD: Create, Retrieve, Update, Delete.
A grande maioria dos frameworks permite criar telas automaticamente para
Capítulo 4 Criando uma aplicação rápida com o Symfony - 20
utilização destas operações.
O Symfony não é diferente, e sem a necessidade de nenhuma linha de programação é possível criar um módulo para navegar em todas as tabelas de um banco de dados.
4.1. Preparando o banco de dados
No curso “PHP & MySQL Essentials (412)”, é utilizado o exemplo de uma livraria virtual. Neste curso usaremos o mesmo exemplo, mas de modo mais avançado.
Entretanto, haverá uma diferença: usaremos a integridade referencial (chaves estrangeiras) na modelagem do banco, pois o Symfony trata inteligentemente as relações entre as tabelas.
1. Criando o banco de dados
mysql -u root -p < livraria444.sql
2. Criando um usuário com permissões de acesso
grant select, insert, update, delete on livraria444.* to livraria444@localhost identified by 'senha444';
Capítulo 4 Criando uma aplicação rápida com o Symfony - 21
4.2. Criando o projeto no Symfony
Conforme o método utilizado na instalação (PEAR ou outros) a forma de criação de um projeto será levemente diferente.
Para criarmos o projeto, devemos digitar os comandos:
cd /home/aluno/livraria444symfony generate:project livraria444
Agora podemos começar a criar nossa aplicação, inicialmente do lado administrativo com CRUD para todas as tabelas.
• Todos os comandos serão executados no diretório do projero(/home/aluno/livraria444)
4.3. Criando uma aplicação
A aplicação criada receberá o nome de admin
symfony generate:app admin
• A tarefa generate:app cria uma aplicação dentro do projeto
• A opção –escaping-strategy=on proteje a aplicação contra ataques XSS( Cross-site scripting) e pode ser adicionada antes do nome da aplicação
Capítulo 4 Criando uma aplicação rápida com o Symfony - 22
• A opção –csrf-secret=SenhaQualquer proteje a aplicação contra ataques de CSRF (Cross-site request forgery) e pode ser adicionada antes do nome da aplicação
• admin é o nome da aplicação criada
Informações sobre estes tipos de ataques a sites web:
http://pt.wikipedia.org/wiki/Cross-site_scriptinghttp://en.wikipedia.org/wiki/CSRF
Já é possível acessar o projeto através do endereço:
http://livraria.local
O ambiente de desenvolvimento fica disponível em:
http://livraria.local/admin_dev.phpVeja a barra de debug que aparece no canto superior direito.
4.4. Lendo o banco de dados e criando classes de acesso
Em aplicativos MVC (Model-View-Controler: termo explicado detalhadamente mais adiante), existe um recurso chamado scaffolding, que consiste em ler o conteúdo de um banco de dados e criar classes de acesso a ele, e também criar um aplicativo de backend (retaguarda ou administrativo) contendo o acesso básico à tabela – o CRUD, já comentado.
O Symfony dispõe de recursos para realizar o scaffolding de modo muito mais simples, mas é preciso seguir os passos adequadamente. Quem realiza esse trabalho é o Doctrine, uma camada de ORM (Object-Relational Mapping: também explicado mais adiante) que é incorporada ao Symfony.
Capítulo 4 Criando uma aplicação rápida com o Symfony - 23
4.5. Configurando o Doctrine para o scaffolding
Vamos editar o arquivo databases.yml afim de configurar o acesso ao banco.
vim /var/www/livraria444/config/databases.yml
Em arquivos yml, a identação é feita através de espaço e não backspace
all: doctrine: class: sfDoctrineDatabase param:dsn: mysql:host=localhost;dbname=livraria444username: livraria444password: senha444
O Doctrine utiliza um arquivo XML para criar as classes de acesso a dados.
Para facilitar nosso trabalho, o Symfony cria este arquivo a partir do banco de dados com o comando a seguir:
symfony doctrine:build-schema
Model são classes de acesso a dados. O Symfony cria as classes para todas as tabelas no banco de dados incluindo relacionamentos através do comando:
symfony doctrine:build-model
Os Models ficam disponíveis no diretório lib/model/doctrine
Capítulo 4 Criando uma aplicação rápida com o Symfony - 24
O Symfony também cria as classes de formulários para todas as tabelas do banco de dados. Isto facilita a criação de formulários com validação, mensagens de erro, etc.
Para criar os formulários com o Symfony, usamos o comando:
symfony doctrine:build-forms
Os formulários ficam disponíveis em lib/form/doctrine
Os módulos seriam equivalentes, a grosso modo, a arquivos .php que recebem parâmetros e realizações baseadas neles.
Os módulos são criados dentro de uma aplicação, e são diretórios dentro do diretório da aplicação.
Como o Symfony trabalha com MVC, e o Model já foi criado, no módulo ficam o Controller (action) e a View (templates).
Para gerar um módulo, fazemos uso do código abaixo:
symfony doctrine:generate-module admin categoria Categoria
Onde:
• admin: é a aplicação, residente em apps/admin
• categoria: é o nome do módulo a ser criado, em apps/admin/modules/categoria
• Categoria: é o nome do Model, em lib/model/doctrine
É importante perceber que o nome do Model tem a inicial maiúscula, como foi gerado pelo Symfony.
Uma vez que o comando termine, já é possível navegar na tabela categoria dentro do Symfony através da url:
http://livraria.local/categoria
Capítulo 4 Criando uma aplicação rápida com o Symfony - 25
Também é possível navegar em modo de desenvolvimento (debug):
http://livraria.local/admin_dev.php/categoria
Pode-se criar os módulos para cada tabela do banco de dados que seja necessária
symfony doctrine:generate-module admin cliente Clientesymfony doctrine:generate-module admin livro Livrosymfony doctrine:generate-module admin pedido Pedidosymfony doctrine:generate-module admin pedidoitem Pedidoitemsymfony doctrine:generate-module admin usuario Usuario
E também podemos navegar nestas tabelas pelos respectivos links:
http://livraria.local/admin_dev.php/clientehttp://livraria.local/admin_dev.php/livrohttp://livraria.local/admin_dev.php/pedidohttp://livraria.local/admin_dev.php/pedidoitemhttp://livraria.local/admin_dev.php/usuario
4.6. Ajustando rótulo de chaves estrangeiras
Até agora não alteramos um único arquivo em PHP. Mas, para que os formulários que representam tabelas que possuem chaves estrangeiras (por exemplo, pedido que tem vínculo com a tabela cliente) é preciso especificar que campo da tabela estrangeira que será rótulo nos comboboxes.
Nas novas versões do Symfony, fazendo uso do Doctrine, o Symfony nos cria os formulários com os combox de chave estrangeira automaticamente, porém podemos nos defrontar com outras versões que não nos oferecem isso, por isso vamos ver como setar manualmente afim de ter conhecimentos em versões que não nos oferecem isso.
Capítulo 4 Criando uma aplicação rápida com o Symfony - 26
Por exemplo, para a tabela cliente temos:
lib/model/doctrine/base/BaseCliente.class.phplib/model/doctrine/Cliente.class.php
As tabelas que possuem este tipo de relacionamento são:
O primeiro arquivo não deve ser alterado, pois numa atualização do Model será sobrescrito. Toda a nossa personalização será realizada no segundo, que é uma classe extendida da primeira.
Para preencher os rótulos dos comboboxes criamos um método __toString que retorna um dos campos da tabela. Neste momento apenas criaremos mecanicamente; mas adiante entenderemos como são feitas as chamadas aos campos.
• categoria (estrangeira para livro)
• cliente (estrangeira para pedido)
• pedido (estrangeira para pedidoitem)
Então vamos alterar estes arquivos como segue:
<?php// lib/model/doctrine/Categoria.class.phpclass Categoria extends BaseCategoria{
public function __toString(){
return $this->getNome();}
}
<?php
Capítulo 4 Criando uma aplicação rápida com o Symfony - 27
// lib/model/doctrine/Cliente.class.phpclass Cliente extends BaseCliente{
public function __toString(){
return $this->getNome();}
}
<?php// lib/model/doctrine/Pedido.class.phpclass Pedido extends BasePedido{
public function __toString(){
return $this->getStatus();}
}
Apesar de não ser perfeito ainda ( o formulário para tabela livro não exibe o ISBN, mas corrigiremos isso mais adiante), nos falta apenas um menu para navegar nestas tabelas.
4.7. Criando um menu para navegação
Vamos alterar o template padrão do módulo para incluir um menu para todas as páginas do projeto.
O arquivo que possui o layout básico que é utilizado por todos os arquivos do módulo está em:
apps/admin/templates/layout.php
Capítulo 4 Criando uma aplicação rápida com o Symfony - 28
O arquivo possui o cabeçalho e rodapé da página, e inclui o conteúdo gerado pelos templates de cada módulo da aplicação. Ele é criado em HTML com os trechos em PHP incluídos, pois faz parte da View do MVC.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <?php include_http_metas() ?> <?php include_metas() ?> <?php include_title() ?> <link rel="shortcut icon" href="/favicon.ico" /> <?php include_stylesheets() ?> <?php include_javascripts() ?> </head> <body> <?php echo $sf_content ?> </body></html>
Capítulo 4 Criando uma aplicação rápida com o Symfony - 29
Para criarmos um menu modificamos seu código como segue:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <?php include_http_metas() ?> <?php include_metas() ?> <?php include_title() ?> <link rel="shortcut icon" href="/favicon.ico" /> <?php include_stylesheets() ?> <?php include_javascripts() ?> </head> <body> <div id=”menu”> <a href=”<?php echo url_for('categoria/index');?>”>Categoria</a> - <a href=”<?php echo url_for('cliente/index');?>”>Cliente</a> - <a href=”<?php echo url_for('livro/index');?>”>Livro</a> - <a href=”<?php echo url_for('pedido/index');?>”>Pedido</a> - <a href=”<?php echo url_for('pedidoitem/index');?>”>Pedido Item</a> - <a href=”<?php echo url_for('usuario/index');?>”>Usuário</a> </div> <?php echo $sf_content ?> </body></html>
A função url_for cria os links no formato do Symfony para o par módulo/ação. Mais tarde, compreendendo melhor as ações, ficará mais clara sua utilização. No momento, sabemos que ela criar de forma organizada, respeitando inclusive o ambiente (de produção ou de desenvolvimento).
Capítulo 4 Criando uma aplicação rápida com o Symfony - 30
4.8. Criando uma página inicial
Nos falta uma “index” para carregarmos automaticamente. Para não misturarmos com os módulos criados para cada tabela, criaremos um módulo sem vínculo com tabela nenhuma, chamado index.
symfony generate:module admin index
Se navegamos agora para:
Http://livraria.local/index
Veremos a página padrão do Symfony para novos módulos criados. Isto é facilmente alterado, como esta própria página explica, retirando o forward (redirecionamento) que é feito na ação padrão deste novo módulo, no arquivo:
<?php// apps/modules/index/actions/action.class.phpclass indexActions extends sfActions{
public function executeIndex(sfWeRequest $request){}
}
Qualquer conteúdo que queiramos colocar nele, além do menu que já estará como padrão da aplicação, é só alterar em seu template que se encontra em:
apps/admin/modules/index/templates/indexSuccess.php
Ele está criado vazio. Podemos alterá-lo para:
<h1>Bem vindo à Livraria do TUX</h1><h2>Utilize o menu para navegar</h2>
Capítulo 4 Criando uma aplicação rápida com o Symfony - 31
Mas para ele tornar-se padrão é preciso alterar o arquivo de configuração do módulo e direcionar para o par (modulo/ação) desde módulo criado.
apps/admin/config/routing.yml
#default ruleshomepage:
url: /param: { module: index, action: index }
default_index:url: /:moduleparam: { action: index }
default:url: /:module/:action/*
Foi alterado em homepage, param, module de default (módulo padrão do Symfony) para index (módulo recém-criado).
Neste momento descobrimos uma funcionalidade do Symfony que nos “atrapalha” neste momento: o Cache. Todos os arquivos de configuração, para maior velocidade, são verificados em sua primeira utilização e então o Symfony cria arquivos otimizados em Cache.
Isto é excelente, mas quando alteramos um arquivo de configuração ou mudamos a localização de algum arquivo de classe ele não é encontrado enquanto não zeramos o cache. Isto é feito com o comando:
symfony cc
Agora podemos acessar a página index diretamente:
http://livraria.local
Podemos também acessar o modo de desenvolvimento e debug:
http://livraria.local/admin_dev.php
Capítulo 5 Entendendo o Symfony - 32
Capítulo 5
Entendendo o Symfony
5.1. Introdução teórica
Para melhor entendimento de como o framework funciona, precisamos conhecer a fundo as “entranhas” do mesmo. Neste capítulo, iremos ver desde a organizção dos arquivos até conceitos de MVC.
5.2. Estrutura de diretórios
Ao criar um projeto, o Symfony cria a seguinte estrutura de arquivos:
livraria444->apps->cache->config->data->doc->lib->log->plugins->test->web->
Nos diretórios que vimos, podemos encontrar:
Capítulo 5 Entendendo o Symfony - 33
• apps: Cada aplicação corresponde a um diretório dentro de apps;
• cache: Cache criado pelo Symfony, para otimização de desempenho;
• config: Arquivos de configuração global do projeto;
• data: Diretório com dados que podem ser importados no banco de dados, ou outras formas de dados variáveis que serão gravados (por exemplo, um banco de dados SQLite);
• doc: Reservado para documentação do projeto;
• lib: Localização de Models, Forms e no caso do SandBox, as bibliotecas do Symfony;
• log: Registro de atividades do projeto. Útil para debug e rastreio;
• plugins: Reservado para plugins que expandem as funcionalidades do Symfony;
• test: Diretório de testes personalizados;
• web: Pasta pública que contém arquivos acessados via navegador. Especialmente aqui estão imagens, css, javascript além dos arquivos públicos do próprio Symfony;
Capítulo 5 Entendendo o Symfony - 34
5.3. O MVC do Symfony
A implementação do MVC do Symfony utiliza-se de vários arquivos, o que inicialmente pode parecer confuso:
• Camada Model:
◦ Abstração de Banco de Dados (Database abstraction)
◦ Acesso a Dados (Data access)
• Camada View:
◦ Layout
◦ Lógica de Apresentação
◦ Template
• Camada Controller:
◦ Controle Frontal (Front controller)
◦ Ação (Action)
Entretanto, na prática, com o uso dos geradores de código (em especial as opções dos comandos symfony doctrine:? ) a maioria desses arquivos já fica à disposição do desenvolvedor, seja de forma permanente, seja como base para personalizações.
Capítulo 5 Entendendo o Symfony - 35
5.4. Camada Model
Fica localizada no diretório lib/model. A abstração de Banco de Dados é realizada de forma transparente, mediante unicamente a configuração de acesso ( como visto no capítulo Criando uma aplicação rápida com o Symfony). São criados basicamente três tipos de arquivos:
• mapeamento: Utilizada internamente pelo ORM. Não é acessada diretamente pelo desenvolvedor.
• Classes das tabelas: Para instância de objetos representando registros da tabela e seus relacionamentos.
Os arquivos criados pelo Symfony através dos comandos symfony doctrine:?Serão guardados dentro de lib/model/map e lib/model/om. São classes abstratas (no caso de classes de tabelas) que servem de base para as classes extendidas que se encontram em lib/model.
Estas sim são classes disponíveis ao desenvolvedor, e onde pode-se incluir novos métodos conforme sua necessidade. Esta estrutura permite que o Symfony recrie os arquivos base sem afetar as personalizações do desenvolvedor.
5.5. Camada View
Primeiro, é preciso compreender o termo template: é um modelo de código para apresentação. No caso do Symfony, geralmente é criado em XHTML, com trechos de PHP para inclusão de informações dinâmicas.
Layouts são templates globais para cada aplicação, representando a apresentação final das páginas. Ficam armazenados no diretório apps/aplicação/templates, sendo que o padrão é a utilização do arquivo apps/aplicação/templates/layout.php
É comum (e necessário) que o layout padrão inclua o código dos templates gerados em cada ação de um módulo. Entretanto, para organização ou retulização, podem ser criados layouts parciais (partials) a serem incluídos em determinados
Capítulo 5 Entendendo o Symfony - 36
locais de layouts e templates.
No Symfony, o termo templates é referência a arquivos chamados por ações do módulo para compor o corpo da página. O layout geralmente os inclui em seu corpo. Localizam-se nos diretórios dos módulos, no formato apps/aplicação/modules/modulo/templates. Também pe possível templates parciais (partials).
A Lógica de Apresentação consiste na montagem desde “quebra cabeças” que resulta na página final exibida para o usuário. Isto envolve processos automatizados do Symfony mas também personalizações que o desenvolvedor queira fazer diretamente no código dos templates e layouts.
Os arquivos de templates e layouts são arquivos em XHTML (por padrão, mas fica a cargo da necessidade do desenvolvedor) e PHP comuns, com acesso a variáveis especiais do Symfony para geração de conteúdo de forma dinâmica.
5.6. Camada Controller
O Controle Frontal (Front controller) é quem gerencia as requisições realizadas pelo usuário final.
Encontra-se na raiz do diretório web. Podem existir vários Controles Frontais, conforme as aplicações e ambientes configurados. Veremos isto na próxima seção.
Ações (Actions) são arquivos actions.class.php residentes em cada módulo. São chamados pelo Controlador Frontal e não acessados diretamente, por isto não precisam estar disponíveis dentro do diretório web. Em Ações acontece a maior parte do desenvolvimento no Symfony.
Capítulo 5 Entendendo o Symfony - 37
5.7. Aplicações, Módulos e Ações
No exemplo do capítulo Criando uma aplicação rápida com o Symfony foi aprendido como criar aplicações e módulos no Symfony.
Agora aprofundaremos um pouco para compreender a estrutura destes elementos no Symfony.
Ao abrirmos o Diretório ADMIN, nos encontramos com a seguinte estrutura de diretórios.
• Config: Arquivos de configuração da aplicação, em formato YAML (um simplificador do formato XML);
• i18n: Arquivos de tradução para aplicativos multilínguas;
• lib: Classes exclusivas do aplicativo;
• modules: Módulos da aplicação;
• template: Layouts da aplicação (templates disponíveis para todos os módulos);
• actions: Controllers do módulo. Normalmente só é necessário um arquivo, action.class.php;
• templates: Diretório de templates do módulo;
Uma aplicação pode conter vários módulos, vinculados ou não a um Model.
Capítulo 5 Entendendo o Symfony - 38
5.8. Ambientes e Handlers
Existem dois problemas corriqueiros em aplicações web:
• Um projeto, múltiplos clientes: Num sistema web é bastante comum a necessidade de um portal administrativo ou restrito (backend) e um portal público ou para o usuário final (frontend);
• Ambiente de desenvolvimento: Para desenvolver a aplicação é importante ter acesso a ferramentas de debuf, verificar variáveis em tempo de execução, consultas realizadas no banco de dados, requisições, etc; também é comum existir um banco de dados em teste e um banco de dados em produção;
No Primeiro caso são praticamente dois sistemas diferentes (ou mais...) acessando o mesmo banco de dados com regras de requisição e resposta diferentes, apresentação diferente, mas com as mesmas regras de negócio de acesso ao banco.
No segundo caso os desenvolvedores possuem um trabalho muito grande de ligar e desligar debugs, ter cópia de ambientes diferentes, com acesso a banco de dados diferentes, manter sincronizados estes dois ambientes, ou então, mesmo que somente em sua máquina de desenvolvimento, há muita mão-de-obra para testar a visão do usuário final.
O Symfony, para facilitar a vida do desenvolvedor, permite a criação de vários ambientes dentro de um projeto, e para isto, utiliza os handlers. Handlers são arquivos em PHP que fazem o primeiro trabalho da Camada Controller, que é definir qual aplicação, módulo e ação serão executados.
Capítulo 5 Entendendo o Symfony - 39
5.9. Configurando Ambientes
O Symfony, para não dificultar as configurações utilizando o XML, ou retroagir utilizando o ultrapassado formato INI, ou ainda inventar um novo formato, optou por utilizar uma linguagem que é um “XML Simplificado”, chamado de YAML (YAML Ain't Markup Language – http://www.yaml.org/).
O YAML é muito simples de se escrever. Tem poucas regras, das quais resume-se abaixo as mais importantes:
• A hierarquia é mantida pela identação;
• A identação é com dois ESPAÇOS;
• Não se utliza TABULAÇÃO como identação;
• Cada chave é seguida de dois pontos;
• O valor depois dos dois pontos é atribuído à chave;
• É preciso um espaço entre os dois pontos e o valor;
• Texto com espaço é definido entre aspas SIMPLES;
• O que ficar após um “#” é um comentário na linha;
Para configurar ambientes no Symfony, utilizamos inicialmente o arquivo:
config/databases.yml
all: doctrine: class: sfDoctrineDatabase param: dsn: mysql:host=localhost;dbname=livraria444username: livraria444password: senha444
Capítulo 5 Entendendo o Symfony - 40
Então temos os ambientes dev, test, além do prod, que é padrão e está omitido por não possuir personalizações na configuração inicial. Além deles temos o all que determina valores padrão para todos os ambientes (que podem ser sobrescritos individualmente).
• prod: Ambiente de produção, com cache ativado e com apenas alertas de erros(sem debug);
• dev: Ambiente de desenvolvimento, com cache desativado, erros em nível máximo(com debug);
• test: Ambiente especial semelhante ao de produção, mas que só executado em linha de comando para rodar testes automatizados;
• ??: É possível configurar outros ambientes personalizados.
É Muito comum o ambiente de desenvolvimento utilizar outro banco de dados; ou então, um ambiente intermediário, com uma cópia do banco de produção criado para testes, mas com debug.
Quando uma nova aplicação é criada, por padrão são criados dois handlers:
• web/minhaaplicacao.php: corresponde ao ambiente de produção;
• web/minhaaplicacao_dev.php; corresponde ao ambiente de desenvolvimento;
Se a aplicação é a primeira a ser criada, seu ambiente de produção terá como handler web/index.php, e torna-se o aplicativo padrão do projeto.
Uma ferramenta importante do Symfony é a barra de Debug, uma barra flutuante que aparece em todas as páginas, fornecendo informações valiosas sobre varáveis e consultas a banco de dados.
Capítulo 5 Entendendo o Symfony - 41
Essa barra de debug é ativada dentro do handler:
<?phpif(!in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1','::1'))){ die('You are not allowed to access this file. Check '. basename(__FILE__).' for more information.');}require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');$configuration = ProjectConfiguration::getApplicationConfiguration ('admin', 'dev', true);sfContext:createInstance($configuration)->dispatch();?>
A atenção especial é dada aos parâmetros do método estático:
ProjectConfiguration::getApplicationConfiguration('admin', 'dev', true);
Ele recebe três parâmetros:
• aplicação: no exemplo 'admin';
• ambiente: no exemplo 'dev'. Podendo ainda ser 'prod' (oque é o caso do ambiente de produção) ou outro ambiente que tennha sido configurado no arquivo config/databases.yml
• webdebug: no exemplo true, ou seja, ativo(no ambiente de produção, false);
Capítulo 5 Entendendo o Symfony - 42
5.10. A URL no Symfony
Na URL de uma aplicação Symfony, até os três primeiros parâmetros são determinadas a aplicação, o módulo e a ação a serem executadas.
1. Situação 1 – Homepage
http://livraria.local
Se não há parâmetros após o domínio, então subentende-se que a aplicação e o ambiente são os definidos no arquivo:
web/index.php
(…)// APLICAÇÃO = admin, AMBIENTE = prod, DEBUG = false$configuration = ProjectConfiguration::getApplicationConfiguration ('admin', 'prod', 'false');(…)
Já o módulo e a ação são os definidos no arquivo:
apps/minhaaplicacao/config/routing.yml
dentro da opção homepage, param
(…)homepage: url: / param: {module: default, action: index}(…)
Capítulo 5 Entendendo o Symfony - 43
2. Situação 2 – Um handler diferentes
http://livraria.local/admin_dev.php
Se o primeiro parâmetro é um handler, então é executado este handler, que estará no diretório web, que define a aplicação. Já o módulo e a ação seguem a mesma situação
acima, sendo definidos no arquivo:
apps/minhaaplicacao/config/routing.yml
3. Situação 3 – módulo
http://livraria.local/categoriahttp://livraria.local/admin_dev.php/categoria
Se o primeiro parâmetro não é um handler, ou então é um handler seguido de uma barra mais uma palavra, então esta última informação é um módulo (no exemplo acima, categoria).
A ação é definida no arquivo:
apps/minhaaplicacao/config/routing.yml
dentro da opção default_index, param (valor padrão é index).
(…)default_index: url: /:module param: {action: index}(…)
4. Situação 4 – módulo/ação
http://livraria.local/categoria/newhttp://livraria.local/admin_dev.php/categoria/new
Capítulo 5 Entendendo o Symfony - 44
Se o primeiro parâmetro não é um handler, ou então é um handler seguido de uma barra mais uma palavra e de outra barra e palavra, então esta última informação é uma ação (no exemplo acima, new) e a penúltima é um módulo (no exemplo acima, categoria).
5. Situação 5 – módulo/ação/variavel/valor
http://livraria.local/categoria/edit/cat_id/1http://livraria.local/admin_dev.php/categoria/edit/cat_id/1
Se já estão identificados aplicação, módulo e ação, então o que segue são sempre pares de variável/valor, que serão enviados via GET.
No exemplo acima, a ação edit do módulo categoria receberá a variável cat_id com o valor 1.
Seria o equivalente a:
http://livraria.local/categoria/edit?cat_id=1http://livraria.local/admin_dev.php/categoria/edit?cat_id=1
no PHP natural, o que equivaleria no script de destino a variável:
$_GET['cat_id'] = 1
apesar de que utiliza-se um método próprio no Symfony para recuperar esta variável, como será visto mais adiante.
Se houverem mais pares de variável/valor, seguirão as mesmas regras:
http://livraria.local/categoria/edit/cat_id/1/descricao/Romancehttp://livraria.local/admin_dev.phpcategoria/edit/cat_id/1/descricao/Romance
equivale às variáveis no destino:
$_GET['cat_id'] = 1$_GET['descricao'] = 'Romance'
Capítulo 6 Usando o EasyEclipse - 45
Capítulo 6
Usando o EasyEclipse
6.1. Introdução teórica
O EasyEclipse (http://eclipse.org) já foi instalado com o plugin PHPEclipse. Vamos aprender a utilizá-lo para gerenciar um projeto em PHP e compreender algumas de suas facilidades.
6.2. Iniciando um projeto
Apesar do EasyEclipse ter sido instalado e possuir uma opção de menu para iniciá-lo, temos um problema: o usuário da máquina não possui acesso para salvar o diretório /var/www/livraria444, pois foi criado pelo usuário root.
Portanto, para facilitar a utilização no desenvolvimento, é prático executar o EasyEclipse como superusuário. No Gnome é possível fazer iso pressionando ALT+F2 e digitando:
gksu /opt/easyeclipse-php-1.2.2.2/eclipse
Em seguida é solicitada a senha do superusuário, e então o EasyEclipse solicita que se defina a área de trabalho (workspace) padrão, que é um diretório dentro do diretório do usuárop onde, por padrão o EasyEclipse sugerirá que residam os projetos criados(mas não é obrigatório).
Capítulo 6 Usando o EasyEclipse - 46
6.3. Criando um projeto
Acione o menu File / New / Project / PHP / PHP Project.
Opcionalmente pode-se criar um novo projeto na área do Navegador, clicando com o botão direito do mouse e selecionando File / New / Project / PHP / PHP Project.
É solicitado em seguida o caminho um nome e um caminho até o projeto. Este pode ser vazio(novo), ou um já existente. Neste último caso, o EasyEclipse varre o diretório e “importa” todos os arquivos para o projeto.
Importante: o EasyEclipse mantém controle sobre as alterações dos arquivos – nãoé simplesmente uma navegação de um diretório. Se algum arquivo for criado no diretório por fora do EasyEclipse, ele não vai “enxergar”. Do mesmo modo, se algum arquivo for alterado por fora do EasyEclipse, ele vai acusar esta “inconsistências”;
No caso do Symfony, isto é bastante comum de acontecer com o uso das ferramentas de linha de comando que refazem os Models, por exemplo. Nestas situações, basta solicitar ao EasyEclipse que releia o diretório do projeto: clica-se com o botão direito em cima do nome do projeto e então em “Refresh” (Atualizar).
6.4. Visão Geral da ferramenta
O EasyEclippse tem como base o PHPEclipse, que é um plugin para desenvolvimento em PHP.
Ele, além do realce de sintaxe, trás outras facilidades como o auto-completar os comandos, sugestão de métodos de classes (muitíssimo útil no Symfony, que é 100% orientado a objeto), e incorpora recursos globais do Eclipse como navegação de classes, erros e tarefas(TODO).
Para usuários Windows é possível integrar com o XAMPP(ambiente de desenvolvimento apache/MySQL/PHP para Windows) através do menu PHP/Apache.
Como projetos em PHP envolvem invariavelmente em algum momento manipulação de arquivos XHTML/HTML, JavaScript, CSS e SQL, outros plugins incorporados abrem estes arquivos e apresentam facilidades para estas linguagens.
Capítulo 6 Usando o EasyEclipse - 47
De moro geral, alguns atalhos de teclado são úteis em todos os editores:
• CTRL+espaço: sugere complementação de comandos ou parâmetros;
• TAB: identa o código – válido para várias linhas selecionadas;
• SHIFT+TAB: “des”identa o código – válido para várias linhas selecionadas;
• CTRL+PGUP, CTRL+PGDN: navega pelas abas do editor(arquivos abertos);
A lista completa de atalhos de teclado está disponível no menu Help/ Key Assist...
O EasyEclipse guarda a última janela encerrada; ou seja, quando se abrir novamente o EasyEclipse ele vai abrir exatamente como foi encerrado.
Uma janela muito útil do EasyEclipse é o Outline, que permite a navegação dentro de um arquivo – por exemplo, numa classe são listados os métodos; num arquivo CSS, cada uma das entradas, etc.
6.5. Configurações Úteis
Um padrão importante nos projetos atuais é a quantidade de caracteres que são utilizados para identação. De modo geral o caracter TAB não é ,aos utilizado (para evitar diferentes interpretações por editores ou visualizadores), sendo utilizado o espaço (o padrão mais comum são 2 espaços). Entretanto, para facilitar, é possível automaticamente converter o pressionamento do TAB por espaços ( ou o contrário com o SHIFT+TAB).
Capítulo 7 Camada Controller: controlando as requisições e respostas - 48
Capítulo 7
Camada Controller: controlando as
requisições e respostas
Pelo padrão MVC o Controller é oque faz a interconexão entre a lógica de negócio (Model) e a apresentação (View).
Basicamente o Symfony separa o Controller nos seguintes componentes:
• Front Controller (Controle frontal): ponto de entrada único da aplicação; carrega a configuração e determinação de qual ação será executada;
• Actions(Ações): lógica da aplicação, verifica a integridade da requisição e prepara os dados necessários para a camada de apresentação;
• Objetos Request, Response, Session: dão acesso aos parâmetros requisitados, cabeçalhos de resposta e aos dados persistentes.
Capítulo 7 Camada Controller: controlando as requisições e respostas - 49
7.1. Ações (Actions)
O Front Controller determina qual o módulo e ação correspondente será executado através da leitura da URL – como já foi explicado anteriormente. Para que seja controlada a alçao correspondente, ela deve existir dentro da classe.
class pessoaActions extends sfActions{ public function executeIndex(sfWebRequest $request) { $this->pessoas = Doctrine::getTable('Pessoa') ->createQuery('a') ->execute(); }
public function executeNew(sfWebRequest $request) { $this->form = new PessoaForm(); }
public function executeCreate(sfWebRequest $request) { $this->forward404Unless($request->isMethod(sfRequest::POST));
$this->form = new PessoaForm();
$this->processForm($request, $this->form);
$this->setTemplate('new'); }
public function executeEdit(sfWebRequest $request) { $this->forward404Unless($pessoa = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id'))), sprintf('Object pessoa does not
Capítulo 7 Camada Controller: controlando as requisições e respostas - 50
exist (%s).', $request->getParameter('id'))); $this->form = new PessoaForm($pessoa); }
public function executeUpdate(sfWebRequest $request) { $this->forward404Unless($request->isMethod(sfRequest::POST) || $request->isMethod(sfRequest::PUT)); $this->forward404Unless($pessoa = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id'))), sprintf('Object pessoa does not exist (%s).', $request->getParameter('id'))); $this->form = new PessoaForm($pessoa);
$this->processForm($request, $this->form);
$this->setTemplate('edit'); }
public function executeDelete(sfWebRequest $request) { $request->checkCSRFProtection();
$this->forward404Unless($pessoa = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id'))), sprintf('Object pessoa does not exist (%s).', $request->getParameter('id'))); $pessoa->delete();
$this->redirect('pessoa/index'); }
protected function processForm(sfWebRequest $request, sfForm $form) { $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName())); if ($form->isValid())
Capítulo 7 Camada Controller: controlando as requisições e respostas - 51
{ $pessoa = $form->save();
$this->redirect('pessoa/edit?id='.$pessoa->getId()); } }}
As ações devem manter o seguinte padrão de nomenclatura para o método:
• Iniciar com a palavra execute toda em minúscula;
• Continuar com o nome da ação, com a primeira letra em maiúscula;
Métodos que não seguem este padrão não são ações, não podem ser acessadas pelo Front Controller, apesar de ser possível seu uso internamente pela classe, conforme a necessidade.
Capítulo 7 Camada Controller: controlando as requisições e respostas - 52
7.2. Término da ação
As ações ao seu término, por padrão, chamam um template de mesmo nome e sufixo Success.
Portanto, a ação executeIndex chamará o template indexSucess.php, a ação executeList chamará o template listSuccess.php, e assim por diante.
Na realidade existe um return implícito ao final de cada ação chamando a View padrão, que é:
// Ambas ações chamam a View Success correspondente a cada açãopublic function executeIndex(sfWebRequest $request) { $this->pessoas = Doctrine::getTable('Pessoa') ->createQuery('a') ->execute(); }
Para chamar uma View personalizada, a ação deve terminar assim:
// O Symfony va procurar o template actionNameMeuTemplate.phppublic function executeIndex(){
return 'MeuTemplate';}
Mas é possível não chamar nenhum template. Este é o caso, por exemplo, para funções que serão executadas em modo “batch”, ou em agendamentos no cron, ou seja, não terão saída.
// O Symfony não vai executar nenhum templatepublic function executeAgendamento(){ return sfView::NONE;}
Capítulo 7 Camada Controller: controlando as requisições e respostas - 53
7.3. Redirecionamento
O Symfony permite dois tipos de redirecionamento:
• Para outra ação: é algo interno, e não interfere na URL exibida para o usuário, que nem percebe que isto aconteceu;
public function executeList(){ $this->forward('meumodulo','index');}
• Para outra URL (Redirect)
public function executeList(){ $this->redirect('meumodulo/index'); $this->redirect('http://www.terra.com.br');
O forward e o redirect functionam como o return, ou seja, o código após eles nunca será executado.
Existe um tipo de redirecionamento bastante comum, que é o da “página não encontrada” (erro 404 do Apache). Seu uso é exemplificado a seguir:
$this->forward404Unless($pessoa = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id'))), sprintf('Object pessoa does not exist (%s).', $request->getParameter('id'))); $this->form = new PessoaForm($pessoa);
A página de erro pode ser personalizada. Basta criar um template em um módulo, uma ação e alterar a configuração no arquivo config/settings.yml da aplicação:
Capítulo 7 Camada Controller: controlando as requisições e respostas - 54
all:.actions: error_404_module: default error_404_actions: error404
Entretanto é comum necessitarmos realizar redirecionamento após um teste lógico. Para isto o Symfony dispõe de mais alguns métodos:
• forwardIf()
• forwardUnless()
• forward404If()
• forward404Unless()
• redirectIf()
• redirectUnless()
$this->forward404Unless($pessoa = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id'))), sprintf('Object pessoa does not exist (%s).', $request->getParameter('id'))); $this->form = new PessoaForm($pessoa);
Quando o cliente envia uma requisição, uma série de informações são recebidas pelo servidor Web, como dados sobre navegador, URL, GET e POST. O Symfony dispõe de todas estas informações ao Controller através do objeto sfWebRequest
Informações da Requisição-------------------------------------------------------------------------------------------------------------------Nome Função=>Exemplo-------------------------------------------------------------------------------------------------------------------isMethod($method) É POST ou GET=> true or false
Capítulo 7 Camada Controller: controlando as requisições e respostas - 55
getMethod()Nome do método de requisição=> 'POST'
getHttpHeader('Server') HTTP header=> 'Apache/2.0.59 (Unix) DAV/2PHP/5.1.6'
getCookie('teste') Valor do nome do cookie=> 'testando'
isXmlHttpRequest() É uma requisição Ajax?=> true
isSecure() É uma requisição SSL?=> true
hasParameter('teste')Parâmetro presente na requisição=> true
getParameter('teste') Valor de um parâmetro=> 'testando'
getParameterHolder()->getAll() Array com todos os parâmetros de requisição-------------------------------------------------------------------------------------------------------------------Informações da URI-------------------------------------------------------------------------------------------------------------------
getUri() URI completa=> 'http://localhost/mymodule/myaction'
getPathInfo()Informação do caminho=> 'mymodule/myaction'
getReferer() Referer
Capítulo 7 Camada Controller: controlando as requisições e respostas - 56
=> 'http://localhost/'
getHost()Nome do Host=> 'localhost'
getScriptName()Nome do Front Controller=> 'index.php'
-------------------------------------------------------------------------------------------------------------------Informação do navegador do cliente-------------------------------------------------------------------------------------------------------------------getLanguages() Array com as línguas disponíveis=> Array( [0] => en_US [1] => en )
getCharsets() Array com os charsets disponíveis=> Array( [0] => UTF-8 [1] => ISO-8859-1 )
getAcceptableContentTypes() Array com os content types disponíveis=> Array( [0] => text/xml [1] => text/html )
O primeiro parâmetro recebido pela ação é o objeto sfWebRequest, então, comumente é recebido como uma variável:
$this->pessoas = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id'))) ->createQuery('a') ->execute();
Capítulo 7 Camada Controller: controlando as requisições e respostas - 57
7.4. ilizando sessões de usuário
O Symfony guarda as informações de sessão no objeto sfUser. Ele pode ser recuperado na ação através do método getUser. Apesar de seu nome, ele está disponível mesmo que não haja controle de usuários
// Armazenando valor na sessãopublic function executaEntrada(){ $this->getUser()->setAttribute('fone','3333.3333');}// Recuperando valor da sessão// O método permite um “valor padrão”, caso não exista// a variável na sessãopublic function executeSaida(){ $this->getUser->getAttribute('fone','não informado');}// Removendo uma variável da sessãopublic function executeRemovendo(){ $this->getUser->getAttributeHolder()->remove('fone');}// Limpando todas as variáveis de sessãopublic function executeLimpando(){ $this->getUser()->getAttributeHolder()->clear();}
7.5. Atributos Flash – variáveis de sessão descartáveis
Muitas vezes é necessário passar um valor de script para o outro dentro de um mesmo processamento. No PHP comum, isto geralmente força que um redirecionamento utilizando header('Location') necessite ser montado com passagem de parâmetros. Isto é muito comum para passar códigos de mensagens de erro, ou confirmação de gravação de registro, por exemplo.
Isto seria mais elegantemente resolvido com uma variável de sessão, mas depois ela seria inútil.
Capítulo 7 Camada Controller: controlando as requisições e respostas - 58
Para resolver este tipo de situação, o Symfony possui uma “variável de sessão descartável”, chamada flash.
O importante é lembrar: o valor do atributo flash só está disponível dentro de uma mesma requisição.
// Definindo o valor numa ação$this->getUser()->setFlash('mensagem','Registro Salvo');// (…)// Recuperando o valor em outra ação$mensagem = $this->getUser()->getFlash('mensagem');
Também pode ser útil recuperar o atributo flash num template. Para isto, é só utilizar o objeto $sf_user:
<?php if($sf_user->hasFlash('mensagem')): ?> <?php echo $sf_user->getFlash('mensagem') ?><?php endif; ?>
7.6. Repassando variáveis para a View
Uma funcionalidade muito importante é que a ação realiza a lógica da aplicação mas necessita repassar informações para a camada View. A forma que o Symfony faz isto é criando variáveis na ação que estarão disponíveis na View.
Se na ação temos o seguinte código:
public function executeShow(){ // Criando um atributo na ação // que ficará disponível na View$this->pessoa = Doctrine::getTable('Pessoa')->find(array($request->getParameter('id')));}
No layout ou template teremos a variável $pessoa, no caso um objeto Pessoa
<?php echo $pessoa->getNome() ?>
Capítulo 7 Camada Controller: controlando as requisições e respostas - 59
Capítulo 8
Camada Model: acesso ao banco de
dados
8.1. Introdução teórica
Bancos de dados são Relacionais. O PHP 5 e o Symfony são Orientado a Objetos. Para que se possa ser mais efetivo no uso da orientação a objeto.
É neste contexto que se encaixa a ORM. Toda lógica de acesso a dados é encapsulada pela ORM, mantendo todo o acesso a dados e suas regras de negócios num único local. O grande benefício é o reuso, pois é possível reutilizar um método de acesso que qualquer local do aplicativo, ou até de outro aplicativo. Além disso, uma regra de negócio ou uma chamada comum fica em um único lugar, facilitando o acesso e a manutenção.
Outra facilidade: como os registros são objetos, incluir novas funcionalidades não significa criar obrigatóriamente campos em uma tabela. Diferentes formas de visualizar um campo ou um conjunto de campos independe de qual banco de dados se está acessando.
Capítulo 8 Camada Model: acesso ao banco de dados - 60
Exemplo:
public function getPrimeiroNome()public function getSobrenome()public function getNomeCompleto()
O método getNomeCompleto utiliza os dois métodos anteriores para apresentar o nome completo de um cliente, por exemplo. Independente de mudanças nos nomes dos campos ou até em qual banco (MySQL, Postgres, Oracle, etc) será acessado, este método estará sempre disponível e consistente.
Informações mais complexas como o total de uma nota fiscal também podem ser criadas desta forma – e acessando outras tabelas através de suas classes respectivas.
Outra imensa vantagem é não preocupar-se com as variações nas sintaxes SQL entre diferentes bancos de dados.
Padrões de nomes de campos, tabelas e chaves estrangeiras, não são obrigatórios, mas facilita na posterior utilização dos métodos das classes.
• Nomes de tabelas sem hifens ou underscore (sublinhado vazio);
• Nomes de tabelas em minúsculas;
• Nomes de campo em minúsculas, com underscore (sublinhado vazio) separando campos grandes;
O Doctrine – ORM principal do Symfony em sua versão 1.4.4 – converte campos como nome_funcionario para um método como getNomeFuncionario, oque é bastante claro e legível.
Os nomes de tabela são convertids de cliente para classe Cliente.
É muito importante definir chaves estrangeiras, pois o Doctrine vasculha os relacionamentos e cria os devidos métodos de acesso.
Capítulo 8 Camada Model: acesso ao banco de dados - 61
O Doctrine ORM baseia a construção dos modelos num arquivo schema.yml que possui uma estrutura como no exemplo a seguir:
Pessoa: connection: doctrine tableName: pessoa columns: id:type: integer(4)fixed: falseunsigned: falseprimary: trueautoincrement: true nome:type: string()fixed: falseunsigned: falseprimary: falsenotnull: trueautoincrement: false sobre_nome:type: string()fixed: falseunsigned: falseprimary: falsenotnull: trueautoincrement: false email:type: string(45)fixed: falseunsigned: falseprimary: falsenotnull: trueautoincrement: false
Capítulo 8 Camada Model: acesso ao banco de dados - 62
Neste curso não abordaremos esta forma de criação de Models, mas com a varredura do banco para criação deste esquema (como visto no capítulo Criando uma aplicação rápida com o Symfony).
Também já foi citado que na criação dos Models há dois tipos de arquivos: mapeamento, classes das tabelas. Vamos detalhar um pouco os últimos dois, que são os utilizados na prática.
8.2. Classes de Tabelas
As classes de tabelas representam um registro na tabela. O Doctrine cria dois tipos: a classe Base e a Custom (personalizada). A classe Base é abstrata, não pode ser acessada diretamente, e é criada automaticamente pelo Doctrine. A classe Custom é criada pelo Doctrine, mas nunca é sobreescrita; ou seja, se houver alterações posteriores, somente serão realizadas na Base.Utiliza-se unicamente a Custom. Como herança da Base temos os getters(para recuperar valor dos campos) e setters (para alterar valor dos campos), métodos para salvar e excluir, entre outros.
8.3. Acessando dados através do Model
Considerando o exemplo da classe Pessoa, veremos algumas operações em seu arquivo “actions.class.php”.
8.4. Utilizando critérios no Doctrine
Para abstrair qualquer banco de dados, o Doctrine utiliza uma sintaxe própria de filtragem. A seguir tem-se um resumo das estruturas mais utilizadas.
Sintaxe Básica:-------------------------------------------------------------------------------------------------------------------Where column = value=> where(“column = value”);
Capítulo 8 Camada Model: acesso ao banco de dados - 63
WHERE column <> value=> where(“colum <> value”);
-------------------------------------------------------------------------------------------------------------------DQL-------------------------------------------------------------------------------------------------------------------http://www.doctrine-project.org/documentation/manual/1_2/en/dql-doctrine-query-language -------------------------------------------------------------------------------------------------------------------
Outras Sintaxes-------------------------------------------------------------------------------------------------------------------
ORDER BY column->addAscendingOrderByColumn(column);
LIMIT limit->limit(limit);
8.5. Métodos personalizados
8.5.1. Método especial __toStringJá vimos que a classe tabela é uma classe “Custom”, ou seja, pode ser
personalizada. Entretanto existe um método especial que pode ser adicionado e que é muito útil para tabelas que são “pai” em relacionamentos, ou seja, são tabelas estrangeiras: o método __toString
Este método é chamado automaticamente pelo Symfony quando da montagem de comboboxes em formulários para preencher a legenda. Deste modo, é possível determinar como será a legenda.
<?php
Capítulo 8 Camada Model: acesso ao banco de dados - 64
// lib/model/Pessoas.phpclass Pessoa extends BasePessoa{ public function __toString(){ return $this->getCodigo().'-'.$this->getNome(); }}
foi determinado como legenda a concatenação entre Código e Nome da Pessoa. Poderia ser qualquer outro formato necessário.
8.6. Sobrescrita de métodos
Muitas vezes é necessário um tratamento antes de alterar ou recuperar um campo, por exemplo. É possível alterar o comportamento padrão simplesmente sobrescrevendo o métodona classe extendida, como no exemplo:
<?php// lib/model/Pessoa.phpclass Pessoa extends BasePessoa{ /*** Set the value of [nome] column.* * @paramstring $v new value* @return Cliente The current object */ public function setNome($v) { if ($v !== null) {$v = (string) $v; }
Capítulo 8 Camada Model: acesso ao banco de dados - 65
if ($this->nome !== $v) {// Colocando todas as letras em maiúsculas$this->nome = strtoupper($v); }
return $this; } // setNome()
}?>
Uma pequena alteração: o nome será convertido todo para maiúsculas antes de ser gravado.
Capítulo 9
Camada View: Apresentação
9.1. Introdução teórica
Já vimos anteriormente que a Camada View é composta de Layouts e Templates. Como esta camada é responsável pelo resultado final de nosso trabalho
Capítulo 9 Camada View: Apresentação - 66
de programação, é importante compreendermos onde vamos colocar nosso código.
9.2. A montagem da View
De modo geral, a montagem da View é feita com a leitura de um layout (a nível de aplicação) e um template (a nível de módulo/ação).
Já vimos que o padrão de layout da aplicação é:
apps/aplicacao/templates/layout.php
enquanto os templates residem no diretório templates de cada módulo. No capítulo sobre Controllers vimos que cada ação, ao seu final, chama um template.Ainda temos a situação de utilizar trechos de templates, que são os partials.
Vamos conhecer o layout padrão do Symfony
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <?php include_http_metas() ?> <?php include_metas() ?> <?php include_title() ?> <link REL="shortcut icon" HREF="/favicon.ico" /> </head> <body> <?php echo $sf_content ?> </body></html></comandoNumerado>
e considerando um template como segue:
<h1> Olá, Mundo </h1>
Teremos a seguinte página gerada:
Capítulo 9 Camada View: Apresentação - 67
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8" /> <meta name="title" CONTENT="symfony project" /> <meta name="robots" CONTENT="index, follow" /> <meta name="description" CONTENT="symfony project" /> <meta name="keywords" CONTENT="symfony, project" /> <title>symfony project</title> <link REL="stylesheet" TYPE="text/css" HREF="/css/main.css" /> <link REL="shortcut icon" HREF="/favicon.ico"> </head> <body> <h1>Olá, Mundo</h1> </body></html>
Então percebe-se que o trecho de código:
<?php echo $sf_content ?>
inclui o template. Conforme a distribuição do layout da página (com cabeçalho, menu, rodapé, etc), então posicionamos este código adequadamente.
Existem também três funções especiais (include_http_metas(), include_metas(), include_title()). Elas geram os headers do XHTML/HMTL baseados na configuração da View na aplicação e no módulo requisitado.
Para a aplicação, encontramos esta configuração em:
apps/aplicacao/config/view.yml
default:
Capítulo 9 Camada View: Apresentação - 68
http_metas: content-type: text/html
metas: #title: symfony project #description: symfony project #keywords: symfony, project #language: en #robots: index, follow
stylesheets: [main.css]
javascripts: []
has_layout: on layout:layout
Para o módulo, não há um arquivo criado. Se criado, ele vai ser lido em cascata, ou seja, o que estiver na configuração é lido primeiro, e depois ele é lido e sobrescreve as informações.
apps/aplicacao/modulo/config/view.yml
editSuccess: metas: title: Editando Pessoa
listSuccess: metas: title: Listagem de Pessoas
printSuccess: metas: title: Imprimir registro
Capítulo 9 Camada View: Apresentação - 69
stylesheets: [print.css]
all: metas: title: Cadastro de Pessoas
No exemplo acima, a palavra chave all representa um padrão para o módulo, e cada template referenciado possui informações personalizadas. Isto é uma forma muito simples de incluir arquivos javascript onde são necessários, por exemplo.
9.3. Partials (Parciais)
Partials, como já foi visto anteriormente, são trechos de template que podem ser incluídos em determinados pontos do layout ou do template. Eles iniciam com um underscore ( _ ) e residem ou no diretório de layouts da aplicação (globais) ou no diretório de templates de cada módulo.
Para incluir partials, utiliza-se um dos três códigos abaixo, conforme a necessidade:
// Estando no mesmo módulo, é possível incluir o partial// apps/aplicacao/modules/meumodulo/templates/_parcial.php// sem citar o módulo<?php include_partial('parcial') ?> // Se quiser incluir em qualquer outro lugar fora do módulo// então é preciso informar o módulo<?php include_partial('meumodulo/parcial') ?>
// Um partial global como// apps/aplicacao/templates/_parcialGlobal.php// é incluído assim<?php include_partial('global/parcialGlobal') ?>
Capítulo 9 Camada View: Apresentação - 70
Entretanto, os partials não tem acesso às variáveis disponíveis.
9.4. Variáveis do Symfony
O Symfony dispõe algumas variáveis para que a View possa ser montada com informações provenientes de vários lugares. Já foi visto no capítulo sobre Controllers como as ações passam variáveis para a View, mas existem outras informações que estão disponíveis nas ações que também estão disponíveis na View.
• $sf_context: sfContext
• $sf_request: sfRequest
• $sf_params: Parâmetros do objeto Request
• $sf_use: sfUser
Os usos são semelhantes ao do Controller.
9.5. Helpers
Helpers são funções em PHP que retornam algum tipo de trecho em HTML. O Symfony possui vários pré-definidos e é possível também criar Helpers personalizados.
Existem vários grupos de Helpers, e nem todos estão ativos por padrão. Para carregar um que não esteja ativo, utiliza-se:
<?php use_helper('HelperName') ?><?php use_helper('HelperName1', 'HelperName2', 'HelperName3') ?>
Abaixo uma lista dos mais utilizados.
// Grupo Tag// =========<?php echo tag('input', array('name' => 'endereco', 'type' => 'text')) ?>
Capítulo 9 Camada View: Apresentação - 71
// ou<?php echo tag('input', 'name=endereco type=text') ?> => <input name="endereco" TYPE="text" /><?php echo content_tag('textarea', 'Texto livre', 'name=texto') ?> => <textarea name="texto">Texto livre</textarea>
// Grupo Url// =========// Link para a ação de criação de novo registro no módulo pessoa// Importante: o link será gerado considerando o roteamento,// o front controller atual, etc.<?php echo link_to('Nova Pessoa', 'pessoa/new') ?>=> <a HREF="/pessoa/new">Nova Pessoa</a>
// Grupo Asset// ===========
// Tag IMG <?php echo image_tag('logo.png', 'alt=Logotipo size=200x100') ?> => <img SRC="/images/logo.png" ALT="Logotipo" WIDTH="200" height="100"/>
// Incluindo Javascript<?php echo javascript_include_tag('atualizar') ?> => <script language="JavaScript" TYPE="text/javascript" src="/js/atualizar.js"></script>
// Incluindo CSS<?php echo stylesheet_tag('formatacao') ?> => <link HREF="/stylesheets/formatacao.css" MEDIA="screen" rel="stylesheet"type="text/css" />
Capítulo 9 Camada View: Apresentação - 72
Capítulo 10 Forms: criando formulários - 73
Capítulo 10
Forms: criando formulários
10.1. Introdução teórica
Uma necessidade especial no trabalho com o aplicativos Web é a criação e manipulação de formulários. Isto envolve algumas tarefas recorrentes:
• Montar o formulário: através de tags XHTML/HTML, é preciso criar uma a uma cada tag de cada campo;
• Tratar o envio do formulário: definir na tag form como o formulário será enviado;
• Validar os campos do formulário: ao receber os valores, decidir se são ou não válidos;
• Exibir erros de validação: se algum campo não for validado, retornar erro;
• Armazenar os dados: se estiverem vinculados a alguma tabela de banco de dados;
• Retornar informação sobre gravação de registro: alguma
Capítulo 10 Forms: criando formulários - 74
mensagem informativa, confirmando ou não a gravação.
Estas tarefas se tornam ainda mais tediosas e de complexa manutenção conforme aumenta o número de tabelas e campos nestas tabelas.
O Symfony propõe uma abordagem muito eficiente para centralizar tanto a criação como a validação dos formulários. Veremos agora como utilizar de forma básica seus recursos.
10.2. Gerando formulários automaticamente
A maioria dos formulários num sistema são baseados em tabelas de um banco de dados. Para facilitar nosso estudo, começaremos com a visualização de um formulário criado no capítulo Criando uma aplicação rápida com o Symfony. Lembrando que ele foi criado com o uso de uma tarefa automatizada do Doctrine:
symfony doctrine:build-forms
Agora vamos conhecer o arquivo:
lib/form/doctrine/base/BaseLivroForm.class.php
<?phpclass BaseLivroForm extends BaseFormDoctrine{ public function setup() { $this->setWidgets(array('isbn' => new sfWidgetFormInputHidden(),'autor'=> new sfWidgetFormInput(),'titulo' => new sfWidgetFormInput(),'cat_id' => new sfWidgetFormDoctrineChoice(array('model' => 'Categoria', 'add_empty' => true)),'preco'=> new sfWidgetFormInput(),
Capítulo 10 Forms: criando formulários - 75
'sumario' => new sfWidgetFormInput(), ));
$this->setValidators(array('isbn' => new sfValidatorDoctrineChoice( array('model' => 'Livro', 'column' => 'isbn', 'required' => false)),'autor'=> new sfValidatorString( array('max_length' => 80, 'required' => false)),'titulo' => new sfValidatorString( array('max_length' => 100, 'required' => false)),'cat_id' => new sfValidatorDoctrineChoice( array('model' => 'Categoria', 'column' => 'cat_id', 'required' => false)),'preco'=> new sfValidatorNumber(),'sumario' => new sfValidatorString( array('max_length' => 1000, 'required' => false)), ));
$this->widgetSchema->setNameFormat('livro[%s]');
$this->errorSchema = new sfValidatorErrorSchema( $this->validatorSchema);
parent::setup(); }
public function getModelName() { return 'Livro';
Capítulo 10 Forms: criando formulários - 76
}
}?>
Lembrando que ele não deve ser alterado ou acessado diretamente, mas sim a classe extendida LivroForm que está em:
lib/form/doctrine/LivroForm.class.php
<?phpclass LivroForm extends BaseLivroForm{ public function configure() { }}?>
O que for implementado dentro do método configure() sobrescreve o que foi definido na classe Base.
Podemos identificar na classe Base quatro trechos básicos que são:
• $this->setWidgets: define os widgets (elementos de formulário) para cada campo da tabela;
• $this->setValidators: define os validadores para cada campo da tabela;
• $this->widgetSchema->setNameFormat: define um padrão para os nomes de campos (o padrão é criar um array com o nome da tabela, onde cada chave do array é o nome de um campo);
Capítulo 10 Forms: criando formulários - 77
• $this->errorSchema: define o esquema de erros.
Os dois últimos são padronizados e raramente é útil personalizá-los. Os dois primeiros são a alma do esquema de formulários do Symfony, e então vamos conhecê-los um pouco mais.
10.3. Widgets
Na criação da classe BaseLivroForm vemos que ela é uma classe extendida de BaseFormDoctrine - que por sua vez é uma classe extendida de sfForm. A classe BaseFormDoctrine é uma "versão para o ORM Doctrine" da classe padrão de manipulação de formulários do Symfony.
A classe sfForm manipula todas as operações de formulário e seus campos, chamados Widgets. Cada Widgets é uma extensão da classe base sfWidget, e possui um funcionalidade específica.
Quando vamos criar um formulário Web existem várias dificuldades, incluindo tamanho dos campos, o rótulo (label), o tipo de input (text, password, radio, checkbox, além das opções de select e textarea), e muitas vezes ainda é necessário pegar informações de listagem ou de outras tabelas para criar listagens.
Isto no Symfony fica muito simples: existem Widgets para cada situação corriqueira. Se olhamos o código específico de criação dos Widgets, temos:
$this->setWidgets(array('isbn' => new sfWidgetFormInputHidden(),'autor'=> new sfWidgetFormInput(),'titulo' => new sfWidgetFormInput(),'cat_id' => new sfWidgetFormDoctrineChoice(array('model' => 'Categoria', 'add_empty' => true)),'preco'=> new sfWidgetFormInput(),'sumario' => new sfWidgetFormInput(), ));
vemos, por exemplo, que o campo isbn está como hidden, sendo exibido assim:
Capítulo 10 Forms: criando formulários - 78
<input type="hidden" name="livro[isbn]" id="livro_isbn" />
Os campos tipo input são gerados de forma simples:
<input type="text" name="livro[autor]" id="livro_autor" /><input type="text" name="livro[titulo]" id="livro_titulo" /><input type="text" name="livro[preco]" id="livro_preco" /><input type="text" name="livro[sumario]" id="livro_sumario" />
Mas o poder do Symfony é visto no campo cat_id, que é select montado na tabela categoria:
<select name="livro[cat_id]" id="livro_cat_id"><option value="" selected="selected"></option><option value="1">Informática</option><option value="2">Auto Ajuda</option><option value="4">Administração</option><option value="5">Ficção</option></select>
No capítulo Criando uma aplicação rápida com o Symfony vimos que para esta funcionalidade ser efetiva é preciso criar um método __toString que retorna um dos campos da tabela. Isto é feito no Model, pois ele é que é chamado neste momento para preencher o select.
Existem diversos Widgets, e cada um possui seus atributos. Não cabe estudar um a um, mas conforme for necessário, se detalhando em cada um que se utilizar. Uma lista completa de referência dos Widgets é encontrada em:
http://www.symfony-project.org/api/1_4/widget
10.4. Validators
Uma vez que temos os Widgets podemos anexar a eles Validators
Capítulo 10 Forms: criando formulários - 79
(validadores), que são regras para aceitar os valores enviados pelo formulário. Os Validators, é preciso deixar claro, sempre testam TODOS os campos; portanto, mesmo que não seja necessário validar um campo, é preciso definir uma validação "vazia", ou não obrigatória.
Vejamos os ... gerados automaticamente pelo Doctrine:
$this->setValidators(array('isbn' => new sfValidatorDoctrineChoice( array('model' => 'Livro', 'column' => 'isbn', 'required' => false)),'autor'=> new sfValidatorString( array('max_length' => 80, 'required' => false)),'titulo' => new sfValidatorString( array('max_length' => 100, 'required' => false)),'cat_id' => new sfValidatorDoctrineChoice( array('model' => 'Categoria', 'column' => 'cat_id', 'required' => false)),'preco'=> new sfValidatorNumber(),'sumario' => new sfValidatorString( array('max_length' => 1000, 'required' => false)), ));
Cada Validator já possui regras pré-definidas e aceita um ou dois arrays como parâmetro:
• primeiro: opções
• segundo: mensagens
Se não há o segundo array, então as mensagens serão padrão. Mas veremos logo a seguir como personalizá-las.
Capítulo 10 Forms: criando formulários - 80
Novamente, há muitas opções, que variam conforme o Validator. Alguns são muito usados e comuns:
• max_length: tamanho máximo de caracteres
• min_length: tamanho mínimo de caracteres
• required: campo obrigatório ou não
• default: valor padrão para o campo
• label: rótulo para o campo
No exemplo acima, sfValidatorDoctrineChoice exige model e column, ou seja, vai checar se o valor enviado está dentro dos possíveis desta tabela e campo (ou seja, checa a integridade referencial antes de salvar no banco, o que evita erros de SQL).
As mensagens de erro são definidas no segundo array, e existem duas mensagens básicas:
• required: quando o campo é de preenchimento obrigatório ('Required')
• invalid: quando a regra de validação não foi atendida ('Invalid')
Outros são comuns para max_lenght e min_lenght, que informam o tamanho máximo e mínimo necessários.
Para personalizar as mensagens, envia-se o segundo array como parâmetro do Validator:
// (...)'email'=> new sfValidatorEmail( array('required' => true), array('required' => 'Email é obrigatório', 'invalid' => 'Email inválido') ),// (...)
Opcionalmente é possível definir uma mensagem padrão de required e invalid
Capítulo 10 Forms: criando formulários - 81
para os Validators informando:
<?phpclass LivroForm extends BaseLivroForm{ public function configure() { $this->setRequiredMessage('Campo obrigatório'); $this->setInvalidMessage('Valor inválido - corrija'); }}?>
sem, entretanto, perder a funcionalidade de criar mensagens personalizadas para cada campo.
Há vários Validators, e da mesma forma que os Widgets é mais interessante estudá-los quando necessitamos deles. Uma lista com os Validators disponíveis é encontrada em:
http://www.symfony-project.org/api/1_4/validator
10.5. Labels
O Symfony cria automaticamente rótulos (labels) para os campos baseado em seu nome. De forma simples, ele tenta manter o seguinte padrão:
• data_nascimento => Data Nascimento
• codigo_area => Codigo Area
Apesar de ser interessante, este padrão não é muito útil em português, pois temos acentuação e isto não é gravado com o nome do campo (pelo menos não por administradores e projetistas de banco com o um mínimo de bom senso).
Podemos personalizar os rótulos através do próprio Widget com a opção label
Capítulo 10 Forms: criando formulários - 82
como utilizando um método específico:
<?phpclass LivroForm extends BaseLivroForm{ public function configure() { $this->widgetSchema->setLabels(array('isbn' => 'ISBN','autor'=> 'Autor','titulo' => 'Título','cat_id' => 'Categoria','preco'=> 'Preço','sumario' => 'Sumário', )); }}?>
10.6. Controlando o formulário
Todo controle do formulário, como não poderia deixar de ser, é realizado nas ações (controller). Abaixo segue um arquivo actions.class.php do módulo livro, comentado para melhor compreensão do mecanismo. Os métodos não relacionados ao formulário foram excluídos para melhor didática
<?phpclass livroActions extends sfActions{
Capítulo 10 Forms: criando formulários - 83
// (...)
// Método para criação de novo registro public function executeNew(sfWebRequest $request) {// Cria uma nova instância de LivroForm// e o disponibiliza para a View.// // Como não está vinculado a um registro pré-existente,// quando for chamado $form->getObject()->isNew()// resultará "true", e então as opções para novo registro// serão exibidas na View, como no caso de para qual ação // será enviada o formulário
$this->form = new LivroForm(); }
// Método que processa a criação de um novo registro public function executeCreate(sfWebRequest $request) {
// Se não foi recebido um POST, redireciona para// página não encontrada (erro 404)
$this->forward404Unless($request->isMethod('post'));
// Cria uma nova instância de LivroForm $this->form = new LivroForm();
// Processa o formulário através do método// processForm desta classe (mais adiante) $this->processForm($request, $this->form);
// Define o template como newSuccess.php// (o padrão seria createSuccess.php)
Capítulo 10 Forms: criando formulários - 84
$this->setTemplate('new'); }
// Método para edição de registro public function executeEdit(sfWebRequest $request) {
// Se não conseguir recuperar um parâmetro "isbn" e com ele// recuperar um registro do banco de dados, retorna// página não encontrada (erro 404) com a mensagem:// Object livro does not exist $this->forward404Unless($livro=Doctrine::getTable('Livro')->find(array($request->getParameter('isbn'))), sprintf('Object livro does not exist (%s).', $request->getParameter('isbn')));
// Cria uma nova instância de LivroForm// mas agora com dados do livro recuperado do banco de dados $this->form = new LivroForm($livro); }
// Método que processa a atualização do registro public function executeUpdate(sfWebRequest $request) {// Se não foi recebido um POST ou um PUT, redireciona para// página não encontrada (erro 404)// (ver informação sobre o PUT na seção sobre a apresentação// do formulário mais adiante. $this->forward404Unless($request->isMethod('post') || $request->isMethod('put'));
// Se não conseguir recuperar um parâmetro "isbn" e com ele// recuperar um registro do banco de dados, retorna// página não encontrada (erro 404) com a mensagem:
Capítulo 10 Forms: criando formulários - 85
// Object livro does not exist $this->forward404Unless($livro=Doctrine::getTable('Livro')->find(array($request->getParameter('isbn'))), sprintf('Object livro does not exist (%s).', $request->getParameter('isbn')));
// Cria uma nova instância de LivroForm// mas agora com dados do livro recuperado do banco de dados $this->form = new LivroForm($livro);
// Processa o formulário através do método// processForm desta classe (mais adiante) $this->processForm($request, $this->form);
// Define o template como editSuccess.php// (o padrão seria updateSuccess.php) $this->setTemplate('edit'); }
// Método que processa o envio do formulário// em separado para evitar duplicidade de código protected function processForm(sfWebRequest $request, sfForm $form) {
// O método bind do formulário recupera os dados enviados// no $request para poder validar os dados $form->bind($request->getParameter( $form->getName()), $request->getFiles($form->getName()));
// Realiza a validação do formulário if ($form->isValid()) {// Se o formulário foi validado, salva o registro
Capítulo 10 Forms: criando formulários - 86
$livro = $form->save();
// e então redireciona para a tela edição, evitando// o problema de recarregar a página (F5) gerar um novo// envio do formulário$this->redirect('livro/edit?isbn='.$livro->getIsbn()); }
// Se o formulário não foi validado, então na exibição do // formulário as mensagens de erro aparecerão }}?>
10.7. Exibindo o formulário
Até o momento só estudamos a configuração do formulário, agora veremos como exibí-lo no template.
10.7.1. Método automatizado
A geração automática que realizamos no capítulo Criando uma aplicação rápida com o Symfony nos dá uma idéia simples de como criar a parte visual do formulário. O arquivo é um partial que pode ser utilizado em qualquer lugar da aplicação.
O arquivo segue comentado para melhor compreensão.
apps/admin/modules/livro/templates/_form.php
Capítulo 10 Forms: criando formulários - 87
<!--Inclusão de CSS e Javascript para o formulário, se houver--><?php include_stylesheets_for_form($form) ?><?php include_javascripts_for_form($form) ?>
<!--Tag "form"
Algumas condições são incluídas para num único arquivo trabalhar tanto edição quanto criação de registro.
O objeto $form, criado pela ação, pode verificar se o objeto é novo ou não (ou seja, ainda não foi salvo), e entãooptar por montar o link com a ação create ou update.
De mesmo modo, se não for novo, coloca a chave primária como parâmetro (este é um modo de fazer isto).-->
<form action="<?php echo url_for('livro/'.($form->getObject()->isNew() ? 'create' : 'update').(!$form->getObject()->isNew() ? '? isbn='.$form->getObject()->getIsbn() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>>
<!--Novamente se for não for um objeto novo, o Symfony inclui um campo oculto para simular o método PUT do HTTP (algo aindapouco utilizado, e por isto simulado pelo Symfony paraaumentar a segurança)
Capítulo 10 Forms: criando formulários - 88
--><?php if (!$form->getObject()->isNew()): ?><input type="hidden" name="sf_method" value="put" /><?php endif; ?>
<!--Montando a tabela base--> <table><!--Utilizando a tag tfoot, ou rodapé da tabela. Mesmo sendo informada aqui no início, será exibida ao final da tabela-->
<tfoot>
<td colspan="2"><!--O botão de cancelar retorna para a listagem de registros--> <a href="<?php echo url_for('livro/index') ?>"> Cancel</a>
<!--Se o registro não é novo, então exibe um botão para excluir.Como o uso do helper link_to é montado o link, enviando a chave primária e ainda fazendo uso de um recurso de javascriptpara exibir uma janela de confirmação de exclusão(Are you sure? Tem certeza?)--> <?php if (!$form->getObject()->isNew()): ?> <?php echo link_to('Delete', 'livro/delete?isbn='.$form->getObject()->getIsbn(), array('method' => 'delete',
Capítulo 10 Forms: criando formulários - 89
'confirm' => 'Are you sure?')) ?> <?php endif; ?><!--Botão de envio--> <input type="submit" value="Save" /> </td>
</tfoot> <tbody><!--A renderização do form propriamente dito--><?php echo $form ?> </tbody> </table></form>
10.7.2. Método detalhado
Vimos que com o comando:
<?php echo $form ?>
todos os campos são listados em forma de tabela. Mas é possível especificar o layout campo a campo.
O Symfony renderiza para cada campo três partes:
• Rótulo: renderLabel()
<?php echo $form['email']->renderLabel() ?>// HTML<label for="pessoa_email">Email</label>
Capítulo 10 Forms: criando formulários - 90
• Tag do formulário: render()
<?php echo $form['email']->render() ?>// HTML<input type="text" name="pessoa[email]" id="pessoa_email" />
• Mensagens de erro: renderError() (exibido apenas se o formulário foi validado e o campo não passou na validação)
<?php echo $form['email']->renderError() ?>// HTML<ul class="error_list"> <li>Email inválido</li></ul>
É possível gerar também a linha de tabela inteira:
// Gerando uma linha de tabela// Não há diferença da linha gerada pelo // echo $form<?php echo $form['email']->renderRow() ?>
Capítulo 10 Forms: criando formulários - 91
Capítulo 11 AJAX - 92
Capítulo 11
AJAX
11.1. Introdução teórica
Já estudamos na introdução o conceito de AJAX. A grande questão do AJAX é:
• AJAX deixa tudo muito legal, mas...
• ... AJAX dá muito trabalho!!
Isto é uma realidade muito em função da quantidade de Javascript que é necessário escrever adicionalmente ao código gerado no servidor.
Entretando, com o Symfony, pouco se utiliza o Javascript - a não ser que se necessite de uma funcionalidade muitíssimo específica. Utilizando por padrão o framework para AJAX Prototype é possível ter diversas funcionalidades com nenhuma ou pouca escrita de Javascript.
Capítulo 11 AJAX - 93
11.2. Incluindo Javascript
No capítulo Camada View: Apresentação conhecemos os Helpers. Existe um grupo de Helpers para Javascript. Seu uso básico num layout ou template é:
<?php // Incluíndo o Helperuse_helper('Javascript') ;?><?php // Utilizando o Helper para criar uma função
echo javascript_tag(" function alterarNome() { ... }") ?><!-- HTML Gerado:Para validar em XHTML é gerado dentro da declaração CDATA--><script type="text/javascript"> //<![CDATA[function alterarNome(){ ...} //]]> </script>
Capítulo 11 AJAX - 94
11.3. Criando um link para uma função
<?php echo link_to_function('Alterar Nome', "alterarNome()") ?><!--HTML Gerado:--><a href="#" onClick="alterarNome(); return none;">Alterar Nome</a>
Existe ainda o Helper button_to_function, que faz um link para uma função num botão, ou então é possível fazer uma imagem clicável que aciona uma função em javascript; basta mudar o primeiro parâmetro de link_to_function, para image_tag('imagem.png').
11.4. Alterado o conteúdo de um elemento HTML
<div id="processando">Iniciado processamento</div><?php echo javascript_tag( update_element_function('processando', array( 'content' => "Processamento completo", ))) ?>
// Colocando o conteúdo após o conteúdo atual// com "before" seria antes.update_element_function('processando', array( 'position' => 'after', 'content' => "Agora o conteúdo ficou depois",));
Capítulo 11 AJAX - 95
11.5. Chamando uma função remota
Aqui realmente começa a interação via AJAX.
Inicialmente precisamos confirmar se o acesso à biblioteca do AJAX está disponível.
symfony plugin:install sfProtoculousPlugin
Agora é só utilizar os recursos do AJAX. Vamos criar uma pequena ação para teste:
apps/admin/modules/livro/actions/action.class.php
<?phpclass livroActions extends sfActions{ // (...)
// Método para devolver um valor via AJAX public function executeAjax($request) {
// Testando se é uma requisição AJAX válida if($this->getRequest()->isXmlHttpRequest()) {
// Retornando um texto concatenado com o parâmetro enviadoreturn $this->renderText('Ok, recebi uma requisição: ' . $request->getParameter('valor')); } else {
// Se a requisição não é AJAX, então envia uma mensage de erroreturn $this->renderText('Requisição mal formatada'); } }?>
Capítulo 11 AJAX - 96
Agora vamos adicionar um código à index do módulo livro para aprendermos como fazer a requisição e recuperar o valor em um elemento do HTML:
apps/admin/modules/livro/templates/indexSuccess.php
<?php// Incluíndo o Helperuse_helper('Javascript') ;
// (...)
?>
<!-- div que recebe o retorno da requisição AJAX --><div id="retorno"></div>
<?php /*A função link_to_remote*/
echo link_to_remote('Teste de Ajax', array( 'update' => 'retorno', 'url' => 'livro/ajax?valor=TESTANDO',))?>
<?php /*A função link_to_remote com confirmação*/
echo link_to_remote('Teste de Ajax', array( 'update' => 'retorno',
Capítulo 11 AJAX - 97
'url' => 'livro/ajax?valor=TESTANDO', 'confirm' => 'Tem certeza?',))?>
<?php /*A função link_to_remote com método GET*/
echo link_to_remote('Teste de Ajax', array( 'update' => 'retorno', 'url' => 'livro/ajax?valor=TESTANDO', 'method' => 'get',))?>
Se a intenção é que a função seja executada imediatamente no carregamento da página, então é utilziada o helper remote_function:
<?php// Incluíndo o Helperuse_helper('Javascript') ;
// (...)
?>
<!-- div que recebe o retorno da requisição AJAX --><div id="retorno"></div>
<?php /*A função remote_function*/
Capítulo 11 AJAX - 98
echo remote_function('Teste de Ajax', array( 'update' => 'retorno', 'url' => 'livro/ajax?valor=TESTANDO',))?>
11.5.1. Atualizações Periódicas
Este tipo de chamada AJAX é realizada para atualizar constantemente uma página, como por exemplo para checar o recebimento de novos emails.
<div id="retorno"></div><?php // "frequency" é o tempo em segundos// "with" permite passar como parâmetro o valor de algum elemento// do HTML (utilizando a syntaxe do Prototype)echo periodically_call_remote(array( 'frequency' => 60, 'update' => 'retorno', 'url' => 'modulo/acao', 'with'=> "'param=' + \$F('mycontent')",)) ?>
Capítulo 11 AJAX - 99
11.5.2. Formulários AJAX
Vamos fazer um exemplo com o formulário do módulo categoria. Primeiramente, criamos uma ação para tratar a requisição AJAX de envio do formulário:
<?phpclass categoriaActions extends sfActions{
// (...)
// Método que trata o formulário via AJAX public function executeCreateajax(sfWebRequest $request) {
// Verifica se é uma requisição AJAX e se é um // formulário enviado if($request->isXmlHttpRequest() && $request->isMethod('post')) {
// Cria uma nova instância de CategoriaForm$this->form = new CategoriaForm();
// O método bind do formulário recupera os dados enviados// no $request para poder validar os dados$this->form->bind($request->getParameter( $this->form->getName()), $request->getFiles($this->form->getName()));
// Realiza a validação do formulárioif ($this->form->isValid()){// Se o formulário foi validado, salva o registro $categoria = $this->form->save();
// Devolve uma mensagem via AJAX - ok return $this->renderText('Registro Salvo');
Capítulo 11 AJAX - 100
} else {// Devolve uma mensagem via AJAX - erro return $this->renderText('Erro ao salvar registro');}
} else {// Se não é uma requisição AJAX ou não é um POST// devolve uma mensagem de erro
return $this->renderText('Requisição mal formatada'); } }
}?>
11.5.3. Callbacks
Callbacks são ações que são executadas durante a requisição AJAX. O uso mais comum delas é dar ao usuário uma noção de que o "servidor está trabalhando" enquanto ele aguarda uma requisição - os famosos "Carregando" ou imagem com animação.
Os callbacks são os seguintes:
• before: Antes da requisição ser iniciada
• after: Imediatamente após a requisição ser inicia e antes de carregar
• loading: Quando a resposta remota está sendo carregada pelo navegador
• loaded: Quando o navegador terminou de carregar a resposta remota
• interactive: quando o usuário pode interagir com a resposta remota, mesmo que ainda não tenha terminado
Capítulo 11 AJAX - 101
• success: Quando o XMLHttpRequest está completo, e o status HTTP está na faixa 2xx
• failure: Quando o XMLHttpRequest está completo, e o status HTTP não está na faixa 2xx
• 404: Quando a requisição retorna o status 404
• complete: Quando XMLHttpRequest está completo (gerado logo após success ou failure, se ocorrerem)
O exemplo clássico: um "Carregando":
<div id="retorno"></div><div id="carregando">Carregando algo...</div><?php echo link_to_remote('Vamos pegar algo', array( 'update'=> 'retorno', 'url'=> 'modulo/acao', 'loading' => "Element.show('carregando')", 'complete' => "Element.hide('carregando')",)) ?>
Capítulo 11 AJAX - 102
Capítulo 11 AJAX - 103
Apêndices
Capítulo 12 ApêndiceBanco de dados "livraria" - 104
Capítulo 12
Apêndice
Banco de dados "livraria"
--
-- Banco de Dados: `livraria444`
--
CREATE DATABASE `livraria444` DEFAULT CHARACTER SET utf8
COLLATE utf8_unicode_ci;
USE `livraria444`;
SET FOREIGN_KEY_CHECKS=0;
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
Capítulo 12 ApêndiceBanco de dados "livraria" - 105
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Estrutura da tabela `categoria`
--
DROP TABLE IF EXISTS `categoria`;
CREATE TABLE IF NOT EXISTS `categoria` (
`cat_id` int(10) unsigned NOT NULL auto_increment,
`descricao` char(60) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Estrutura da tabela `cliente`
--
DROP TABLE IF EXISTS `cliente`;
CREATE TABLE IF NOT EXISTS `cliente` (
`cliente_id` int(10) unsigned NOT NULL auto_increment,
Capítulo 12 ApêndiceBanco de dados "livraria" - 106
`nome` char(60) collate utf8_unicode_ci NOT NULL,
`endereco` char(80) collate utf8_unicode_ci NOT NULL,
`cidade` char(30) collate utf8_unicode_ci NOT NULL,
`uf` char(20) collate utf8_unicode_ci default NULL,
`cep` char(9) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`cliente_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Estrutura da tabela `livro`
--
DROP TABLE IF EXISTS `livro`;
CREATE TABLE IF NOT EXISTS `livro` (
`isbn` char(13) collate utf8_unicode_ci NOT NULL,
`autor` char(80) collate utf8_unicode_ci default NULL,
`titulo` char(100) collate utf8_unicode_ci default NULL,
`cat_id` int(10) unsigned default NULL,
`preco` float(10,2) NOT NULL,
`sumario` varchar(1000) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`isbn`),
KEY `cat_id` (`cat_id`)
Capítulo 12 ApêndiceBanco de dados "livraria" - 107
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Estrutura da tabela `pedido`
--
DROP TABLE IF EXISTS `pedido`;
CREATE TABLE IF NOT EXISTS `pedido` (
`pedido_id` int(10) unsigned NOT NULL auto_increment,
`cliente_id` int(10) unsigned NOT NULL,
`total` float(6,2) default NULL,
`data_pedido` date NOT NULL,
`status` char(10) collate utf8_unicode_ci default NULL,
`entrega_nome` char(60) collate utf8_unicode_ci NOT NULL,
`entrega_endereco` char(80) collate utf8_unicode_ci NOT NULL,
`entrega_cidade` char(30) collate utf8_unicode_ci NOT NULL,
`entrega_uf` char(20) collate utf8_unicode_ci default NULL,
`entrega_cep` char(9) collate utf8_unicode_ci default NULL,
`forma_pagto` enum('BOLETO','DEPÓSITO','CARTÃO')
collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`pedido_id`),
KEY `cliente_id` (`cliente_id`)
Capítulo 12 ApêndiceBanco de dados "livraria" - 108
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
--
-- Estrutura da tabela `pedidoitem`
--
DROP TABLE IF EXISTS `pedidoitem`;
CREATE TABLE IF NOT EXISTS `pedidoitem` (
`peditem_id` int(10) unsigned NOT NULL auto_increment,
`pedido_id` int(10) unsigned NOT NULL,
`isbn` char(13) collate utf8_unicode_ci NOT NULL,
`preco` float(10,2) NOT NULL,
`quantidade` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`peditem_id`),
KEY `pedido_id` (`pedido_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
--
-- Estrutura da tabela `usuario`
--
DROP TABLE IF EXISTS `usuario`;
CREATE TABLE IF NOT EXISTS `usuario` (
Capítulo 12 ApêndiceBanco de dados "livraria" - 109
`login` char(16) collate utf8_unicode_ci NOT NULL,
`nome` char(16) collate utf8_unicode_ci NOT NULL,
`senha` char(40) collate utf8_unicode_ci NOT NULL,
`perfil` enum('admin','gerente','despacho')
collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`login`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Restrições para as tabelas dumpadas
--
--
-- Restrições para a tabela `livro`
--
ALTER TABLE `livro`
ADD CONSTRAINT `livro_ibfk_1` FOREIGN KEY (`cat_id`)
REFERENCES `categoria` (`cat_id`);
--
-- Restrições para a tabela `pedido`
--
ALTER TABLE `pedido`
Capítulo 12 ApêndiceBanco de dados "livraria" - 110
ADD CONSTRAINT `pedido_ibfk_1` FOREIGN KEY (`cliente_id`)
REFERENCES `cliente` (`cliente_id`);
--
-- Restrições para a tabela `pedidoitem`
--
ALTER TABLE `pedidoitem`
ADD CONSTRAINT `pedidoitem_ibfk_1` FOREIGN KEY (`pedido_id`)
REFERENCES `pedido` (`pedido_id`);
SET FOREIGN_KEY_CHECKS=1;
Capítulo 13 ApêndicesfDoctrineGuard plugin - 111
Capítulo 13
Apêndice
sfDoctrineGuard plugin
http://www.symfony-project.org/plugins/sfDoctrineGuardPlugin
sfDoctrineGuardPlugin é um plugin do Symfony que fornece capacidade autenticação e autorização sobre o padrão de segurança do Symfony.
Lhe dá um Model (objetos usuário, grupo e permissão) e módulos (backend/retaguarda e frontend/frente) para implementar segurança em sua aplicação Symfony em um minuto num plugin configurável
Para instalar o plugin:
symfony plugin:install sfDoctrineGuardPlugin
Reconstrua seu Model:
symfony doctrine:build-modelsymfony doctrine:build-sqlsymfony doctrine:build-formssymfony doctrine:build-filters
Atualize as tabelas de seu banco de dados reiniciando-as (isto vai excluir todas as tabelas e recriá-las
symfony doctrine:insert-sql
Capítulo 13 ApêndicesfDoctrineGuard plugin - 112
Ative um ou mais módulos no seu settings.yml (opcional)
Para sua aplicação backend/retaguarda: sfGuardUser, sfGuardGroup, sfGuardPermission
Para sua aplicação frontend/frente: sfGuardAuth
all: .settings: enabled_modules:[default, sfGuardGroup, sfGuardUser, sfGuardPermission]
Limpe seu cache
symfony cc
Opcionalmente crie um usuário padrão
symfony guard:create-user fabien $secret
Opcionalmente ative o filtro "Remember Me" em filters.yml
security: class: sfGuardBasicSecurityFilter
13.1.1. Implementando segurança em sua aplicação
Para implementar a segurança em uma aplicação Symfony:
Ativando o módulo sfGuardAuth em settings.yml
all: .settings: enabled_modules: [..., sfGuardAuth]
Altere o padrão para os módulos de login e segurança em settings.yml
login_module:sfGuardAuthlogin_action:signinsecure_module: sfGuardAuthsecure_action: secure
Capítulo 13 ApêndicesfDoctrineGuard plugin - 113
Altere a classe pai em myUser.class.php
class myUser extends sfGuardSecurityUser{}
Adicionamente altere as regras de forward em routing.yml
sf_guard_signin: url:/login param: { module: sfGuardAuth, action: signin }sf_guard_signout: url:/logout param: { module: sfGuardAuth, action: signout }
sf_guard_password: url:/request_password param: { module: sfGuardAuth, action: password }
Você pode personalizar o parâmetro url em cada rota.
Você deve ter uma regra de roteamento @homepage (utilizado quando o usuário faz logout).
Estas rotas são automativamente registradas pelo plugin se o módulo sfGuardAuth é ativado, a não ser que você defina sf_guard_plugin_routes_register para falso no arquivo de configuração app.yml
all: sf_guard_plugin: routes_register: false
Ative a segurança para alguns módulos ou para a aplicação inteira em security.yml
default:
Capítulo 13 ApêndicesfDoctrineGuard plugin - 114
is_secure: on
Está feito. Agora, se você tentar acessar uma página segura, será redirecionado para a página de login.
Se você carregou o arquivo padrão de dados do plugin, tente logar com o usuário admin com a senha admin.
13.1.2. Administrando seus usuários, permissões e grupos
Para administrar seus usuários, permissões e grupos, o sfDoctrineGuardPlugin vem com 3 módulos que podem ser integrados á sua aplicação backend/retaguarda. Estes módulos são auto-gerados graças ao Symfony Admin Generator.
Ative os módulos em settings.yml
all: .settings: enabled_modules: [..., sfGuardGroup, sfGuardPermission, sfGuardUser]
Acesse os módulos com a rota padrão:
http://livraria.local/sfGuardUser
13.1.3. Personalizando os templates do módulo sfGuardAuth
Por padrão, o módulo sfGuardAuth vem com dois templates muito simples:
• signinSuccess.php
• secureSuccess.php
Se você quer personalizar um destes templates:
• Crie o módulo sfGuardAuth em sua aplicação (não utilize a tarefa init-module, apenas crie o diretório sfGuardAuth)
• Crie um template com o nome do template que você quer personalizar no diretório sfGuardAuth/templates
• O Symfony agora monta seu template ao invés do padrão.
Capítulo 13 ApêndicesfDoctrineGuard plugin - 115
13.1.4. Personalizando os templates do módulo sfGuardAuth
Se você quer personalizar ou adicionar métodos ao sfGuardAuth:
• Crie um módulo sfGuardAuth em sua aplicação
• Crie um arquivo actions.class.php em seu diretório actions que herde de BasesfGuardAuthActions (não esqueça de incluir BasesfGuardAuthActionspois ele não pode ser autocarregado pelo Symfony)
<?php
require_once(sfConfig::get('sf_plugins_dir').'/sfDoctrineGuardPlugin/modules/sfGuardAuth/lib/BasesfGuardAuthActions.class.php');
class sfGuardAuthActions extends BasesfGuardAuthActions{ public function executeNewAction() { return $this->renderText('This is a new sfGuardAuth action.'); }}?>
13.1.5. Classe sfGuardSecurityUser
Esta classe herda da classe sfBasicSecurityUser do Symfony e é utilizada pelo objeto user na sua aplicação Symfony (por que você modificou a classe base myUser anteriormente).
Então, para acessá-la, você pode utilzizar o padrão $this-;gt;getUser() em suas ações ou $sf_user em seus templates.
Capítulo 13 ApêndicesfDoctrineGuard plugin - 116
sfGuardSecurityUser adiciona alguns métodos:
• signIn() and signOut()
• getGuardUser() que retorna o objeto sfGuardUser
• Um conjunto de métodos "proxy" para acessar diretamente o objeto sfGuardUser
Por exemplo, para recuperar o nome de usuário atual:
$this->getUser()->getGuardUser()->getUsername()
// ou via proxy method
$this->getUser()->getUsername()
13.1.6. Flag superadministrador
O sfDoctrineGuardPlugin tem o conceito de super administrator. Um usuário que é superadministrador sobrepõem-se a todas as checagens de credencial.
A flag de superadministrador não pode ser definida via web, você deve definir esta flag diretamente no banco de dados ou através da tarefa de linha de comando:
symfony promote-super-admin adminsymfony guard:promote admin (versão 3.1.3)
13.1.7. Validators (Validadores)
O sfDoctrineGuardPlugin vem com um validador para você utilizar em seus módulos: sfGuardUserValidator.
Este validador é utilizado pelo módulo sfGuardAuth para validar o usuário e senha e automaticamente logar o usuário.
Capítulo 13 ApêndicesfDoctrineGuard plugin - 117
13.1.8. Personalizando o Model sfGuardUser
O Model sfGuardUser é muito simples. Não há colunas email ou first-name ou birthday. Como você não pode adicionar métodos à classe, o sfAuthPlugin possibilita definir uma classe com o perfil do usuário.
Por padrão, sfGuardUser procura a classe sfGuardUserProfile.
Aqui está um exemplo simples de uma classe sfGuardUserProfile que você pode adicionar ao schema.yml:
sf_guard_user_profile: _attributes: { phpName: sfGuardUserProfile } id: user_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: true, onDelete: cascade } first_name: varchar(20) last_name:varchar(20) birthday: date
Agora você pode acessar os dados do usuário através do objeto user:
$this->getUser()->getGuardUser()->getProfile()->getFirstName()// or via the proxy method$this->getUser()->getProfile()->getFirstName()
O métodogetProfile() recupera o objeto de perfil de usuário associado ou cria um novo se ainda não existe.
Quando você exclui um usuário, o perfil associado também é excluído.
Você pode mudar o nome da classe de perfil de usuário e o nome da chave estrangeira em app.yml:
all: sf_guard_plugin: profile_class:sfGuardUserProfile profile_field_name: user_id
Capítulo 13 ApêndicesfDoctrineGuard plugin - 118
13.1.9. Checar a senha do usuário com um método externo
Se você não pode armazenar a senha no banco de dados porque possui um servidor LDAP, um arquivo .htaccess ou você armazena sua suas senhas em outra tabela, você pode informar sua própria chamada checkPassword (método estático ou função) em app.uml
all: sf_guard_plugin: check_password_callable: [MyLDAPClass, checkPassword]
Quando o Symfony chama o método $this->getUser()->checkPassword(), irá chamar seu método ou função. Sua função deve receber dois parâmetros, o primeiro é o nome de usuário e o segundo é a senha. Deve retornar true ou false. Aqui segue um modelo para esta função:
function checkLDAPPassword($username, $password){ $user = LDAP::getUser($username); if ($user->checkPassword($password)) { return true; } else { return false; }}
Capítulo 13 ApêndicesfDoctrineGuard plugin - 119
13.1.10. Alterar o algoritmo utilizado para armazenar senhas
Por padrão, senhas são armazenadas num hash sha1(). Mas você pode modificar isto para qualquer chamada em app.yml:
all: sf_guard_plugin:algorithm_callable: [MyCryptoClass, MyCryptoMethod]
# or
all: sf_guard_plugin: algorithm_callable: md5
Como o algoritmo é armazenado para cada usuário, você pode mudar de idéia depois sem a necessidade de regerar todas as senhas atuais dos usuários.
13.1.11. Alterando o nome ou o período de expiração do cookie "Remember Me"
Por padrão, a funcionalide Remember Me cria um cookie chamado sfRemember vai permanecer por 15 dias. Você pode modificar este comportamente em app.yml:
all: sf_guard_plugin: remember_key_expiration_age: 2592000# 30 days in seconds remember_cookie_name: myAppRememberMe
Capítulo 13 ApêndicesfDoctrineGuard plugin - 120
13.1.12. Personalizando a manipulação de redirecionamento de sfGuardAuth
Se você deseja redirecionar o usuário para seu perfil após um login ou definir um destino de logout, você pode alterar os valores de redirecionamente em app.yml:
all: sf_guard_plugin: # the plugin use the referer as default success_signin_url:@my_route?param=value # the plugin use the referer as default success_signout_url: module/action
13.1.13. Configurando o formulário de login
Você pode modificar o formuário de login do módulo sfGuardAuth em app.yml:
all: sf_guard_plugin: signin_form: sfGuardFormSigninCustom