Crie Web sites rapidamente com CakePHP, Parte 2: Faça mais e melhor com CakePHP

O CakePHP é uma ajuda estável, pronta para produção, de desenvolvimento rápido para construir Web sites em PHP. Esta série "Crie Web sites rapidamente com CakePHP" mostra como construir um catálogo de produtos on-line usando o CakePHP.

Duane O'Brien, PHP developer, 自由职业者

Duane O'Brien é um canivete suíço tecnológico desde quando The Oregon Trail era somente texto. Sua comida favorita é sushi. Ele nunca esteve na lua.



02/Jun/2009 (Primeira publicação 12/Dez/2006)

Antes de começar

Nota do editor: Esta série foi publicada originalmente em 2006 e atualizada em 2007 e 2008. Desde sua última publicação, os desenvolvedores do CakePHP fizeram mudanças nele, resultando em múltiplas revisões nesta série. Esta revisão foi escrita para CakePHP V1.2.2.8120.

Esta série "Crie Web sites rapidamente com CakePHP" foi projetada para desenvolvedores de aplicativos PHP que queiram começar a usar o CakePHP para facilitar a própria vida. No fim, você terá aprendido a instalar e configurar o CakePHP, as noções básicas de design do Model-View-Controller (MVC), a validar dados de usuário no CakePHP, a usar os auxiliares CakePHP e a ter um aplicativo pronto e em execução rapidamente usando o CakePHP. Pode parecer muita coisa para aprender, mas não se preocupe — O CakePHP faz a maior parte do trabalho para você.

Sobre esta série

  • A Parte 1 se concentra em ter o CakePHP pronto e em execução, e nas noções básicas para preparar um aplicativo simples que permita aos usuários registrar uma conta e fazer login no aplicativo.
  • A Parte 2 demonstra como usar scaffolding e Bake para começar rapidamente seu aplicativo, e a usar as listas de controle de acesso (ACLs) do CakePHP.
  • A Parte 3 mostra como usar Sanitize, uma classe CakePHP útil, que ajuda a tornar um aplicativo seguro ao limpar os dados enviados pelo usuário. A Parte 3 também abrange o componente de segurança CakePHP, manipulando solicitações inválidas e outras autenticações de solicitação avançadas.
  • A Parte 4 se concentra primariamente no componente de Sessão do CakePHP, demonstrando três modos de salvar os dados da sessão, além do componente Manipulador de Solicitação para ajudá-lo a gerenciar múltiplos tipos de solicitações (navegadores móveis, solicitações que contêm XML ou HTML, etc).
  • A Parte 5 trata de cache, em especial a visualização e o layout dele, o que pode ajudar a poupar consumo de recursos de servidor e agilizar seu aplicativo.

Sobre este tutorial

Este tutorial mostra como iniciar seu aplicativo CakePHP usando scaffolding e Bake. Você aprenderá também os detalhes do uso das ACLs do CakePHP. Você poderá dar uma olhada no que é scaffolding e o que ele faz. Daí, aprenderá a usar o Bake para gerar o código para o scaffold, permitindo que você o ajuste no caminho. Por fim, você aprenderá sobre ACLs: o que são, como criá-las e como usá-las no seu aplicativo. Esse tutorial foi desenvolvido sobre o aplicativo de produto on-line Tor, criado na Parte 1.

Pré-requisitos

Presume-se que você esteja familiarizado com o PHP, tenha uma idéia fundamental do design de bancos de dados e não se importe de sujar as mãos. Não é necessário entender totalmente o padrão de design MVC, visto que o básico será abrangido neste tutorial. O principal é querer muito aprender, estar pronto para entrar de cabeça e ansioso para acelerar seu tempo de desenvolvimento.

Requisitos do sistema

Antes de começar, você precisa de um ambiente no qual trabalhar. O CakePHP tem requisitos de servidor razoavelmente mínimos:

  1. Um servidor HTTP que suporte sessões (e, de preferência, mod_rewrite). Este tutorial foi escrito usando o Apache V2.2.4 com mod_rewrite ativado.
  2. PHP V4.3.2 ou posterior (incluindo PHP V5). Este tutorial foi escrito usando PHP V2.3.
  3. Um mecanismo de banco de dados suportado. Este tutorial foi escrito usando MySQL V5.0.4.

Você também precisará de um banco de dados pronto para ser usado pelo seu aplicativo. O tutorial fornecerá a sintaxe para criar as tabelas necessárias em MySQL.

O modo mais simples de fazer o download do CakePHP é acessar CakeForge.org e baixar a versão estável mais recente. Este tutorial foi escrito usando V1.2.2.8120. Também estão disponíveis compilações e cópias rápidas direto de Subversão. Os detalhes estão no Manual de CakePHP (veja os Recursos).


Tor, até o momento

No fim da Parte 1, você recebeu a oportunidade de usar suas habilidades para construir algumas funcionalidades faltantes no Tor. Login/Logout, índice, o uso de senhas em hash e fazer login e registrar automaticamente um usuário estavam na sua lista de deveres. Como você se saiu?

A visualização de login

Sua visualização de login talvez se pareça à Listagem 1.

Listagem 1. Visualização de login
<?php
if (isset($error)) {
  echo('Invalid Login.');
}
?>
 
<p>Please log in.</p>
<?php echo $form->create('User', array('action' => 'login')); ?>

<?php
    echo $form->input('username');
    echo $form->input('password');
?>

<?php echo $form->end('Login');?>
<?php echo $html->link('Register', array('action' => 'register')); ?>

Sua visualização de índice talvez se pareça à Listagem 2.

Listagem 2. Visualização de índice
<p>Hello, <?php echo($user['first_name'] . ' ' . $user['last_name']); ?></p>

<?php echo $html->link('knownusers', array('action' => 'knownusers')); ?>

<?php echo $html->link('logout', array('action' => 'logout')); ?>

Ambas as visualizações devem parecer bem simples. A visualização de índice verifica na sessão o nome de usuário e, se não houver, o envia para o login. A visualização de login não determina uma mensagem de erro específica, de modo que alguém que tente invadir o sistema por tentativa e erro não saberá que partes estão corretas.

Seu controlador talvez se pareça à Listagem 3.

Listagem 3. Controlador
<?php

