Neste primeiro post de 2010 dedico a atender um pedido feito em um comentário de post passado de mostrar como integrar o Smarty (funcionando tanto com o 2.6.26 quanto com o 3.0b5) nas versões mais novas do Zend Framework. A base usada para fazer ele foi da Documentação Oficial do Zend Framework, fiz poucas alterações na classe que eles criaram.

Em versões anteriores, era necessário explicitamente trocar o Zend_View pelo Smarty no index. Agora, temos a opção de criar uma classe que transforme outro sistema de templates em uma instância da Zend_View, e o melhor: de maneira simples e sem contar com eventuais gambiarras para ‘mascarar’ alguns problemas que dava quando substituíamos.

Optando pelo Smarty

Antes de começar a mexer com Smarty, temos que usar o Autoloader do Zend para poder rodar tudo sem precisar dar includes em todas as páginas, em especial o Bootstrap. Para isso, coloque dentro da sua index, antes de instanciar o Zend_Application:

require_once 'Zend/Loader/Autoloader.php';

$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('My_');

Esse código basicamente tornará todas as classes dentro da pasta library/My visíveis, usando o mesmo esquema de nomeação do Zend. Por exemplo, eu coloquei a classe de integração com o Smarty (que mostrarei mais abaixo) dentro de My/View/Smarty.php, e portanto, devo chamá-la de My_View_Smarty.

Ok, vamos lá: baixe o Smarty e descompacte-o dentro da pasta library.

Altere o Bootstrap, adicionando o método:

protected function _initView()
    {

        // aqui podemos passar quaisquer parâmetros que sejam atributos do objeto smarty
        // se quisermos habilitar o cache, por exemplo:
        $params = array(
                      'caching'        => true,
                      'cache_lifetime' => 60
                  );

        $view = new My_View_Smarty('../application/views/smarty/', $params);
        $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
        $viewRenderer->setView($view)
                     ->setViewBasePathSpec($view->getEngine()->template_dir)
                     ->setViewScriptPathSpec(':controller/:action.:suffix')
                     ->setViewScriptPathNoControllerSpec(':action.:suffix')
                     ->setViewSuffix('tpl');
        Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

    }

Crie o arquivo de implementação do Smarty:

<?php

require_once('Smarty/Smarty.class.php');

class My_View_Smarty implements Zend_View_Interface
{

    /**
     * Smarty object
     * @var Smarty
     */
    protected $_smarty;

    /**
     * Constructor
     *
     * @param string $tmplPath
     * @param array $extraParams
     * @return void
     */
    public function __construct($tmplPath = null, $extraParams = array())
    {
        $this->_smarty = new Smarty();

        if (null !== $tmplPath) {
            $this->setScriptPath($tmplPath);
        }

        foreach ($extraParams as $key => $value) {
            $this->_smarty->$key = $value;
        }
    }

    /**
     * Return the template engine object
     *
     * @return Smarty
     */
    public function getEngine()
    {
        return $this->_smarty;
    }

    /**
     * Set the path to the templates
     *
     * @param string $path The directory to set as the path.
     * @return void
     */
    public function setScriptPath($path)
    {

        if (is_readable($path)) {
            $this->_smarty->template_dir = $path."templates";
            $this->_smarty->compile_dir  = $path."templates_c";
            $this->_smarty->cache_dir    = $path."cache";
            $this->_smarty->config_dir   = $path."config";
            return;
        }

        throw new Exception('Invalid path provided');
    }

    /**
     * Retrieve the current template directory
     *
     * @return string
     */
    public function getScriptPaths()
    {
        return array($this->_smarty->template_dir);
    }

    /**
     * Alias for setScriptPath
     *
     * @param string $path
     * @param string $prefix Unused
     * @return void
     */
    public function setBasePath($path, $prefix = 'Zend_View')
    {
        return $this->setScriptPath($path);
    }

    /**
     * Alias for setScriptPath
     *
     * @param string $path
     * @param string $prefix Unused
     * @return void
     */
    public function addBasePath($path, $prefix = 'Zend_View')
    {
        return $this->setScriptPath($path);
    }

    /**
     * Assign a variable to the template
     *
     * @param string $key The variable name.
     * @param mixed $val The variable value.
     * @return void
     */
    public function __set($key, $val)
    {
        $this->_smarty->assign($key, $val);
    }

