Dev (Back & Front)ARTIGO

Crud usando Symfony 1.0

Dando continuidade à série sobre artigos do framework symfony, essa semana montarei um CRUD sem utilizar o admin generator. Será criado um sistema de agenda de contatos de telefones.

Tudo será feito de forma manual e vocês verão que não é nenhum bicho de sete cabeças. Qualquer dúvida quanto à instalação, confira o artigo anterior.

Requisitos:

Versão do framework utilizada: 1.0.18 (http://www.symfony-project.org/)

IDE utilizada: Netbeans 6.7 (http://www.netbeans.org)

Siga os procedimentos abaixo:

Crie a pasta onde ficará o projeto:

mkdir imasters

Verifique se o symfony está setado no path (sendo que o /opt/php/bin é onde meu php tá rodando).

No linux:

PATH=$PATH:/opt/php/bin; export PATH

No Windows:

Defina em variáveis de ambiente no campo PATH o caminho do PHP. Exemplo:

C:\\xampplite\\php

Dentro da pasta imasters, criaremos o projeto do symfony (será criada toda a estrutura). Os comandos abaixo são executados no terminal.

symfony init-project imasters

Criar a aplicação frontend:

symfony init-app frontend

O banco de dados que iremos utilizar será o mesmo do artigo anterior.

Abaixo, o SQL para criação do BD e das tabelas.

CREATE SCHEMA IF NOT EXISTS `exemplo01` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;<br />USE `exemplo01`;<br /><br />CREATE  TABLE IF NOT EXISTS `exemplo01`.`contato` (<br />  `id` INT NOT NULL AUTO_INCREMENT ,<br />  `nome` VARCHAR(100) NULL ,<br />  PRIMARY KEY (`id`) )<br />ENGINE = InnoDB;<br /><br /><br />CREATE  TABLE IF NOT EXISTS `exemplo01`.`tipo` (<br />  `id` INT NOT NULL AUTO_INCREMENT ,<br />  `tipo` VARCHAR(45) NULL ,<br />  PRIMARY KEY (`id`) )<br />ENGINE = InnoDB;<br /><br /><br />CREATE  TABLE IF NOT EXISTS `exemplo01`.`telefone` (<br />  `id` INT NOT NULL AUTO_INCREMENT ,<br />  `tipo_id` INT NULL ,<br />  `contato_id` INT NULL ,<br />  `ddd` VARCHAR(2) NULL ,<br />  `fone` VARCHAR(20) NULL ,<br />  PRIMARY KEY (`id`) ,<br />  INDEX `fk_telefone_contato` (`contato_id` ASC) ,<br />  INDEX `fk_telefone_tipo` (`tipo_id` ASC) ,<br />  CONSTRAINT `fk_telefone_contato`<br />    FOREIGN KEY (`contato_id` )<br />    REFERENCES `exemplo01`.`contato` (`id` )<br />    ON DELETE NO ACTION<br />    ON UPDATE NO ACTION,<br />  CONSTRAINT `fk_telefone_tipo`<br />    FOREIGN KEY (`tipo_id` )<br />    REFERENCES `exemplo01`.`tipo` (`id` )<br />    ON DELETE NO ACTION<br />    ON UPDATE NO ACTION)<br />ENGINE = InnoDB;

Configure a conexão com o banco de dados nos arquivos: config/databases.yml e config/propel.ini

databases.yml

all:<br />  propel:<br />    class:          sfPropelDatabase<br />    param:<br />      phptype:      mysql<br />      hostspec:     localhost<br />      database:     exemplo01<br />      username:     root<br />      password:     <br />      encoding:     utf8<br />      persistent:   false

propel.ini

Configurar as linhas abaixo, onde exemplo01 é o nome do bd que criamos.

propel.database.createUrl  = mysql://root@localhost/<br />propel.database.url        = mysql://root@localhost/exemplo01

Vamos gerar o schema.yml (mapeamento objeto-relacional) e os models:

Para gerar o schema.yml:

symfony propel-build-schema

Para gerar os modelos:

symfony propel-build-model

Criar os módulos:

symfony init-module frontend contato

symfony init-module frontend tipo

symfony init-module frontend telefone

e criar as actions necessárias para o funcionamento da aplicação:

apps/frontend/modules/contato/actions/actions.class.php

class contatoActions extends sfActions {<br /><br /><br />   /**<br />    * Exibe os contatos cadastrados<br />    */<br />    public function executeIndex() {<br /><br />        $c = new Criteria();<br />        $c->addAscendingOrderByColumn(ContatoPeer::NOME);<br />        $this->contatos = ContatoPeer::doSelect($c);<br />    }<br /><br /><br />    /**<br />     * Edita/Novo Contato<br />     */<br />    public function executeEditar() {<br /><br />        if($this->getRequestParameter('id')) {<br />            $this->contato = ContatoPeer::retrieveByPk($this->getRequestParameter('id'));<br />            $this->forward404Unless($this->contato);<br />            $this->acao    = 'Editar Contato';<br />        }<br />        else {<br />            $this->contato = new Contato();<br />            $this->acao    = 'Novo Contato';<br />        }<br />    }<br /><br /><br />    /**<br />     * Atualiza os dados do contato<br />     */<br />    public function executeAtualizar() {<br />       if($this->getRequestParameter('id')) {<br />            $contato = ContatoPeer::retrieveByPk($this->getRequestParameter('id'));<br />            $this->forward404Unless($contato);<br />        }<br />        else {<br />            $contato = new Contato();<br />        }<br /><br />        $contato->setNome($this->getRequestParameter('nome'));<br />        $contato->save();<br /><br />        $this->forward('contato', 'index');<br />    }<br /><br /><br />    /**<br />     * Exclui um contato<br />     */<br />    public function executeExcluir()<br />    {<br />        $contato = ContatoPeer::retrieveByPk($this->getRequestParameter('id'));<br />        $this->forward404Unless($contato);<br />        $contato->delete();<br />        $this->forward('contato', 'index');<br />    }<br /><br />}

apps/frontend/modules/telefone/actions/actions.class.php

class telefoneActions extends sfActions<br />{<br /><br />    /**<br />    * Exibe os telefones cadastrados<br />    */<br />    public function executeIndex() {<br /><br />        $c = new Criteria();<br />        $c->addAscendingOrderByColumn(TelefonePeer::CONTATO_ID);<br />        $c->addAscendingOrderByColumn(TelefonePeer::TIPO_ID);<br />        $this->telefones = TelefonePeer::doSelect($c);<br /><br />    }<br /><br /><br />    /**<br />     * Edita/Novo Telefone<br />     */<br />    public function executeEditar() {<br /><br />        if($this->getRequestParameter('id')) {<br />            $this->telefone = TelefonePeer::retrieveByPk($this->getRequestParameter('id'));<br />            $this->forward404Unless($this->telefone);<br />            $this->acao    = 'Editar Telefone';<br />        }<br />        else {<br />            $this->telefone = new Telefone();<br />            $this->acao = 'Novo Telefone';<br />        }<br /><br />        //contatos<br />        $c = new Criteria();<br />        $c->addAscendingOrderByColumn(ContatoPeer::NOME);<br />        $this->contatos = ContatoPeer::doSelect($c);<br /><br />        //tipos<br />        $c = new Criteria();<br />        $c->addAscendingOrderByColumn(TipoPeer::TIPO);<br />        $this->tipos = TipoPeer::doSelect($c);<br />    }<br /><br /><br />    /**<br />     * Atualiza os dados do telefone<br />     */<br />    public function executeAtualizar() {<br />        if($this->getRequestParameter('id')) {<br />            $telefone = TelefonePeer::retrieveByPk($this->getRequestParameter('id'));<br />            $this->forward404Unless($telefone);<br />        }<br />        else {<br />            $telefone = new Telefone();<br />        }<br /><br />        $telefone->setTipoId($this->getRequestParameter('tipo_id'));<br />        $telefone->setContatoId($this->getRequestParameter('contato_id'));<br />        $telefone->setDdd($this->getRequestParameter('ddd'));<br />        $telefone->setFone($this->getRequestParameter('fone'));<br />        $telefone->save();<br /><br />        $this->forward('telefone', 'index');<br />    }<br /><br /><br />    /**<br />     * Exclui um telefone<br />     */<br />    public function executeExcluir() {<br />        $telefone = TelefonePeer::retrieveByPk($this->getRequestParameter('id'));<br />        $this->forward404Unless($telefone);<br />        $telefone->delete();<br />        $this->forward('telefone', 'index');<br />    }<br /><br />}

apps/frontend/modules/tipo/actions/actions.class.php

class tipoActions extends sfActions {<br /><br />   /**<br />    * Exibe os tipos cadastrados<br />    */<br />    public function executeIndex() {<br /><br />        $c = new Criteria();<br />        $c->addAscendingOrderByColumn(TipoPeer::TIPO);<br />        $this->tipos = TipoPeer::doSelect($c);<br />    }<br /><br /><br />    /**<br />     * Edita/Novo Tipo<br />     */<br />    public function executeEditar() {<br /><br />        if($this->getRequestParameter('id')) {<br />            $this->tipo = TipoPeer::retrieveByPk($this->getRequestParameter('id'));<br />            $this->forward404Unless($this->tipo);<br />            $this->acao    = 'Editar Tipo';<br />        }<br />        else {<br />            $this->tipo = new Tipo();<br />            $this->acao = 'Novo Tipo';<br />        }<br />    }<br /><br /><br />    /**<br />     * Atualiza os dados do tipo<br />     */<br />    public function executeAtualizar() {<br />        if($this->getRequestParameter('id')) {<br />            $tipo = TipoPeer::retrieveByPk($this->getRequestParameter('id'));<br />            $this->forward404Unless($tipo);<br />        }<br />        else {<br />            $tipo = new Tipo();<br />        }<br /><br />        $tipo->setTipo($this->getRequestParameter('tipo'));<br />        $tipo->save();<br /><br />        $this->forward('tipo', 'index');<br />    }<br /><br /><br />    /**<br />     * Exclui um tipo<br />     */<br />    public function executeExcluir() {<br />        $tipo = TipoPeer::retrieveByPk($this->getRequestParameter('id'));<br />        $this->forward404Unless($tipo);<br />        $tipo->delete();<br />        $this->forward('tipo', 'index');<br />    }<br />}

Agora vamos criar os templates referentes às actions:

apps/frontend/modules/contato/templates/indexSuccess.php

<?php include_partial('global/menu') ?><br /><br /><?php echo button_to('Novo contato', 'contato/editar') ?><br /><br /><table><br />    <thead><br />        <tr><br />            <td>Id</td><br />            <td>Nome</td><br />            <td>Opções</td><br />        </tr><br />    </thead><br /><br />    <tbody><br />        <?php<br />        if($contatos) { ?><br />        <?php foreach($contatos as $contato) { ?><br />        <tr><br />            <td><?php echo $contato->getId() ?></td><br />            <td><?php echo $contato->getNome() ?></td><br />            <td><?php echo link_to('editar', 'contato/editar?id=' . $contato->getId() ) ?>  <br />                <?php echo link_to('excluir', 'contato/excluir?id=' . $contato->getId(), '?confirm=Deseja mesmo excluir?' ) ?></td><br />        </tr><br />        <?php<br />        }<br />        }<br />        else { ?><br />           <tr><br />               <td colspan="3">Nenhum contato cadastrado!</td><br />           </tr><br />        <?php } ?><br />    </tbody><br /></table>

apps/frontend/modules/contato/templates/editarSuccess.php

<?php include_partial('global/menu') ?><br /><br /><?php use_helper('Object') ?><br /><br /><?php echo form_tag('contato/atualizar') ?><br /><?php echo object_input_hidden_tag($contato, 'getId') ?><br /><br /><table><br />    <thead><br />        <tr><br />            <td colspan="2"><?php echo $acao ?></td><br />        </tr><br />    </thead><br /><br />    <tbody><br />       <tr><br />          <td>Nome:</td><br />          <td><?php echo object_input_tag($contato, 'getNome') ?></td><br />       </tr><br /><br />       <tr><br />           <td colspan="2"><?php echo submit_tag('Gravar') ?>  <br />           <?php echo button_to('Cancelar', 'telefone/index', array('confirm'=>'Deseja mesmo cancelar?')) ?></td><br />       </tr><br />    </tbody><br /></table>

apps/frontend/modules/telefone/templates/indexSuccess.php

<?php include_partial('global/menu') ?><br /><br /><?php echo button_to('Novo telefone', 'telefone/editar') ?><br /><br /><table><br />    <thead><br />        <tr><br />            <td>Id</td><br />            <td>Contato</td><br />            <td>Tipo</td><br />            <td>Fone</td><br />        </tr><br />    </thead><br /><br />    <tbody><br />        <?php<br />        if($telefones) { ?><br />        <?php foreach($telefones as $telefone) { ?><br />        <tr><br />            <td><?php echo $telefone->getId() ?></td><br />            <td><?php echo $telefone->getContato()->getNome() ?></td><br />            <td><?php echo $telefone->getTipo()->getTipo() ?></td><br />            <td><?php echo link_to('editar', 'telefone/editar?id=' . $telefone->getId() ) ?>  <br />                <?php echo link_to('excluir', 'telefone/excluir?id=' . $telefone->getId(), '?confirm=Deseja mesmo excluir?' ) ?></td><br />        </tr><br />        <?php<br />        }<br />        }<br />        else { ?><br />           <tr><br />               <td colspan="4">Nenhum telefone cadastrado!</td><br />           </tr><br />        <?php } ?><br />    </tbody><br /></table>

apps/frontend/modules/telefone/templates/editarSuccess.php

<?php include_partial('global/menu') ?><br /><br /><?php use_helper('Object') ?><br /><br /><?php echo form_tag('telefone/atualizar') ?><br /><?php echo object_input_hidden_tag($telefone, 'getId') ?><br /><br /><table><br />    <thead><br />        <tr><br />            <td colspan="2"><?php echo $acao ?></td><br />        </tr><br />    </thead><br /><br />    <tbody><br />       <tr><br />          <td>Contato:</td><br />          <td><?php echo select_tag(<br />              'contato_id',<br />              objects_for_select(<br />                              $contatos,<br />                              'getId',<br />                              'getNome',<br />                              $telefone->getContatoId(),<br />                              'include_custom=--Selecione--'<br />                           )<br />               ) ?></td><br />       </tr><br /><br />       <tr><br />          <td>Tipo:</td><br />          <td><?php echo select_tag(<br />              'tipo_id',<br />              objects_for_select(<br />                              $tipos,<br />                              'getId',<br />                              'getTipo',<br />                              $telefone->getTipoId(),<br />                              'include_custom=--Selecione--'<br />                           )<br />               ) ?></td><br />       </tr><br /><br />       <tr><br />          <td>DDD:</td><br />          <td><?php echo object_input_tag($telefone, 'getDdd', array('size'=>'2')) ?></td><br />       </tr><br /><br />       <tr><br />          <td>Telefone:</td><br />          <td><?php echo object_input_tag($telefone, 'getFone', array('size'=>'15')) ?></td><br />       </tr><br /><br />       <tr><br />           <td colspan="2"><?php echo submit_tag('Gravar') ?>  <br />           <?php echo button_to('Cancelar', 'telefone/index', array('confirm'=>'Deseja mesmo cancelar?')) ?><br />           </td><br />       </tr><br />    </tbody><br /></table>

apps/frontend/modules/tipo/templates/indexSuccess.php

<?php include_partial('global/menu') ?><br /><br /><?php echo button_to('Novo tipo', 'tipo/editar') ?><br /><br /><table><br />    <thead><br />        <tr><br />            <td>Id</td><br />            <td>Tipo</td><br />            <td>Opções</td><br />        </tr><br />    </thead><br /><br />    <tbody><br />        <?php<br />        if($tipos) { ?><br />        <?php foreach($tipos as $tipo) { ?><br />        <tr><br />            <td><?php echo $tipo->getId() ?></td><br />            <td><?php echo $tipo->getTipo() ?></td><br />            <td><?php echo link_to('editar', 'tipo/editar?id=' . $tipo->getId() ) ?>  <br />                <?php echo link_to('excluir', 'tipo/excluir?id=' . $tipo->getId(), '?confirm=Deseja mesmo excluir?' ) ?></td><br />        </tr><br />        <?php<br />        }<br />        }<br />        else { ?><br />           <tr><br />               <td colspan="3">Nenhum tipo cadastrado!</td><br />           </tr><br />        <?php } ?><br />    </tbody><br /></table>

apps/frontend/modules/tipo/templates/editarSuccess.php

<?php include_partial('global/menu') ?><br /><br /><?php use_helper('Object') ?><br /><br /><?php echo form_tag('tipo/atualizar') ?><br /><?php echo object_input_hidden_tag($tipo, 'getId') ?><br /><br /><table><br />    <thead><br />        <tr><br />            <td colspan="2"><?php echo $acao ?></td><br />        </tr><br />    </thead><br /><br />    <tbody><br />       <tr><br />          <td>Nome:</td><br />          <td><?php echo object_input_tag($tipo, 'getTipo') ?></td><br />       </tr><br /><br />       <tr><br />           <td colspan="2"><?php echo submit_tag('Gravar') ?>  <br />           <?php echo button_to('Cancelar', 'tipo/index', array('confirm'=>'Deseja mesmo cancelar?')) ?></td><br />       </tr><br />    </tbody><br /></table>

apps/frontend/templates/_menu.php

<div id="menu"><br /><ul><br />    <li><span><?php echo link_to('Contato', 'contato/index') ?></span></li><br />    <li><span><?php echo link_to('Tipo', 'tipo/index') ?></span></li><br />    <li><span><?php echo link_to('Telefone', 'telefone/index') ?></span></li><br /></ul><br /></div>

Alterar em apps/frontend/config/routing.yml a action que será executada em primeira instância.

homepage:<br />  url:   /<br />  param: { module: default, action: index }

mudar para:

homepage:<br />  url:   /<br />  param: { module: contato, action: index }

Criei um arquivo chamado _menu.php que será o menu do sistema e será incluído em todas as páginas. Nos templates, para efetuar a inclusão utilizaremos o include_partial(). Os partials são pedaços de códigos reutilizáveis, são armazenados na pasta templates/. Um arquivo partial SEMPRE começa com sublinhado (_), isso ajuda a distiguir os arquivos, já que eles ficam no mesmo diretório. Ele pode ser incluído no mesmo módulo, em outro módulo, ou no diretório global templates/.

Edite o arquivo apps/frontend/config.yml para alterar os dados do projeto como title, description e keywords. No caso do nosso projeto, deixei da seguinte maneira:

de:

metas:<br />    title:        symfony project<br />    robots:       index, follow<br />    description:  symfony project<br />    keywords:     symfony, project<br />    language:     en

para:

metas:<br />    title:        Crud Symfony - iMasters<br />    robots:       index, follow<br />    description:  Exemplo de criaçao de um crud manual<br />    keywords:     crud, symfony, imasters<br />    language:     en

Limpe o cache do symfony

symfony cc

Vamos abrir o browser e ver como está ficando o nosso “pequeno” sistema. Veja que está sem layout mas está funcional, incluindo, alterando e excluindo os dados.

http://127.0.0.1/imasters/web/frontend_dev.php/

Último passo será mexermos no CSS para criar uma apresentação mais amigável. Abra o arquivo: web/css/main.css

ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,body,html,p,blockquote,fieldset,input<br />{ margin: 0; padding: 0; }<br />a img,:link img,:visited img { border: none; }<br /><br />ol, ul, li {list-style: none;}<br /><br />a<br />{<br />  text-decoration: none;<br />}<br /><br />a:hover<br />{<br />  text-decoration: underline;<br />}<br /><br />body, td<br />{<br />  margin: 0;<br />  padding: 0;<br />  font-family: Arial, Verdana, sans-serif;<br />  font-size: 11px;<br />}<br /><br />body<br />{<br />  padding: 20px;<br />}<br /><br /><br />/*MENU*/<br />#menu{background:#666666;height:24px;}<br />#menu li{float:left;position:relative;}<br />#menu li a { color:#fff;}<br />#menu li span{display:block; font:bold 14px arial;line-height:24px;width:122px; text-align:center; background:#8F8F8F; cursor:pointer;margin-left:10px}<br />#menu li ul{overflow:hidden;position:absolute;z-index:10000;}<br />#menu li ul li{float:none;display:block; font:bold 12px arial;line-height:18px;width:120px; text-align:center; background:#fff; cursor:pointer;margin-left:10px; border:1px solid #CCCCCC; margin-top:2px;}<br />#menu li ul li a{ color:#8F8F8F;}<br /><br />/*TABELAS*/<br />thead tr{background: #A2090C; }<br />thead td{text-align:center;color:#fff;font:bold 11px arial;padding:3px 0;}<br />tbody td{padding:3px;}<br />table {width:100%; margin-top:15px; *border-collapse: collapse;}<br />table .nivel_2{background:#ccc;}<br />table .nivel_2 td{color:#000;text-align:left; padding:3px;}<br />table tbody td{border-bottom:1px solid #ccc;}<br />table tbody td.center{text-align:center}

Veja como ficou o sistema após as alterações no CSS:

Lista os contatos.Lista os contatos.

Novo contatoNovo contato

Novo telefoneNovo telefone

Links úteis:

http://www.symfony-project.org/book/1_0/

Download do sistema

Para efetuar o download dos arquivos, clique AQUI

ATENÇÃO

Se for utilizado o arquivo acima, não se esqueça de mudar o arquivo config/config.php que indica o caminho do symfony.

Semana que vem tem mais. Espero que vocês tenham gostado!

Não deixe de nos enviar críticas ou sugestões para o próximo assunto, afinal, a coluna é de vocês.

atualmente presta serviços de WebMaster/Programador para o UOL (http://www.uol.com.br), Graduado em Tecnólogo em Informática pela UNIFIAN - Faculdades Anhanguera e Pós-Graduado em Desenvolvimento de Software para Web pela UFSCAR. Foi o primeiro profissional a chegar à marca de 100 artigos publicados no iMasters e trabalha com PHP desde 2000. Site: http://www.juliocesarmartini.com

Ver perfil