class UsersController extends AppController
{
    var $name = 'Users';
    var $helpers = array('Html', 'Form' );

    function register()
    {
if (!empty($this->data))
{
$this->data['User']['password'] = md5($this->data['User']['password']);
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your registration information was accepted');
$this->Session->write('user', $this->data['User']['username']);
$this->redirect(array('action' => 'index'), null, true);
} else {
$this->data['User']['password'] = '';
$this->Session->setFlash('There was a problem saving this information');
}
}
    }

    function knownusers()
    {
$this->set('knownusers', $this->User->findAll(null,
 		array('id', 'username', 'first_name', 'last_name'), 'id DESC') ) ;
    }

    function login()
    {
if ($this->data)
{
$results = $this->User->findByUsername($this->data['User']
			 ['username']);
if ($results && $results['User']['password'] ==
 			      md5($this->data['User']
			 ['password']))
{
$this->Session->write('user', $this->data['User']['username']);
$this->redirect(array('action' => 'index'), null, true);
} else {
$this->set('error', true);
}
}
    }

    function logout()
    {
$this->Session->delete('user');
$this->redirect(array('action' => 'login'), null, true);
    }

    function index()
    {
$username = $this->Session->read('user');
if ($username)
{
$results = $this->User->findByUsername($username);
$this->set('user', $results['User']);
} else {
$this->redirect(array('action' => 'login'), null, true);
}
    }

}
?>

O uso de md5() para senhas em hash e a comparação dos seus valores em hash significa que você não precisa armazenar senhas em texto simples no banco de dados — desde que faça o hash das senhas antes de armazená-las. Quanto a usuários que você já tenha criado, você terá de atualizar suas senhas em texto simples com as versões MD5 delas: UPDATE users SET PASSWORD = md5( PASSWORD ) WHERE 1.

A ação logout não precisa de visualização. Só precisa limpar os valores que você coloca na sessão.

Não há problema se suas soluções não forem exatamente iguais a estas. Se você não conseguiu suas próprias soluções, atualize seu código usando o acima para ficar pronto para concluir o restante deste tutorial.


Scaffolding

No momento, o Tor não faz muita coisa. Ele permite que as pessoas se registrem, façam login e vejam quem mais está registrado. Agora o que ele precisa é permitir que os usuários insiram alguns produtos no catálogo ou vejam alguns produtos de outros usuários. Um bom modo de começar isso é usar o scaffolding.

Scaffolding é um conceito que vem do Ruby on Rails (veja os Recursos). É um modo excelente de construir estruturas rapidamente para prototipar o aplicativo, sem ter que escrever um monte de código descartável. Mas o scaffolding, como indica o nome em inglês ("andaime"), é algo que deve ser usado para ajudar a construir o aplicativo, não algo sobre o qual construí-lo. Quando você começar a desejar que o scaffolding agisse diferente, é hora de parar de usá-lo.

Configuração das tabelas de produtos

O scaffolding funciona examinando tabelas de bancos de dados e criando os tipos básicos de elementos normalmente usados com uma tabela: listas, botões acrescentar/excluir/editar, as coisas normalmente chamadas de Criar, Ler, Atualizar, Excluir (CRUD). Para começar, você precisa de algumas tabelas para conter as informações de produto e de vendedor.

Listagem 4. Criando tabelas para conter informações de produto
CREATE TABLE 'products' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT ,
'title' VARCHAR( 255 ) NOT NULL ,
'dealer_id' INT( 10 ) NOT NULL ,
'description' blob NOT NULL ,
PRIMARY KEY  ('id')
) TYPE = MYISAM ;

CREATE TABLE 'dealers' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT ,
'title' VARCHAR( 255 ) NOT NULL ,
PRIMARY KEY  ('id')
) TYPE = MYISAM ;

Além disso, será útil para esta demonstração inserir alguns dados na tabela de vendedor.

INSERT INTO dealers (title)
VALUES ('Vogon Construction Corp'), ('Bistromathics, Inc')

Um comentário importante sobre o scaffolding: Lembra daquele comentário sobre configuração do banco de dados sobre teclas estrangeiras que seguem o formato singular_id como user_id ou winner_id? No CakePHP, o scaffold espera que qualquer campo que termine em _id seja uma tecla estrangeira em uma tabela com o nome do que quer que preceda _id— por exemplo, o scaffolding esperaria que dealer_id fosse uma tecla estrangeira da tabela do vendedor.

Configuração do modelo de produtos

A funcionalidade dos produtos representa um conjunto totalmente novo de modelos, visualizações e controladores. Você terá de criá-los como fez na Parte 1. Crie seu modelo de produto em app/models/product.php.

Listagem 5. Criando um modelo de produto
<?php
  class Product extends AppModel
  {
    var $name = 'Product';
    var $belongsTo = array ('Dealer' => array(
'className' => 'Dealer',
'foreignKey'=>'dealer_id')
    );
  }
?>

Você notará a variável $belongsTo. Isso é conhecido como associação de modelo.

Associações de modelo

As associações de modelo dizem a um modelo que ele está relacionado de alguma maneira a outro modelo. Configurar associações adequadas entre os seus modelos lhe permitirá lidar com entidades e seus modelos associados como um todo, em vez de individualmente. No CakePHP, há quatro tipos de associações de modelo:

hasOne
A associação hasOne diz ao modelo que cada entidade nele tem uma entidade correspondente em outro modelo. Um exemplo disso seria a entidade de perfil correspondente da entidade do usuário (presumindo-se que só seja permitido um perfil por usuário).
hasMany
A associação hasMany diz ao modelo que cada entidade nele tem várias entidades correspondentes em outro modelo. Um exemplo disso seria um modelo de categoria que tenha muitas coisas que pertençam à categoria (posts, produtos, etc.). No caso do Tor, uma entidade vendedora tem muitos produtos.
belongsTo
Isso diz ao modelo que cada entidade dele aponta para uma entidade em outro modelo. Isso é o oposto de hasOne, então um exemplo seria uma entidade de perfil que aponte de volta para uma entidade de usuário correspondente.
hasAndBelongsToMany
Essa associação indica que uma entidade tem muitas entidades correspondentes em outro modelo e também aponta de volta para muitas entidades correspondentes em outro modelo. Um exemplo disso seria uma receita. Muitas pessoas talvez gostem da receita e ela talvez tenha vários ingredientes.