    /**
     * Allows testing with empty() and isset() to work
     *
     * @param string $key
     * @return boolean
     */
    public function __isset($key)
    {
        return (null !== $this->_smarty->get_template_vars($key));
    }

    /**
     * Allows unset() on object properties to work
     *
     * @param string $key
     * @return void
     */
    public function __unset($key)
    {
        $this->_smarty->clear_assign($key);
    }

    /**
     * Assign variables to the template
     *
     * Allows setting a specific key to the specified value, OR passing
     * an array of key => value pairs to set en masse.
     *
     * @see __set()
     * @param string|array $spec The assignment strategy to use (key or
     * array of key => value pairs)
     * @param mixed $value (Optional) If assigning a named variable,
     * use this as the value.
     * @return void
     */
    public function assign($spec, $value = null)
    {
        if (is_array($spec)) {
            $this->_smarty->assign($spec);
            return;
        }

        $this->_smarty->assign($spec, $value);
    }

    /**
     * Clear all assigned variables
     *
     * Clears all variables assigned to Zend_View either via
     * {@link assign()} or property overloading
     * ({@link __get()}/{@link __set()}).
     *
     * @return void
     */
    public function clearVars()
    {
        $this->_smarty->clear_all_assign();
    }

    /**
     * Processes a template and returns the output.
     *
     * @param string $name The template to process.
     * @return string The output.
     */
    public function render($name)
    {
        return $this->_smarty->fetch($name);
    }

}

Para funcionar, basta criar as pastas de templates, cache e configs usadas pelo Smarty.

Exemplo de execução

O controller não fica muito diferente do que já era com o Zend_View comum… usando o controller index por exemplo:

    public function indexAction()
    {
        // cria algumas variáveis
        $valor1 = mt_rand(0,100);
        $valor2 = array(
            array('id' => '1', 'titulo' => 'Teste 1'),
            array('id' => '2', 'titulo' => 'Teste 2'),
            array('id' => '3', 'titulo' => 'Teste 3')
        );
        // inclui as variáveis na view
        $this->view->assign('valor1', $valor1);
        $this->view->assign('valor2', $valor2);
    }

No arquivo views/smarty/index/index.tpl, teríamos:

<p>{$valor1}</p>
<table>
    <tr>
        <th>Código</th>
        <th>Título</th>
    </tr>
    {foreach from=$valor2 item=registro}
    <tr style="background-color: {cycle values="#ff0000,#00ff00"}">
        <td>{$registro.id}</td>
        <td>{$registro.titulo}</td>
    </tr>
    {/foreach}
</table>

Espero que tenham gostado do tutorial e que tenha facilitado a vida dos amantes de Smarty e ZF!

Posted in php at janeiro 2nd, 2010. 8 Comments.

Já falei bastante aqui sobre o Zend Framework, mas estava fazendo muita falta ao menos um que falasse sobre banco de dados, não é mesmo? Nesse artigo vou falar sobre a integração do framework com o MySQL, por ser um dos mais utilizados com o PHP.

Requisitos

Para conectar ao banco, utilizarei o pdo_mysql. Para isso, é necessário habilitar no php.ini as extensões php_pdo e php_pdo_mysql.

Configurações do banco

Tudo ok no php, é necessário agora indicar pro framework como ele conectará ao mysql. Para facilitar o processo, já tem criada uma classe de Resource (parte do módulo Zend_Application, para carregar funcionalidades com o bootstrap) pronta para que o Zend_Db crie a conexão, bastando então alterar o arquivo de configurações (se criado pelo Zend_Tool, estará em application/configs/application.ini), adicionando com as suas informações:

resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "username"
resources.db.params.password = "password"
resources.db.params.dbname = "database"

Pronto, temos uma conexão com o banco funcionando.

O Banco de Dados

Como a idéia é mostrar o funcionamento do Zend_Db com o MySQL, farei um exemplo simples com um crud de usuários. Então, será um banco simples:

