Uma introdução ao framework CakePHP e suas contribuições para o desenvolvimento ágil.
Workshop 1: Desenvolvimento ágil com o CakePHP
Agenda
Apresentação Motivação Desenvolvimento ágil Princípios de desenvolvimento ágil Recursos ágeis do CakePHP O padrão MVC O padrão ORM Validações Aplicação exemplo (Ajax blog) Como aproveitar melhor tudo isso? Agradecimentos
Agenda
Apresentação
A 2km é uma empresa mineira especializada no desenvolvimento ágil de soluções para web utilizando software livre.
+20 projetos utilizando o CakePHP nos últimos 12 meses.
Desenvolveu projetos para empresas e organizações como Record Minas, PMDB-MG, Orca veículos entre outras.
Apresentação
Carlos Pires (Cadu) é bacharel em ciência da computação. Desenvolve e é apaixonado pela web e por software livre desde 1999. Trabalhou com Java(JEE) por mais de 6 anos em diversas empresas da capital mineira. Ultimamente tem se dedicado ao CakePHP e à jQuery. Quando não está andando de bike, está estudando línguas (inglês e espanhol), curtindo um samba de raíz ou tomando uma cervejinha com os amigos.
Daniel Golgher é tecnólogo em Processamento de Dados, Bacharel em Sistema de Informação e Especialista em Engenharia de Software. Desenvolve em PHP desde 2001. Gosta de software livre, especialmente dos projetos: CakePHP, FreeBSD, MySQL, Apache, PHP, Python dentre outros. Nas horas vagas vai ao cinema com a namorada e passeia com o Baco (São Bernardo).
Agenda
Motivação
Divulgar o framework CakePHP Divulgar os novos cursos da 2km Retribuir à comunidade de software livre Combater o código 'espaguete' Mostrar que programar pode ser divertido!
Agenda
Desenvolvimento ágil
Estórias de usuário Programação em Pares Programação orientada ao teste (TDD) Refatoração (Refactoring) Reunião em pé (Stand up meeting) Sprints
Agenda
Princípios do desenvolvimento ágil
COC – Convention Over Con!guration DRY - Don't Repeat Yourself KISS - Keep It Short and Simple or Keep It Simple Stupid SoC - Separation of Concerns YAGNI - You Ain't Gonna Need It
Agenda
Recursos ágeis do CakePHP
MVC [CoC e SoC] ORM [CoC] Validação [DRY e KISS] Testes unitários (SimpleTest) [TDD] Helpers, Components e Behaviors [DRY] Geração de código (Bake) [Productivity]
Agenda
Código espaguete?
Fonte: http://desciclo.pedia.ws/wiki/PHP
<div style='display:<?=$valor?$tipo1:$tipo2; ?>'><? if($flag == 0){ ?><script> var a = [<?=implode(',',$lista)?>]; <? $SQL = "SELECT * FROM clientes WHERE 1 ORDER BY data ASC LIMIT 1 OFFSET 1 " ?><? }else{ ?><b> entroh aqui flag= <?=$flag //debugue ?> </b> <? $SQL = "SELECT * FROM clientes WHERE ".$cond." ORDER BY data ASC LIMIT 1 OFFSET ".$flag ?><script> // var a= [<?=implode(',',$lista_)?>];<? } ?> // debugue alert(a);
<<?php echo '/'."script"; // kuIDaDeNHo c/AxXx bAhRRaxXXxx ?>>
</div>
Código MVC
<?php//Arquivo da Classe de Modeloclass Usuario extends AppModel {
var $name = 'Usuario';var $displayField = 'nome';
}?>
<?php//Arquivo da Classe de Controleclass UsuariosController extends AppController {
function teste($grupo_id=null){ $usuarios=$this->Usuario->find('list',array('conditions'=>array('grupo_id'=>$grupo_id))); $this->set(compact('usuarios'));}
}?>
<?php//Arquivo da Classe de Visão$form->create('Usuario',array('action'=>'teste'));$form->input('usuario');$form->end('Enviar');?>
models/usuario.php
controllers/usuarios_controller.php
views/usuarios/teste.ctp
Código em uma camada?
<?php$conn = mysql_connect('localhost','root','senha');mysql_select_db('meu_banco',$conn);$query = 'SELECT id,nome FROM usuarios WHERE grupo="'.$_POST['grupo_id'].'"';$result = mysql_query($query);?><html>
<head>...</head><body> <form action="teste.php"> Selecione o usuário: <select name='usuario'> <?php while($row = mysql_fetch_row($result)){ echo"<option id=".$row[0].">".$row[1]."</option>"; } ?> </select> <input type='submit'> </form></body>
</html>
Exemplo de código em uma camada
Agenda
O padrão ORM
2
1
4
3
Tabelas e relacionamentos
O padrão ORM <?php
class Produto extends AppModel {
var $name="Produto"; var $hasOne = array('Descricao'); var $hasAndBelongsToMany = array('Usuario'); var $belongsTo = array('Categoria' => array( 'className' => 'Categoria', 'foreignKey' => 'categoria_id') ); var $hasMany = array('Comentario' => array( 'className' => 'Comentario', 'foreignKey' => 'comentario_id', 'dependent' => false, 'conditions' => '', 'fields' => '', 'order' => '', 'limit' => ’’ ) );}
?>
1 2
3
4
Classe do modelo Produto com os relacionamentos
Agenda
Validações var $validate = array(
'login' => array('isUnique' => array( 'rule'=>'isUnique', 'message'=>'Este e-mail já existe na base de dados.'), 'email' => array( 'rule' => 'email', 'message' => 'O campo E-mail deve ser um email válido')),'senha' => array( 'rule' => array( 'confirmaSenha', 'senha'), 'message' => 'O campo senha e a confirmação da senha não conferem'),'confirma_senha' => array( 'rule' => 'alphanumeric', 'required' => true, 'message'=>'O campo confirmação da senha é obrigatório', 'on'=>'create'),'nome' => array( 'rule' => array('between', 2, 64), 'message' => 'O campo nome deve possuir de 2 a 64 caracteres') );
function confirmaSenha($data) { return $data ['senha'] == $this->data ['Usuario'] ['confirma_senha']; }
models/usuario.php
Agenda
Aplicação Exemplo: Ajax blog
CREATE TABLE `posts` ( `id` int(10) unsigned NOT NULL auto_increment,
`title` varchar(50) default NULL,`body` text,`created` datetime default NULL,`updated` datetime default NULL,PRIMARY KEY (`id`),KEY `title` (`title`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Estrutura da tabela posts da aplicação exemplo
Ajax blog: Con!guração do banco
<?phpclass DATABASE_CONFIG {
var $default = array( 'driver' => 'mysql', 'connect' => 'mysql_connect', 'host' => 'localhost', 'login' => 'root', 'password' => 'passwd', 'database' => 'test', 'prefix' => '' );
}?>
Arquivo de con!guração do banco: con!g/database.php
Ajax blog: Camada de modelo
<?php
class Post extends AppModel {
var $name = 'Post';
}
?>
Classe de modelo da tabela posts: models/post.php
Ajax blog: Camada de controle <?phpclass PostsController extends AppController {
var $name = 'Posts';var $layout = 'ajax_blog';var $helpers = array ('html', 'javascript', 'ajax', 'time' );
function index() { $this->set ( 'data', $this->Post->find ( 'all' ) );}function view($id) { $this->layout = 'ajax'; $this->set ( 'data', $this->Post->read (null,$id) );}function delete($id = null) { if (isset ( $id )) { $id = substr ( $id, 5 ); } $this->Post->del ( $id ); $this->set ( 'data', $this->Post->find ( 'all' ) ); $this->render ( 'list', 'ajax' );}function add() { if (! empty ( $this->data )) { if ($this->Post->save ( $this->data )) { $this->set ( 'data', $this->Post->find ( 'all' ) ); $this->render ( 'list', 'ajax' ); } } else { $this->render ( 'add', 'ajax' ); }}
}?>
Classe de controle dos posts: controllers/posts_controller.php
Ajax blog: Layout padrão
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>CakePHP :: Ajax Blog :: <?php echo $title_for_layout?></title><?php echo $html->charset(); ?><?php echo $javascript->link(array('prototype','scriptaculous','zebra_tables')); ?><?php echo $html->css('css'); ?></head>
<body><div id="container"> <center><h2>Cake Framework AJAX Demo</h2></center>
<div id="content"> <?php echo $content_for_layout?>
</div> <div id="pb-cake"> <a href="http://www.cakephp.org/" class="simple"> <?php echo $html->image('cake.power.png',array( 'width'=>"80", 'height'=>"15", 'alt'=>"CakePHP :: A Rapid Development Framework", 'border'=>"0")); ?> </a> </div></div></body></html>
Layout padrão dos posts: views/layouts/ajax_blog.ctp
Ajax blog: View da index <form action="/posts/search"> <p> <?php echo $html->image('magnify.png', array('alt'=>'Magnify Icon')); ?> <b>Live Search:</b> <input type="text" name="livesearch" id="livesearch" size="40"> <?php echo $html->image('spinner.gif', array('alt'=>'Spinner', 'id'=>'search_spinner', 'style'=>'display:none;')); ?> </p></form><?php echo $ajax->observeField('livesearch', array(
'update'=>'post_table', 'url'=>"/posts/search", 'frequency'=>1,'loading'=>"Element.show('search_spinner');", 'complete'=>"Element.hide('search_spinner');stripe();"));
?><div id="wastebin" class="wastebin"> <center><b>Drag the post's title right here.</b></center> <div> <p id="indicator"> <?php echo $html->image('spinner.gif', array('alt'=>'Indicator')); ?> Deleting ... </p> </div></div><?php echo $ajax->dropRemote('wastebin',
array('accept'=>'title', 'hoverclass'=>'wastebin-active'), array('url'=>'/posts/delete/', 'with'=>'{id:element.id}', 'update'=>'post_table', 'loading'=>'Element.show(\'indicator\')', 'complete'=>'Element.hide(\'indicator\');stripe();' ));
?>...
Layout da view da index: views/posts/index.ctp
Ajax blog: Elemento da listagem dos posts
<td> <div class="title" id="post_<?php echo $post['id'];?>">
<?php echo $html->image('document.gif', array('alt'=>'MagnifyIcon')); ?> <?php echo $post['title']; ?> </div> <?php echo $ajax->drag('post_'.$post['id'], array('revert'=>'true')); ?>
</td> <td><?php echo date("d/m/Y", strtotime($post['created'])) ?> </td> <td> <?php echo $ajax->link('View',"/posts/view/{$post['id']}", array('fallback'=>'#view',
'update'=>"post_content", 'complete'=>"new Effect.Appear('post_content');")) ?>
| <?php echo $ajax->link('Edit', "/posts/edit/{$post['id']}", array('fallback'=>'#edit',
'update'=>"add_post", 'complete'=>"new Effect.Appear('add_post');")) ?>
| <?php echo $ajax->link('Delete', "/posts/delete/{$post['id']}", array('fallback'=>'#list',
'update'=>"post_table", 'complete'=>"stripe();")) ?>
</td>
Elemento da listagem dos posts: views/elements/ajax_posts_lists.ctp
Agenda
Como aproveitar melhor tudo isso?
Fazendo os cursos de desenvolvimento web que a 2km interativa! está lançando: Curso de CakePHP (Curso mais completo do CakePHP no Brasil - 60 horas) Curso de jQuery (Abordando as novidades da versão 1.3. Em breve) Curso de Padrões Web (em breve)
Agenda
Agradecimentos
Agradecemos a presença de todos neste sábado e esperamos vê-los nos próximos workshops!
Dúvidas, críticas, sugestões e doações para:
Carlos Pires (e-mail: [email protected] / Twitter: @cadu)
Daniel Golgher (e-mail: [email protected] / Twitter: @golgher)
http://www.2km.com.br