A variável belongsTo nesse caso indica que cada produto na tabela de produtos "pertence a" determinado vendedor.

Criando um modelo de vendedor

Como a associação indica, um modelo de vendedor também é necessário. O modelo de vendedor será usado depois no Tor para construir a funcionalidade que define as vendas. Enquanto o modelo de produto tem uma associação de belongsTo apontando para um vendedor, o modelo de vendedor tem uma associação com o produto de hasMany.

Listagem 6. O modelo de vendedor tem uma associação com o produto de hasMany
<?php
  class Dealer extends AppModel
  {
    var $name = 'Dealer';
    var $hasMany = array ('Product' => array(
'className' => 'Product',
'foreignKey'=>'dealer_id')
    );
  }
?>

Por enquanto, você pode pular o acréscimo de validação de dados, mas à medida que o aplicativo for se desenvolvendo, você talvez tenha ideias de tipos diferentes de validação para acrescentar.

Criando o controlador de produtos

Você já criou e associou os modelos de produto e vendedor. Agora o Tor sabe como os dados estão inter-relacionados. A seguir, faça seu controlador em app/controllers/products_controller.php—, mas desta vez acrescente a variável de classe $scaffold.

Listagem 7. Acrescentando uma variável de classe ao seu controlador
<?php
class ProductsController extends AppController
{
  var $scaffold;
}
?>

Salve o controlador e acesse http://localhost/products (sim, sem criar visualizações ou controlador de Vendedor). Você deve ver algo como o mostrado na Figura 1.

Figura 1. Lista de produtos vazia
Empty product list

Simples assim. Assim, você tem uma interface para a tabela de produtos que lhe permite acrescentar, editar, excluir, alistar, dividir e fatiar seus produtos.

Tente acrescentar um produto. Deverá ser pedido que você insira um título e descrição, além de selecionar um vendedor. Essa lista de vendedores lhe parece familiar? Deveria parecer — você a inseriu na tabela de vendedor logo após criá-la. O scaffolding reconheceu as associações da tabela à medida que você as definiu e gerou automaticamente a lista suspensa de vendedores para você.

Agora volte e olhe a quantidade de código que você escreveu para obter toda essa funcionalidade. Ficou bem mais fácil?


Usando o gerador de código Bake

Não é necessário descartar totalmente tudo o que o scaffolding lhe fornecer. Usando o Bake, o gerador de código do CakePHP, você pode gerar um controlador que contenha funções que representem a funcionalidade do scaffolding e as visualizações que a acompanham. Para as partes do Tor relacionadas a produtos, isso economizará muito tempo.

No CakePHP V1.1, o Bake era um script PHP que você chamava diretamente. No CakePHP V1.2, a funcionalidade do Bake foi movida para o Cake Console, com o qual você se familiarizará no restante deste tutorial. Você pode simplificar a vida se acrescentar o caminho /webroot/cake/console à sua variável de ambiente PATH. Isso lhe permitirá chamar o Cake Console sem especificar as informações de caminho. Você não precisa fazer isso, e o tutorial presumirá que você não fez. Além disso, você deve executar o Cake Console a partir do seu diretório app de aplicativo, — no caso: /webroot/app —, ou o Cake Console achará que você está tentando fazer algo novo.

Antes de continuar, faça uma cópia do seu diretório app existente. O Bake vai sobrescrever o controlador dos produtos, e é sempre necessário fazer backup dos seus arquivos quando a operação envolver a palavra "sobrescrever" (ou as palavras "copiar", "excluir", "formatar" ou "vodu"). Se tiver problemas para fazer isso funcionar, certifique-se de que o executável php esteja na sua variável de ambiente PATH.

Usando o Bake no seu controlador de produtos

Para usar o Bake, cd no diretório /webroot/app e abrir o Cake Console: ../cake/console/cake bake. Deve aparecer uma tela que se parece à Figura 2.

Figura 2. Menu do Bake
Bake menu

Para o aplicativo Tor, o modelo que você escreveu deve ser o suficiente, então vamos começar com o controlador. Pressione C para selecionar o controlador. O Cake Console vai dar uma olhada no aplicativo como está e apresentar uma lista de controladores possíveis que você talvez queira submeter ao Bake.

Figura 3. Nome do controlador
Controller name

Nesse caso, você está criando o controlador de produtos, que seria o controlador N° 2. O Bake vai perguntar se você quer construir o controlador interativamente. Por enquanto, pressione N para deixar o Bake tomar todas as decisões sozinho, mas, mais tarde, tente construir o controlador interativamente para entender o que o Bake pode fazer para você além disso. O Bake vai perguntar então se você quer incluir alguns métodos de classe básicos (veja a Figura 4). Pressione Y— porque obter esse código é a principal razão de você estar aqui. A seguir, o Bake lhe perguntará se você quer criar métodos para administrar roteamento. Pressione N— visto que você não precisa deles no momento. O Bake lhe pedirá para confirmar o controlador antes de continuar. Quando você continuar, o Bake se certificará se você deseja sobrescrever o controlador existente (nesse caso, você deseja). O Bake deverá informá-lo então que criou o arquivo \app\controllers\products_controller.php e lhe perguntará se você deseja criar alguns arquivos de teste da unidade. Você pode ignorar isso por enquanto. Quando terminar, você retornará ao menu do Bake.

Figura 4. Controlador criado
Controller created

Isso é tudo. Saia do Cake Console, abra app/controllers/products_controller.php e dê uma olhada no que o Bake fez para você. Deve ser algo parecido à Listagem 8.

Listagem 8. products_controller.php após o Bake
class ProductsController extends AppController {

    var $name = 'Products';
    var $helpers = array('Html', 'Form' );

    function index() {
  $this->Product->recursive = 0;
  $this->set('products', $this->paginate());
    }