CREATE TABLE IF NOT EXISTS `user` (
  `usr_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `usr_name` varchar(255) NOT NULL,
  `usr_register` date NOT NULL,
  PRIMARY KEY (`usr_id`)
)

Models

Antes de ir para a parte realmente importante, é recomendável usar o Module_Autoloader para não precisar ficar cheio de includes em todas as nossas classes tanto de banco quanto de negócio. Então, inclua no arquivo application/Bootstrap.php:

protected function _initAutoload()
    {
        $autoloader = new Zend_Application_Module_Autoloader(array(
            'namespace' => 'Default_',
            'basePath'  => dirname(__FILE__),
        ));
        return $autoloader;
    }

Para usar o Zend_Db, usarei uma abordagem semelhante ao manual do framework, de forma que separaremos as classes de negócio das classes de tabela com o uso do pattern Data Mapper. Da forma que criei, as classes de negócio devem ficar na pasta application/models, os mappers na pasta application/models/Mapper e as classes de tabela em application/models/Table.

Primeiro, a classe para a tabela de usuários (application/models/Table/User.php):

<?php

class Default_Model_Table_User extends Zend_Db_Table_Abstract {

    protected $_name = 'user';
    protected $_primary = 'usr_id';

}

Vamos às explicações:

  1. o nome: por conta do autoloader que foi colocado no Bootstrap, é necessário nomear que nem as próprias classes do framework, com base nas pastas… como o arquivo está em application/models/Table/User.php, então deve ficar Default_Model_Table_User
  2. todas as classes de tabela devem estender Zend_Db_Table_Abstract
  3. o nome da tabela deve ser obrigatoriamente setado em $_name. A chave primária não é obrigatória pois o Zend é capaz de descobrir, mas por via das dúvidas eu coloquei.

Em seguida, temos que configurar o mapper, a classe que faz a comunicação entre as classes de negócio e de tabela. Primeiro, uma classe abstrata que eu fiz para evitar repetições em sistemas maiores, e contém o que todos os mappers deveriam ter:

<?php

abstract class Default_Model_Mapper_Abstract
{

    // liga o mapper à classe de tabela
    protected $_dbTable;
    // indica o nome da entidade (ex.: User), que é usado tanto no
    // modelo quanto na classe de tabela
    protected $_model;

    public function getDbTable()
    {
        if (null === $this->_dbTable)
        {
            $this->setDbTable("Default_Model_Table_".$this->getModel());
        }
        return $this->_dbTable;
    }   

    public function getModel()
    {
        if (null === $this->_model)
        {
            // ok, eu sei que isso daria erro... mas é justamente para forçar o erro
            // para saber que faltou setar o modelo no mapper
            $this->setModel('Abstract');
        }
        return $this->_model;
    }

    public function setDbTable($dbTable)
    {
        if (is_string($dbTable))
        {
            $dbTable = new $dbTable();
        }
        if (!$dbTable instanceof Zend_Db_Table_Abstract)
        {
            throw new Exception('Invalid table data gateway provided');
        }
        $this->_dbTable = $dbTable;
        return $this;
    }

    public function setModel($model)
    {
        $this->_model = $model;
    }

}

Não vou comentar muito porque acho que os comentários já explicam… agora uma implementação para o mapper do usuario:

<?php

class Default_Model_Mapper_User extends Default_Model_Mapper_Abstract {

    public function __construct()
    {
        $this->setModel("User");
    }

    public function add( Default_Model_User $user )
    {

        $data = array(
            'usr_name'     => $user->getName(),
            'usr_register' => new Zend_Db_Expr('NOW()')
        );

        return $this->getDbTable()->insert($data);

    }

    public function edit( Default_Model_User $user )
    {

        $data = array(
            'usr_name'   => $user->getName()
        );

        return $this->getDbTable()->update($data, array('usr_id = ?' => $user->getId()));

    }

    public function remove( $id )
    {

        return $this->getDbTable()->delete( array('usr_id = ?' => $id) );

    }

    public function find($id, Default_Model_User $user)
    {
        $result = $this->getDbTable()->find($id);
        if (0 == count($result)) {
            return;
        }
        $row = $result->current();
        $user->setId($row->usr_id);
        $user->setName($row->usr_name);
        $user->setRegister($row->usr_register);

        return $user;
    }

    public function fetchAll()
    {

        $table = $this->getDbTable();

        $resultSet = $this->getDbTable()->fetchAll();
        $entries   = array();

        foreach ($resultSet as $row) {
            $entry = new Default_Model_User();
            $entry->setId($row->usr_id);
            $entry->setName($row->usr_name);
            $entry->setRegister($row->usr_register);
            $entry->setMapper($this);
            $entries[] = $entry;
        }
        return $entries;

    }

}

Vendo o código, podemos perceber algumas coisas:

  1. através do método getDbTable(), podemos fazer várias operações de bancos de dados, tanto as da classe Zend_Db_Table quanto as do Zend_Db_Select (que no caso eu não usei)
  2. ao invés de fazer esse tipo de operação dentro da própria classe de negócio, é retornado um objeto da mesma: isso é muito útil em questão de manutenção, já que se precisarmos alterar algo no banco de dados ou até mesmo substituí-lo por outro componente, não precisaremos mexer na classe de negócio
  3. Graças a indução de tipo, o serviço do item passado torna-se bem mais fácil!

Agora só falta a classe de negócios: assim como os mappers, eu fiz uma classe abstrata. Ela ficou assim:

>?php

abstract class Default_Model_Abstract {

    // define a classe de mapper ligada ao model
    protected $_mapper;
    // indica qual o modelo usado
    protected $_model;

    public function __construct(array $options = null)
    {
        if (is_array($options))
        {
            $this->setOptions($options);
        }
    }

    public function __get($name)
    {
        $method = 'get' . $name;
        if (('mapper' == $name) || !method_exists($this, $method))
        {
            throw new Exception('Invalid property');
        }
        return $this->$method();
    }

    public function getMapper()
    {
        if (null === $this->_mapper)
        {
            $model = "Default_Model_Mapper_".$this->getModel();
            $this->setMapper(new $model());
        }
        return $this->_mapper;
    }

    public function getModel()
    {
        if (null === $this->_model)
        {
            $this->setModel('Abstract');
        }
        return $this->_model;
    }

    public function __set($name, $value)
    {
        $method = 'set' . $name;
        if (('mapper' == $name) || !method_exists($this, $method))
        {
            throw new Exception('Invalid property');
        }
        $this->$method($value);
    }

    public function setOptions(array $options)
    {
        $methods = get_class_methods($this);
        foreach ($options as $key => $value)
        {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods))
            {
                $this->$method($value);
            }
        }
        return $this;
    }

    public function setMapper($mapper)
    {
        $this->_mapper = $mapper;
        return $this;
    }

    public function setModel($model)
    {
        $this->_model = $model;
    }

    public function add()
    {
        $this->getMapper()->add($this);
    }

    public function edit()
    {
        $this->getMapper()->edit($this);
    }

    public function remove()
    {
        $this->getMapper()->remove($this->getId());
    }

    public function find($id)
    {
        $this->getMapper()->find($id, $this);
        return $this;
    }

    public function fetchAll()
    {
        return $this->getMapper()->fetchAll();
    }

}

Acho que não há muito o que comentar, é a mesma idéia da classe abstrata do mapper. A única observação a se fazer é que os métodos abaixo dos setters e getters poderiam estar na classe que estenderá, mas eu preferi já deixar aí para não dar mais trabalho depois.

A última classe (finalmente!) é a de usuários:

<?php

class Default_Model_User extends Default_Model_Abstract {

    protected $id;
    protected $name;
    protected $register;

    public function __construct(array $options = null)
    {
        $this->setModel("User");

        parent::__construct($options);
    }

    public function getId() {
        return $this->id;
    }

    public function getName() {
        return $this->name;
    }

    public function getRegister() {
        return $this->register;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function setName($name) {
        $this->name = $name;
    }

    public function setRegister($register) {
        $this->register = $register;
    }

}

Simples não? uma classe apenas com os atributos básicos, setters e getters.

Concluindo

É isso ae, foi apenas uma de tantas possibilidades que deve haver para conectar ao banco. Acredito que essa seja uma boa opção para quem quer aproveitar os recursos que o framework implementou no Zend_Application.

Posted in php at dezembro 13th, 2009. 4 Comments.

Meio que uma continuação do post passado, vou abordar um pouco mais do Zend_View, o componente do Zend Framework responsável pela camada de Visão do modelo MVC implementado.

Obs.:  Este post está sendo feito com base na versão 1.9.4 do framework.

Um Simples Exemplo

Simples mesmo: passar uma informação do controller para a view.

No Controller, dentro da Action desejada:

// cria algumas variáveis
$valor1 = 123.45;
$valor2 = array(
    array('id' => '1', 'titulo' => 'Teste 1'),
    array('id' => '2', 'titulo' => 'Teste 2'),
    array('id' => '3', 'titulo' => 'Teste 3')
);
// inclui as variáveis na view
// podemos atribuir o valor como objeto
$this->view->valor1 = $valor1;
// ou usar o método assign... assign('nome dentro da view', $valor);
$this->view->assign('valor2', $valor2);

E na view:

<p><?php echo $this->valor1 ?></p>
<table>
    <tr>
        <th>Código</th>
        <th>Título</th>
    </tr>
    <?php foreach ($this->valor2 as $registro){ ?>
    <tr>
        <td> <?php echo $this->escape($registro['id']); ?>
        <td> <?php echo $this->escape($registro['titulo']); ?>
    </tr>
    <?php } ?>
</table>

Ou seja: no Controller indicamos quais variáveis queremos que a instância do Zend_View “enxergue”, de maneira que na view só precisamos dar um escape para mostar. Esse escape, por padrão, é o htmlspecialchars()  para evitar que os valores das variáveis causem alguma quebra no html.

View Helpers

Como foi visto no exemplo, as coisas são bem simples: setamos variáveis e usamos-as na view. Mas o problema é: e se quisermos fazer algo mais complexo, como por exemplo, alterar a cor das linhas da tabela do nosso primeiro exemplo?

Para evitar soluções como, por exemplo, criar um contador com um if para verificar se é par ou impar, temos os helpers, que são implementações em classes de funcionalidades complexas que não queremos colocar explicitamente na view.

Neste caso, já temos o Zend_View_Helper_Cycle, um helper já implementado. Assim, basta alterar uma única linha:

<tr style="background-color: <?php echo $this->cycle(array("#FF0000","#00FF00"))->next()?>">

Apenas com um comando dentro do <tr>, o nosso exemplo já nos mostra uma tabela que alterna o fundo em vermelho e verde.

Esse é apenas um dos já implementados: caso queira aprender quais são e como usá-los, recomendo dar uma olhada na referência oficial dos View Helpers. Não gostaria de colocar todos aqui porque qualquer explicação seria mera repetição, e minha intenção mesmo é mostrar a criação de um helper.

Então vamos lá: usando o primeiro exemplo, estamos mostrando um número, mas queremos mostrá-lo como um preço. Para isso, contaremos com o Zend_Currency, que é um componente próprio para tratar disso, mas como não queremos fazer isso no controller, vamos facilitar nossa vida e chamar esse helper.

Na instalação, o framework já nos deixou uma pasta própria para ele em application/views/helpers/ e é nele que deve ser criado o seguinte arquivo:

<?php

class Zend_View_Helper_Currency extends Zend_View_Helper_Abstract
{

    /*
     * por convenção do framework, o método deve ser o nome da
     * classe (Currency) em minúsculo
     */

    public function currency($valor)
    {

        // instanciamos a classe que trata, explicitando o locale
        $currency = new Zend_Currency('pt_BR');
        // devemos SEMPRE retornar valores, nunca mostrar na saída
        return $currency->toCurrency($valor);

    }

}

Eu não vou mudar muito o foco para o Zend_Currency, mas só para não deixar dúvidas, o método retorna o valor formatado em reais, com o formato português do Brasil.

Feito isso, mudamos na view apenas a linha que chamamos o $valor1:

<p><?php echo $this->currency($this->valor1); ?></p>

Deve ser mostrado na tela algo como ‘R$123,45′.

Obs.: Muitas pessoas (inclusive eu) acham prático criar uma pasta dentro do library com todas as classes próprias que não façam parte do Controller e do Model. Caso queira deixar seus helpers dentro de uma pasta dessas, crie um método no Bootstrap instanciando o Zend_View e use o método addHelperPath para que o framework encontre-os:

protected function _initView()
    {
        $view = new Zend_View();
        $view->addHelperPath('path/to/my/view/helper', 'My_View_Helper');

        $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
        $viewRenderer->setView($view);
        Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

        return $view;
    }

Conclusão

Vimos que o  Zend_View é um poderoso componente de templates que permite de maneira muito simples a incorporação de funções complexas novas. Apesar de ter sido uma visão superficial, baseada nos view helpers, já é possível criar uma camada de visão de maneira limpa e eficiente.

Mesmo assim, os próximos posts devem complementar algumas das informações passadas aqui, então fiquem atentos!

Posted in php at outubro 25th, 2009. 1 Comment.

Apesar do que eu tinha dito no post anterior, voltarei na idéia de instalação do Zend, já que nas últimas versões o Zend_Tool tem se mostrado uma ótima opção por ser rápido e prático para iniciar projetos, de forma muito mais eficiente que no tutorial passado.

Obs 1.: Desta vez eu fui mais específico quanto a instalação, mas não muito didático quanto a Controllers, Actions, View e o outras idéias do framework. Caso você não conheça muito sobre o framework, recomendo a leitura do meu post Começando com o Zend Framework 1.8 antes de continuar com esse.
Obs 2.: Faço este tutorial supondo que a versão usada no php é igual ou superior ao 5.2.4 e que o módulo mod_rewrite do Apache está habilitado.

Instalação em Windows:

Faça o download da última versão do Zend Framework, e extraia em uma pasta de fácil acesso. No caso do tutorial, eu estou usando a pasta C:\www\ZendFramework-1.9.4-minimal (foi usada a versão 1.9.4, a última versão lançada no momento de criação do tutorial).

Feito isso, precisamos certificar que o caminho para o arquivo .bat usado pelo Zend_Tool está no Path: para isso, vá em Painel de Controle > Sistema > Avançado > Variáveis de ambiente. Em “Variáveis do sistema”, edite o registro “Path”, colocando ao final ;C:\www\ZendFramework-1.9.4-minimal\bin.

Agora no prompt de comando (Iniciar > Executar > cmd), vamos testar se ele está encontrando o Path… digite:

zf show version

Se tudo der certo, ele deve mostrar a versão que será instalada. Se deu erro, certifique-se que o caminho que está no Path é correto.

Agora vá com o prompt até a pasta em que você deseja que seja criada a pasta do novo projeto. No caso, criei ele em C:\www\

cd C:\www\

Agora o comando para criar o projeto… eu o chamei de projeto_zf1.9.4:

zf create project projeto_zf1.9.4

Se tudo ocorreu corretamente, uma pasta com o mesmo nome do projeto foi criada e os arquivos básicos foram criados na estrutura padrão, conforme a imagem:

Estrutura Padrão - Zend Framework

Agora só falta ligar a biblioteca do zend ao projeto. Podemos fazer isso de 2 maneiras: chamando a biblioteca externamente, adicionando-a ao path de execução do php (uma das maneiras de fazer isso é adicionando o caminho da biblioteca dentro do set_include_path() do index.php); ou copiando a pasta da biblioteca (que está em C:\www\ZendFramework-1.9.4-minimal\library) para a pasta library do projeto.

Pronto! Para vê-lo rodando, acesse a pasta /public. Pelo meu exemplo, basta acessar: http://localhost/projeto_zf1.9.4/public

Configurações básicas do Framework

Agora que temos a base já instalada e funcionando, as configurações são dependentes apenas das necessidades do projeto. Vou apresentar algumas dicas e opções que o framework nos proporciona, mas sem me aprofundar muito. Quem sabe depois eu faço alguns posts especificando alguns deles.

  1. Criando Controllers e Actions: Ao contrário do que acontecia antes, não é preciso mais criar controllers e actions manualmente. Isso é realmente uma maravilha, quem leu o post sobre o Zend 1.8, viu que era muito Ctrl+C Ctrl+V, pra criar a classe do Controller, criar o método do Action, depois encontrar a pasta das views e criar o .phtml… enfim, agora tudo isso se resolve com o Zend_Tool, em apenas 2 comandos no prompt (Não se esqueça de ir para a pasta do projeto antes de executar):
    zf create controller nomecontroller

    e

    zf create action nomeaction nomecontroller
  2. Zend_Application: é o pontapé inicial da aplicação. Ele é chamado no index.php com o objetivo de inicializar a aplicação e chamar o Bootstrap. O componente nos permite criar recursos e executar alguns já disponíveis, como é o caso dos controllers, conexão ao banco, etc. Quem precisar criar ou usar recursos que sejam necessários durante a execução de todo o site, precisa saber mais sobre ele.
  3. Zend_Layout: Recurso muito usado com o Zend_View, que possibilita o uso de um layout padrão para todas as páginas. Para usá-lo, basta colocar as seguintes linhas no arquivo de configuração:
    ; nome do arquivo de layout padrão
    resources.layout.layout = "layout"
    ; caminho para uso dos layouts
    resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
    ; seta o encoding do view
    resources.view.encoding = "UTF-8"
    ; seta o caminho das views
    resources.view.basePath = APPLICATION_PATH "/views"

    Um exemplo de Layout (lembrando que deve ser um arquivo .phtml):

    <?php echo $this->doctype('XHTML1_STRICT'); ?>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <?php echo $this->headTitle('Layout Title') ?>
        <?php echo $this->headLink() ?>
        <?php echo $this->headStyle() ?>
        <?php echo $this->headScript() ?>
    </head>
    <body>
        <?php echo $this->layout()->content ?>
    </body>
    </html>
  4. Conexão com o Banco de Dados:  Outro recurso já implementado pelo Zend_Application. Para usá-lo, basta escrever no arquivo de configurações os parâmetros que o Zend_Db precisa para conectar ao banco de dados. Uma configuração para MySQL:
    resources.db.adapter = "pdo_mysql"
    resources.db.params.host = "localhost"
    resources.db.params.username = "usuario"
    resources.db.params.password = "senha"
    resources.db.params.dbname = "nomedobanco"

Conclusão

Certamente faltam detalhes para rodar uma aplicação mesmo que básica a partir dessas informações, mas espero que seja uma boa contribuição para aqueles que procuram uma maneira rápida de ter seu framework pronto para ser usado.

Nos próximos posts devo entrar em usos mais específicos de Zend_Db e Zend_View para podermos enfim criar aplicações mais “ricas”.

Posted in php at outubro 12th, 2009. 9 Comments.

Há muito ouço da necessidade do uso de frameworks para o desenvolvimento, e isso não ocorre a toa: eles facilitam muito essa tarefa por já possuírem a “base” que todos precisamos.

Temos vários exemplos de frameworks para PHP, como CakePHP, Symfony, CodeIgniter e Prado, mas vamos falar dessa vez do Zend, o framework da empresa responsável pelo PHP. Não que isso a torne melhor que as demais, mas tratarei dele porque vejo nele a solução para muitos problemas de maneira flexível e simples. Nesse post, falarei do básico para começar a usá-lo.

Antes de começar

Como a minha intenção nesse artigo não é explicar o MVC, padrão de arquitetura usado não só no Zend, como em outros frameworks, indico 2 links em que o assunto é melhor abordado: Wikipedia e O que é MVC um bicho de 10 cabeças ou produtividade???

É necessário habilitar no Apache o uso de .htacces e o mod_rewrite.

Para isso, abra o httpd.conf e procure a linha:

LoadModule rewrite_module modules/mod_rewrite.so

Se estiver com o famoso ‘jogo da velha’ (#) antes desse código, retire-o.

Agora procure a linha:

<directory "xxx">

onde xxx é a pasta que o Apache enxerga as suas páginas web (por padrão, é a htdocs). Certifique-se de que nas linhas abaixo contenha:

Options Indexes FollowSymLinks
AllowOverride All

Primeiros Passos

Considerando que está tudo configurado no ambiente, baixe o Framework aqui e extraia-o. Agora vamos definir a estrutura de diretórios a ser usada:

Obs.: essa é uma forma quase padrão de diretórios para o Zend. Entretanto, podemos fazer a estrutura da maneira que preferirmos, pois isso é configurado no Bootstrap, que veremos mais a frente.

meuprojeto/
    /application
        /config
        /controllers
        /models
        /views
            /helpers
            /scripts
                /index
    /library
        /Zend
    /public
        /css
        /img
        /js

Nos arquivos que você extraiu, procure pela pasta library/Zend e copie seu conteúdo para a pasta Zend indicada na estrutura.

Bootstrap!

O Bootstap nada mais é que o ponto de partida do nosso projeto, a index.php: todas as urls desse site agora passarão primeiro pelo Bootstrap, que por sua vez chamará o Controller, que chamará a View e os Models, se necessários forem.

O index.php seria:

<?php
// configura as mensagens de erro
error_reporting(E_ALL|E_STRICT);
// chamamos todos os diretórios com classe que possamos usar
// isso é muito vantajoso já que poderemos carregar tudo como se estivesse na mesma pasta
set_include_path('.' . PATH_SEPARATOR . './library'
                     . PATH_SEPARATOR . './application/models'
                     . PATH_SEPARATOR . get_include_path());
// chama a classe do Zend, responsável por carregar as demais
include "Zend/Loader.php";
// carrega as classes necessárias
Zend_Loader::loadClass('Zend_Controller_Front'); // classe de Controllers
Zend_Loader::loadClass('Zend_Registry'); // classe que registra objetos
Zend_Loader::loadClass('Zend_View'); // classe da View

// instancia a View
$view = new Zend_View();
// coloca o caminho onde estarão as views
$view->addScriptPath('./application/views/scripts/');
// registra o objeto view
Zend_Registry::set('view', $view);

// instancia o Controller
$controller = Zend_Controller_Front::getInstance();
// seta o diretório onde estão os Controllers
$controller->setControllerDirectory('./application/controllers');
// roda o Controller e Action desejados
$controller->dispatch();

É um exemplo talvez dos mais simples de como fazer um bootstrap. O Zend nos proporciona muito mais possibilidades, mas ainda não vou me estender a elas, ficarei no básico das views e controllers.

Assim que é chamado o dispatch(), o bootstrap nos envia para o Controller que requisitamos quando acessamos o site. Mas talvez a grande dúvida seja: como sabemos onde a requisição do dispatch vai parar? A resposta é: a URL.

No Zend Framework, o controller a ser chamado depende da URL chamada. Mas para isso funcionar, precisaremos de um arquivo .htaccess, como o abaixo:

RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|htm|html)$ index.php
php_flag magic_quotes_gpc off
php_flag register_globals off

Os dois exemplos abaixo, nos levam para o Controller index (classe IndexController)
http://localhost/meuprojeto/
http://localhost/meuprojeto/index/

Enquanto que o exemplo abaixo nos levará para um Controller de usuários (classe UsersController)
http://localhost/meuprojeto/users/

Mas isso ainda não nos mostra nada na tela além de erros: precisamos criar o controller e a view da index.

O Controller

No Zend, Controllers são classes simples, que devem seguir algumas regras:

  • O nome da classe deve ter o sufixo Controller
  • Deve estender a classe Zend_Controller_Action
  • As actions (métodos do controller, que serão as páginas de fato) devem ter o sufixo Action

O nosso Controller Index ficaria:

<?php

class IndexController extends Zend_Controller_Action
{

    public function indexAction()
    {
    	// pega a variável view registrada no bootstrap
        $view = Zend_Registry::get('view');
        // cria atributos, que serão visíveis na view
        $view->title = 'Página Inicial';
        $view->body = 'Hello World';
        // chama a view criada
        echo $view->render('index/index.phtml');

    }

}

Assim, criamos um controller simples, que basicamente chama uma view. Note que actions são as páginas propriamente ditas: então se você quiser criar uma página de contato por exemplo, basta criar uma action para ela. A diferença na hora de passar isso pela URL é bem simples:

http://localhost/meuprojeto/index/ vai para a action padrão do controller (que é setada por padrão como index, mas que pode ser alterada graças ao método setDefaultAction() da classe Zend_Controller_Front)

http://localhost/meuprojeto/index/index/ especifica que deve ir ao Controller Index e Action index

http://localhost/meuprojeto/users/subscribe/ especifica que deve ir ao Controller Users e Action subscribe

A partir daí, já fica também a idéia de como passar parâmetros para a Action…

View

Estamos quase lá: já temos uma página inicial, mas não temos nada para mostrar ao usuário além de erros até o momento. No Zend, o padrão para a View é o phtml, uma extensão normalmente usada para indicar que estamos usando php e html juntos.

A view para a nossa página inicial (index.phtml) ficaria:

<html>
<head>
<title><?php echo $this->escape($this->title); ?>
</head>
<body>

escape($this->body); ?>
</body>
</html>

Lembrando que as views ficam dentro do applications/views/scripts, e essa em específico dentro de uma pasta chamada index, o que não é obrigatório, mas por organização gosto de criar páginas com os mesmos nomes dos controllers.

Rodando

Feitos todos os passos anteriores, basta chamar por http://localhost/meuprojeto/ que deve aparecer uma página de Hello World.

Com certeza o Zend tem muito mais a nos oferecer que isso, mas aqui tivemos um começo para o nosso site e espero ter colocado de maneira sucinta e informativa a base do framework. Apesar da Zend atualmente guiar seus usuários para o uso da classe Zend_Application_Bootstrap_Bootstrapper, ainda acho que o bootstrap da maneira abordada é mais fácil de entender para quem está conhecendo.

Nos próximos posts devo abordar sobre recursos mais específicos, como o Zend_DB para a conexão com Banco de Dados ou o uso do Smarty no lugar do Zend_View (é certo que a Zend está mandando muito bem nessas últimas versões com bibliotecas específicas para o Zend_View, mas gosto muito do Smarty e em algumas pesquisas vejo que não sou o único).

Posted in php at julho 15th, 2009. 5 Comments.