    function view($id = null)
{ if (!$id) {
  $this->Session->setFlash('Invalid Product.');
  $this->redirect(array('action'=>'index'));
}
$this->set('product', $this->Product->read(null, $id));

}

function add() {
if (!empty($this->data)) {
   $this->Product->create();
   if ($this->Product->save($this->data)) {
 $this->Session->setFlash(  ('The Product has been saved', true));
 $this->redirect(array('action'=>'index'));
   } else {
  $this->Session->setFlash('The Product could not be
saved. Please, try again.', true))
   }
}
$dealers = $this->Product->Dealer-find('list');

$this->set(compact('dealers'));
    }

function edit($id = null){
if (!$id && empty($this->data)) {
    $this->Session->setFlash('Invalid Product', true));
    $this->redirect(array('action'=>'index'));
}
if (!empty($this->data)) {
    if ($this->Product->save($this->data)) {
   $this->Session->setFlash('The Product has been saved', true));
   $this->redirect(array('action'=>'index'));

} else {

$this->Session->setFlash('The Product could not be
saved. Please, try again.', true));
}
}
if (empty($this->data)) {
    $this->data = $this->Product->read(null, $id);
}
$dealers = $this->Product->Dealer->generateList();
$this->set(compact('dealers'));
    }

function delete($id = null) {
   if (!$id) {
   $this->Session->setFlash('Invalid id for Product', true));
   $this->redirect(array('action'=>'index'));
   }
   if ($this->Product->del($id)) {
   $this->Session->setFlash(__('Product deleted', true));
   $this->redirect(array('action'=>'index'));
}
}
}
?>

Esse controlador que você gerou usando o Cake Console contém a mesma funcionalidade básica que você obteve pelo scaffolding, mas agora você pode ajustá-lo às suas necessidades. Bem fácil. Pronto para outra?

Usando o Bake nas suas visualizações de produtos

Agora que você usou o Bake no seu controlador de produtos, tudo o que o Tor necessita é algumas visualizações de produtos. O Bake também fará isso para você. Comece como antes no seu diretório /webroot/app: ../cake/console/cake bake.

O menu inicial do Bake deve ser idêntico a quando você o usou no controlador. Desta vez, porém, é hora de preparar algumas visualizações. Pressione V para selecionar visualizações. Aparecerá outra lista de visualizações possíveis a serem criadas. Os produtos ainda devem ser o N.° 2 da lista. O Bake lhe perguntará se você deseja criar algumas visualizações de scaffolding (Sim/Y) e se deseja criar visualizações para administrar roteamento (Não). Mais tarde você pode voltar e brincar com as diferentes opções do Bake.

Depois dessas duas opções, o Bake deverá lhe informar que criou as visualizações.

Figura 5. O Bake informa você que criou as visualizações
Bake informs you that it has created the views

Saia do Cake Console, abra a visualização app/views/products/index.ctp e dê uma olhada. Deve ser algo parecido à Listagem 9.

Listagem 9. O índice
<div class="products">
<h2><?php __('Products');?></h2>
<p>
<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current%
records out of %count% total, starting on record %start%, ending on %end%', true)
));
?></p>
<table cellpadding="0" cellspacing="0">
<tr>
<th><?php echo $paginator->sort('id');?></th>
<th><?php echo $paginator->sort('title');?></th>
<th><?php echo $paginator->sort('dealer_id');?></th>
<th><?php echo $paginator->sort('description');?></th>
<th class="actions"><?php __('Actions');?></th>
</tr>
<?php
$i = 0;
foreach ($products as $product):
$class = null;
if ($i++ % 2 == 0) {
$class = ' class="altrow"';
}
?>
<tr<?php echo $class;?>>
<td>
      <?php echo $product['Product']['id'] ?>
</td>
<td>
<?php echo $product['Product']['title'] ?>
</td>
<td>
      <?php echo $html->link(__($product['Dealer']
['title'], true), array('controller'=> 'dealers', '
action'=>'view', $product['Dealer']['id'])); ?>
</td>
<td>
<?php echo $product['Product']['description'] ?>
</td>
<td class="actions">
     <?php echo $html->link(__('View', true),
array('action'=>'view', $product['Product']['id'])); ?>
     <?php echo $html->link(__('Edit', true),
array('action'=>'edit', $product['Product']['id'])); ?>
     <?php echo $html->link(__('Delete', true),
array('action'=>'delete', $product['Product']['id']),
null, sprintf(__('Are you sure you want to delete
#%s?', true),
$product['Product']['id'])); ?>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
<div class="paging">
<?php echo $paginator->prev('<< '.__('previous', true),
 		   array(), null, array('class'=>'disabled'));?>
 |      <?php echo $paginator->numbers();?>
<?php echo $paginator->next(__('next', true).' >>',
 		   array(), null, array('class'=>'disabled'));?>
</div>
<div class="actions">
<ul>
        <li><?php echo $html->link(__('New Product', true),
                       array('action'=>'add')); ?></li>
         <li><?php echo $html->link(__('List Dealers', true),
                       array('controller'=> 'dealers', 'action'=>'index'));
                       ?> </li>
         <li><?php echo $html->link(__('New Dealer', true),
                       array('controller'=> 'dealers', 'action'=>'add')); ?>
</li>
</ul>
</div>

Dê uma olhada também nas outras visualizações. É um monte de coisas que você não precisou escrever. Mais tarde, você vai ajustar essas visualizações para ajudar a bloquear o Tor.

Faça um "test drive"

Você criou um controlador e as visualizações necessárias para a funcionalidade dos produtos. Faça um teste. Comece por http://localhost/products e passe pelas várias partes do aplicativo. Acrescente um produto. Edite um. Exclua outro. Visualize um produto. Deve parecer exatamente como quando você usava o scaffolding.

Faça mais e melhor

Isso não chega nem perto de tudo o que o Bake pode fazer por você. Haverá exercícios no fim do tutorial para deixar que você se aventure sozinho. Tenha em mente que o código gerado pelo Bake deve ser seu ponto de partida, não o fim do seu trabalho de desenvolvimento. Mas ajuda a poupar muito tempo se usado adequadamente.


Listas de controle de acesso

Até aqui, o Tor está completamente aberto em termos de acesso. Por exemplo, qualquer um pode acrescentar, editar ou excluir produtos, etc. É hora de bloquear algumas de suas funcionalidades. Para isso, usaremos a funcionalidade ACL do CakePHP.

O que é uma ACL?

Uma ACL é, basicamente, uma lista de permissões. É isso. Não é um meio de autenticação de usuário. Não é a solução milagrosa para a segurança PHP. Uma ACL é apenas uma lista de quem pode fazer o quê.

Quem é, em geral, um usuário, mas pode ser algo como um controlador. Quem é chamado de objeto de solicitação de acesso (ARO). Fazer o quê, nesse caso, normalmente significa "executar código". Fazer o que é chamado de objeto de controle de acesso (ACO).

Portanto, um ACL é uma lista de AROs e ACOs à qual eles têm acesso. Simples, não? Deveria ser. Mas não é.

Assim que a explicação sai do "é uma lista de quem pode fazer o que" e começa jogar esses acrônimos de três letras (TLAs) para cima de você, as coisas podem desandar. Mas um exemplo vai ajudar.

Imagine que há uma festa em um clube. Todo mundo importante está lá. A festa é dividida em várias seções — há o lounge VIP, a pista de dança e o bar principal. E, é claro, uma fila enorme de gente tentando entrar. O enorme e assustador leão-de-chácara da entrada verifica as identificações de todos, olha A Lista e dispensa a pessoa ou a deixa entrar na seção da festa para a qual foi convidada.

As pessoas que querem entrar são os AROs. Estão solicitando acesso às diferentes seções da festa. O lounge VIP, a pista de dança e o bar principal são os ACOs. A ACL é o enorme e assustador leão-de-chácara na porta com sua prancheta. O enorme e assustador leão-de-chácara é o CakePHP.

Criando uma tabela de ACL

No CakePHP V1.1, o gerenciamento de ACL funcionava como o Bake, via script PHP que você chamava diretamente. No CakePHP V1.2, o gerenciamento de ACL faz parte do Cake Console. Usando o Cake Console, você pode configurar uma tabela de banco de dados para ser usada para armazenar as informações da ACL. Na linha de comando, a partir do diretório /webroot/app, execute o seguinte comando: ../cake/console/cake schema run create DbAcl. O Cake Console o avisará que vai criar tabelas (mesmo que não apareçam) e depois lhe perguntará se você deseja criar três tabelas (responda sim para ambos). Quando terminar, o Bake lhe dirá que criou três bancos de dados (veja a Figura 6): acos, aros e aros_acos.

Figura 6. Saída de Shell da ACL
acl.php output

Só precisa isso para começar. Agora é o momento de começar a definir seus AROs e ACOs.

Definindo AROs

Então você tem as tabelas de banco de dados de ACL. E tem um aplicativo que permite que os usuários façam o próprio registro. Como criar os AROs para os seus usuários?

Faz muito sentido acrescentar isso à parte de registro do aplicativo. Desse modo, quando novos usuários se inscrevem, seu ARO correspondente é criado automaticamente para eles. Isso significa que terá de criar manualmente dois AROs para os usuários que já criou, mas o CakePHP também facilita isso.

Definindo grupos

No CakePHP (e ao usar ACLs em geral), os usuários podem ser designados a grupos com o fim de atribuir ou revogar permissões. Isso simplifica muito a tarefa de gerenciamento de permissão, visto que você não precisa lidar com permissões individuais de usuário, o que pode se tornar uma tarefa e tanto se seu aplicativo tiver um número razoável de usuários.

No caso do Tor, você vai definir dois grupos. O primeiro grupo, chamado de Usuários, será usado para classificar todos que simplesmente registraram uma conta. O segundo, chamado Vendedores, será usado para conceder a certos usuários permissões adicionais dentro do Tor. Você criará ambos esses grupos usando o Cake Console, mais ou menos como fez ao criar o banco de dados da ACL. Para criar os grupos, execute os comandos abaixo no diretório /webroot/app.

../cake/console/cake acl create aro 0 Users
../cake/console/cake acl create aro 0 Users

Após cada comando, o CakePHP deve mostrar uma mensagem dizendo que o ARO foi criado.

New Aro 'Users' created.
New Aro 'Dealers' created.

Os parâmetros que você inseriu (por exemplo, '0 Usuários') são o pai e nó. O parâmetro-pai deve corresponder ao grupo ao qual o ARO deve pertencer. Visto que esses grupos são de nível máximo, você os passou como 0. O parâmetro node é uma cadeia de caractere usada para se referir ao grupo.

Acrescentando a criação de ARO ao registro

Acrescer a criação de ARO à parte de registro do usuário no Tor não é difícil. É só uma questão de incluir o componente certo e acrescentar umas linhas de código. Para refrescar sua memória, a função de registro de users_controller.php deve se parecer à Listagem 10.

Listagem 10. Ação register original
    function register()
    {
if (!empty($this->data))
{
$this->data['User']['password'] = md5($this->data['User']
			      ['password']);
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your registration information
 				   was accepted');
$this->Session->write('user', $this->data['User']
				   ['username']);
$this->redirect(array('action' => 'index'), null, true);
} else {
$this->data['User']['password'] = '';
$this->Session->setFlash('There was a problem saving
 				  this information');
}
}
    }

Para começar a usar o componente ACL do CakePHP, você precisa incluí-lo como variável de classe.

Listagem 11. Incluindo os componentes como variável de classe
<?php
class UsersController extends AppController
{
  var $components = array('Acl');
  ...

O array $components simplesmente contém uma lista de componentes CakePHP a incluir, por nome. Os componentes são para os Controladores o que os Auxiliares são para as Visualizações. Há outros componentes disponíveis, como o de segurança, que será tratado em um tutorial posterior. Neste caso, o único de que você precisa é o ACL.

Agora você tem acesso a toda a funcionalidade fornecida pelo componente ACL. Você pode criar um ARO invocando o método create no objeto do ACL ARO (que será mais fácil de ler na Listagem 12). Esse método usa os mesmos parâmetros que você normalmente passaria ao chamar o método create a partir de um modelo, que é essencialmente o que você está fazendo. Neste caso, você especifica o nome do alias (o nome de usuário), o modelo para o qual o ACL aponta (usuário), o foreign_key para o registro (a nova identificação de usuário) e o parent_id (a identificação do nó-pai — neste caso, o grupo de ARO de Usuário, que é encontrado usando a linha findByAlias, abaixo). Para criar o ARO para o seu usuário, você também precisa saber qual é a identificação do usuário que foi salva. Você pode saber isso por meio de $this->User->id após os dados serem salvos.

Juntando tudo, sua função register agora deve se parecer à Listagem 12.

Listagem 12. Função Register
    function register()
    {
if (!empty($this->data))
{
$this->data['User']['password'] = md5($this->data['User']
			['password']);
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your registration information
 				  was accepted');
$this->Session->write('user', $this->data['User']['username']);
$parent = $this->Acl->Aro->findByAlias('Users');
                $aro = new Aro();
                $aro->create();
                $aro->save(array(
'alias' => $this->data['User']['username'],
'model' => 'User',
'foreign_key' => $this->User->id,
'parent_id' => $parent['Aro']['id'])
);
$this->Acl->Aro->save();
$this->redirect(array('action' => 'index'), null, true);
} else {
$this->data['User']['password'] = '';
$this->Session->setFlash('There was a problem saving
 				  this information');
}
}
    }

Você notará que o ARO não é criado até que o salvamento seja bem-sucedido.

Experimente

Isso deve ser tudo o que você precisa para que seus AROs entrem em operação. Para verificar, recomece na linha de comando em webroot/app e peça ao Cake Console para ver a árvore de ARO: ../cake/console/cake acl view aro. Você deve ver algo como o que é mostrado na Figura 7.

Figura 7. Resultado de ARO de visualização de shell da ACL com lista vazia
Output from acl.php view ARO with empty list

Agora acesse http://localhost/users/register e inscreva um novo usuário. Depois de fazer isso, execute de novo o comando ../cake/console/cake acl view aro. Você deve ver algo como o que é mostrado na Figura 8.

Figura 8. Resultado de ARO de visualização de shell da ACL com um usuário
Output from acl.php view ARO with a user

De agora em diante, sempre que alguém registrar uma nova conta, um ARO será criado automaticamente para ele. O ARO vai pertencer ao grupo Usuários.

Criando AROs para usuários existentes

Agora que os AROs estão sendo criados para os novos usuários do Tor, você precisa voltar e criar AROs para os usuários existentes. Você fará isso com o Cake Console, quase que exatamente da mesma maneira em que criou os grupos. Comece usando aquele usuário que você acabou de criar acessando http://localhost/users/knownusers para obter uma lista dos usuários que foram criados.

Figura 9. Resultado de ARO de visualização de shell da ACL com um usuário
Output from acl.php view aro with a user

Daí, para cada usuário, você precisará executar o comando de criação de ARO, como fez ao criar os grupos. No caso de pai, especifique 'Usuários'. No caso do nó, especifique o nome do usuário. Por exemplo, na Figura 9, para criar um ARO para dentarthurdent, execute o seguinte (mais uma vez, no diretório /webroot/app): ../cake/console/cake acl create aro Users dentarthurdent.

Certifique-se de executar esses comandos para cada usuário na sua lista knownusers, exceto para aqueles que você criou para testar a criação de ARO durante o registro de usuário. Confirme se você está especificando a identificação e nome de usuário corretos para cada usuário. Quando terminar, o resultado de ../cake/console/cake acl deve ser semelhante à Figura 10.

Figura 10. Resultado de ARO de shell da ACL com um usuário
Output from acl.php view ARO with a user

Você pode obter ajuda sobre algumas das outras coisas que o Cake Console pode fazer com ACLs executando ../cake/console/cake acl help na linha de comando.


Definindo ACOs

Agora que o Tor está com os AROs definidos, é hora de identificar e definir seu ACO. Neste caso, você vai definir ACOs para representar produtos, organizando-os em grupos como fez com os AROs.

Acrescentando a definição de ACO ao controlador de produtos

Você vai acrescentar a definição inicial de ACO ao controlador de produtos na função add, de modo similar ao que fez com a definição do ARO no registro de usuário. No momento, a função add é exatamente o que o Bake lhe forneceu. Deve ser algo parecido à Listagem 13.

Listagem 13. Função add
       function add() {
if (!empty($this->data))
{ $this->cleanUpFields();
$this->Product->create();
if ($this->Product->save($this->data)) {
$this->Session->setFlash('The Product
     has been saved', true));
$this->redirect(array('action'=>'index'));
} else {
$this->Session->setFlash('The Product could not be
     saved. Please, try again.', true));
}
}
$dealers = $this->Product->Dealer->find('list');
$this->set(compact('dealers'));
}

Novamente, o CakePHP simplifica muito o acréscimo da definição aos seus ACOs. Você começa acrescentando a variável de classe $components ao controlador, como fez para o controlador de usuários.

Listagem 14. Acrescentando a variável de classe $components ao controlador
<?php
class ProductsController extends AppController
{
  var $components = array('Acl');
  ...

Criar um ACO é quase idêntico a criar um ARO. Você chama o método create no objeto de ACL ACO. Desta vez, o alias precisa ser mais do que apenas o título do produto, visto que ele talvez não seja único. Em vez disso, use uma combinação de identificação e título de produto para o alias. O modelo será o Produto, o foreign_key será a nova identificação do produto e o parent_id será a identificação do vendedor que inseriu o produto (você ainda não configurou isso, mas vai fazê-lo em breve). Colocando esses pedaços na sua função add, deve parecer à Listagem 15.

Listagem 15. Nova função add
function add() {
if (!empty($this->data)) {
$this->cleanUpFields();
$this->Product->create();
if ($this->Product->save($this->data)) {
$dealer = $this->Product->Dealer->read(null,
     $this->data['Product']['dealer_id']);
$parent = $this->Acl->Aco->findByAlias($dealer
								    ['Dealer']['title']);
$this->Acl->Aco->create(array(
'alias' =>
		 $this->Product->id.'-'.$this->data['Product']['title'],
'model' => 'Product',
'foreign_key' => $this->Product->id,
'parent_id' => $parent['Aco']['id'])
);
$this->Acl->Aco->save();
$this->Session->setFlash('The Product
      has been saved');
$this->redirect(array('action'=>'index'),
      null, true);
} else {
$this->Session->setFlash('The Product could not
      be saved. Please, try again.');
}
}
$dealers = $this->Product->Dealer->generateList();
$this->set(compact('dealers'));
}

Isso deve ser tudo o que é necessário para criar automaticamente os ACOs para os produtos criados no Tor. Antes de continuar, você deve criar ACOs para os produtos e grupos existentes.

Acrescentando definições de ACO para os vendedores

Pode-se usar o Cake Console parta definir ACOs mais ou menos da mesma maneira em que foi usado para definir os AROs para os usuários existentes. Será útil usar a lista de produtos que o CakePHP preparou para você em http://localhost/products.

Mais uma vez, na linha de comando, no diretório /webroot/app, você vai executar alguns comandos create. Comece criando grupos para representar os vendedores que você criou lá atrás, ao criar a tabela de vendedores. Mas desta vez, especifique que está criando um ACO.

../cake/console/cake acl create aco root "Tor Johnson School Of Drama"

../cake/console/cake acl create aco root "Chriswell's Psychic Friends"

Você pode executar ../cake/console/cake acl view aco para verificar se os grupos têm a aparência esperada.

Figura 11. Dump de ACO com vendedores sem produtos
ACO dump with dealers no products

A seguir, exclua os produtos existentes da tabela de produtos. Você deve poder fazer isso indo até o índice de produtos (http://localhost/products/index) e clicando em Excluir ao lado de cada produto.

Visto que você só criou uns dois produtos até agora, recriá-los é o caminho mais curto para acrescentar os ACOs que deseja. Ainda não teste aquela função de novo produto add. Agora que os ACOs estão criados para os vendedores existentes e que você excluiu os produtos existentes, você está pronto para continuar a configurar algumas permissões.


Atribuindo permissões

Agora, o Tor tem vários AROs que representam usuários, e o palco está montado para criar alguns ACOs que representem produtos, agrupados por vendedor. É hora de reuni-los definindo algumas permissões.

Como as permissões funcionam?

Você vai definir especificamente quem tem direito de trabalhar com os produtos. Fará isso permitindo explicitamente plenos direitos a um ARO (neste caso, um usuário) sobre um ACO (neste caso, um produto) e sobre uma ação. As ações poderão ser ler (ou seja, o usuário poderá ver as informações no banco de dados), criar (o usuário poderá inserir informações no banco de dados), atualizar (o usuário poderá modificar informações), excluir (o usuário poderá excluir informações do banco de dados) ou *, o que significa que o usuário poderá executar todas as ações. Cada ação deve ser concedida individualmente; permitir excluir não implica em permitir criar, nem mesmo ver.

Por padrão, depois de verificar permissões para algo, se não houver permissão definida, o CakePHP presume DENY.

Definindo políticas

Definir políticas de permissão é mais do que apenas escrever e executar código. Você precisa pensar no que seu ACL está realmente tentando realizar. Sem uma ideia clara do que você está tentando proteger e de quem, terá que redefinir constantemente suas permissões.

O Tor tem usuários e produtos. Para os fins deste tutorial, você vai permitir que o usuário que criou o produto tenha permissão total de editá-lo e excluí-lo. Qualquer usuário poderá ver o produto, a menos que lhe seja negado acesso de forma explícita.

Acrescentando definição de permissão ao produto add

O Tor precisa saber como atribuir permissões quando um produto é criado. Isso pode ser feito acrescentando-se duas linhas ao controlador. Uma acrescenta as permissões de visualização para os usuários e a outra, permissões totais para o usuário que o criou. Conceder permissões é algo assim: $this->Acl->allow(ARO, ACO, TYPE);.

Se você não especificar TYPE (create, read, update ou delete), o CakePHP vai presumir que você está concedendo permissão total. Sua nova função add no controlador de produtos deve ser semelhante à Listagem 16.

Listagem 16. Nova função add no controlador de produto
     function add() {
        if (!empty($this->data)) {
             $this->Product->create();
            if ($this->Product->save($this->data)) {
                $dealer = $this->Product->Dealer->read(null,
$this->data['Product']['dealer_id']);
                $parent = $this->Acl->Aco->findByAlias(
$dealer['Dealer']['title']);
                $alias = $this->Product->id.'-'.$this->data
['Product']['title'];
                $aco = new Aco();
                $aco->create();
                $aco->save(array(
                    'alias' => $alias,
                    'model' => 'Product',
                    'foreign_key' => $this->Product->id,
                    'parent_id' => $parent['Aco']['id']
                ));
                $this->Acl->allow('Users', $alias, 'read');
                $this->Acl->allow($this->Session->read('user'), $alias);
                $this->Session->setFlash(__('The Product has been saved', true));
                $this->redirect(array('action'=>'index'));
            } else {
                $this->Session->setFlash(__('The Product could not be saved.
Please, try again.', true));
            }
        }
        $dealers = $this->Product->Dealer->find('list');
        $this->set(compact('dealers'));
    }

OK — agora você pode tentar acrescentar alguns produtos. Faça login como um dos seus usuários e acrescente uns dois produtos, só para ver se tudo dá certo. Pode usar o Cake Console para ver os ACOs que criou quando acrescentou um novo produto. Você está quase no fim. Os AROs e ACOs estão definidos e as permissões foram atribuídas. Agora, o Tor precisa verificar as permissões ao executar as várias ações relacionadas aos produtos.


Colocando as ACLs para trabalhar

Você juntou todas as peças, e agora chegou a hora de colocar suas ACLs para trabalhar. Quando terminar, qualquer usuário terá permissão de ver os produtos no Tor, mas apenas os que os criaram poderão editá-los ou excluí-los.

Você vai acrescentar duas linhas a cada ação no controlador de produtos. Essas linhas vão verificar o acesso do usuário e permitir ou negar a ação com base nas permissões.

Permitindo que os usuários apenas vejam os produtos

Comece com a ação view. Acrescente uma linha para verificar o acesso ao produto, exibindo uma mensagem se a ação não for permitida.

Listagem 17. Acrescentando uma linha para verificar o acesso ao produto
function view($id = null){
if (!$id) {
  $this->Session->setFlash('Invalid Product.', true));
  $this->redirect(array('action'=>'index'));
}
$product = $this->Product->read(null, $id);
$alias = $id . '-' . $product['Product']['title'];
if ($this->Acl->check($this->Session->read('user'), $alias;
			    $id . '-' . $product['Product']['title'], 'read')) {
$this->set('product', $product);
} else {
   $this->Session->setFlash('Only registered users may view this product.');
   $this->redirect(array('action'=>'index'));
       }
}

Salve o arquivo, confirme se foi feito o logout do Tor e acesse a lista de produtos em http://localhost/products. Quando clicar em um dos produtos, você deverá ser redirecionado para a página de Registro de Usuário, como mostrado abaixo.

Figura 12. Redirecionamento
Redirection

Agora, faça login usando qualquer conta e tente de novo. Desta vez, você deverá poder ver o produto, como se vê na Figura 13.

Figura 13. Vendo o produto
Viewing the product

Isso atende a primeira parte das permissões. Agora você precisa dizer ao Tor para negar acesso para editar e excluir a qualquer um, menos o usuário que criou o produto.

Permitindo que apenas o criador do produto o edite ou exclua

O processo para controlar permissões é bem semelhante às ações edit e delete no controlador de produtos.

Listagem 18. Ação edit
    function edit($id = null) {
        if (!$id && empty($this->data)) {
            $this->Session->setFlash(__('Invalid Product', true));
            $this->redirect(array('action'=>'index'));
        }
        $product = $this->Product->read(null, $id);
        $alias = $id.'-'.$product['Product']['title'];
        if ($this->Acl->check($this->Session->read('user'),
$alias, 'update')) {
            if (!empty($this->data)) {
                if ($this->Product->save($this->data)) {
                    $this->Session->setFlash(__('The Product has been saved',
true));
                    $this->redirect(array('action'=>'index'));
                } else {
                    $this->Session->setFlash(__('The Product could not be saved.
Please, try again.', true));
                }
            }
            if (empty($this->data)) {
                $this->data = $this->Product->read(null, $id);
            }
        } else {
            $this->Session->setFlash('You cannot edit this product.');
            $this->redirect(array('action'=>'index'), null, true);
        }
        $dealers = $this->Product->Dealer->find('list');
        $this->set(compact('dealers'));
    }

Para o controlador de exclusão, você precisa acrescentar duas linhas para excluir o ACO do produto sendo excluído. Sua ação delete será semelhante à Listagem 19.

Listagem 19. Ação delete
    function delete($id = null) {
        if (!$id) {
            $this->Session->setFlash(__('Invalid id for Product', true));
            $this->redirect(array('action'=>'index'));
        }
        $product = $this->Product->read(null, $id);
        $alias = $id.'-'.$product['Product']['title'];
        if ($this->Acl->check($this->Session->read('user'),
$alias, 'delete')) {
            if ($this->Product->del($id)) {
                $aco = $this->Acl->Aco->findByAlias($alias);
                $this->Acl->Aco->delete($aco['Aco']['id']);
                $this->Session->setFlash(__('Product deleted', true));
                $this->redirect(array('action'=>'index'));
            }
        } else {
            $this->Session->setFlash('You cannot delete this product.');
            $this->redirect(array('action'=>'index'));
        }
    }

Salve o controlador de produtos e faça um teste. Comece fazendo o logout de http://localhost/users/logout, depois volte à sua lista de produtos em http://localhost/products/ e tente editar ou excluir o produto. Você deve ser direcionado de volta à lista de produtos com uma mensagem.

Figura 14. Falha na edição ou exclusão
Failed edit or delete

Agora, faça o login como o usuário dentarthurdent e acrescente um produto. Tente editá-lo. Depois, exclua-o. Você não deve ter problemas para isso.

Figura 15. Edição ou exclusão bem-sucedida
Successful edit or delete

Faça o logout novamente de http://localhost/users/logout e o login como outro usuário. Daí, tente editar ou excluir outro produto, e você não deverá ser capaz de executar essas ações. Enquanto estiver logado, crie um novo produto e tente modificá-lo ou excluí-lo como outro usuário.


Preenchendo as falhas

Com o CakePHP, você pode construir rápida e facilmente as partes do seu aplicativo, usando o scaffolding e o Bake. Usando ACLs, você pode ter grande controle sobre muitos aspectos do seu aplicativo. Há mais coisas a fazer no Tor. Aí vão alguns exercícios para praticar.

Vendedores

Como você deve ter notado nas visualizações de produtos construídas pelo Bake, há links na visualização de índice que apontam para vendedores. Como fez com os produtos, use o Cake Console para construir um controlador e visualizações para os vendedores. Não faça um modelo, pois você já tem um definido e relacionado aos produtos.

Modifique a ação do vendedor add para verificar se o nome do vendedor é exclusivo.

ACLs

Há um bug na ação add para o controlador de produtos. Ela não verifica quem criou o produto. Essa funcionalidade só deve estar disponível aos usuários. Conserte o bug.

Depois de construir os vendedores, usando as técnicas de ACL que aprendeu, proteja toda a funcionalidade de vendedor contra qualquer um que não pertença ao grupo de vendedores.

Depois de concluir isso, usando ACLs, permite que um usuário crie um vendedor. Você notará que os ACOs criados para os produtos vão para os grupos de ACO que representam os vendedores. Como você configuraria as ACLs para que qualquer membro dos vendedores pudesse mudar o produto, mas apenas o criador dele pudesse excluí-lo?

Visualizações

Na visualização de índice de produtos, crie um modo de exibir apenas os botões Editar e Excluir para os produtos que o usuário pode editar ou excluir.


Resumo

Embora o scaffolding seja um ótimo modo de ver rapidamente seu aplicativo, o Bake é que deve ser usado quando for preciso montar rapidamente uma estrutura. Usando as ACLs do CakePHP, você pode ter muito controle em um nível bastante minucioso dentro do seu aplicativo. Esses são apenas alguns modos em que o CakePHP ajuda a facilitar sua vida e acelerar seu desenvolvimento.

A Parte 3 mostra como usar Sanitize, uma classe CakePHP útil, que ajuda a tornar um aplicativo seguro ao limpar os dados enviados pelo usuário.


Download

DescriçãoNomeTamanho
Part 2 source codeos-php-cake2.source.zip8KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=395935
ArticleTitle=Crie Web sites rapidamente com CakePHP, Parte 2: Faça mais e melhor com CakePHP
publish-date=06022009