Feeds

Quarta-feira, 12 de Novembro de 2008

Tutorial MVC - 4 Inserir dados, Formulários e Validação

Este tutorial é um seguimento directo do anterior. Utilizaremos os mesmos componentes e tabela de dados para criar um sistema de registo de utilizadores. Se não tem acompanhado esta série aconselho-lhe a dar uma olhadela ao tutorial anterior.
No que diz respeito à validação, será usado o novo sistema form_validation introduzido na versão 1.7.0. Dado que este tutorial é orientado para iniciantes não vi razão pela qual não o orientar para a nova versão.

Em qualquer página web com validação de dados existe o seguinte ciclo de validação:

  1. Pedir os dados ao utilizador
  2. Verificar os dados
  3. Caso os dados não sejam válidos exibir erros e voltar ao ponto 1
  4. Tratar os dados

Começando pela solicitação de dados, vamos criar uma view user/register.php com o seguinte conteúdo:

<?php echo validation_errors() ?>

<?php echo form_open('user/register') ?>
<fieldset>
<label for="username">Utilizador: </label>
<input type="text" id="username" value="" />
<label for="password">Palavra-Passe:</label>
<input type="password" id="password" value="" />
<label for="confirm">Confirmar:</label>
<input type="password" id="confirm" value="" />
<label for="email">E-mail</label>
<input type="text" id="email" value="" />
</fieldset>
<input type="submit" value="Enviar" />
</form>

A view acima é um formulário típico em PHP/HTML. Na primeira linha serão imprimidas as mensagens de erro caso a validação dos dados falhe. Recorre-se também à função form_tag para gerar uma tag form que chamará o controlador responsável por validar e armazenar os dados.

Agora para armazenar os dados resta-nos criar o controlador para o efeito. Mas antes disso vamos preparar a nossa classe modelo para guardar este tipo de dados. Lembre-se que estamos a criar um formulário para registo de utilizadores e dados sensíveis como a password não serão armazenados no formato em que são inseridos. A palavra passe será transformada antes de ser armazenada recorrendo à função de hashing sha1 utilizando uma chave salt para impedir a obtenção da palavra passe através de dicionários com hashs conhecidos. Ao modelo user_model.php serão acrescentados os seguintes métodos:

<?php
private function hashPassword($password)
{
//gera uma string salt aleatória
$data['salt'] = sha1(rand());
//gera o hashing da password
$data['password'] = sha1($data['salt'].$password);
return $data;

}

function register()
{
$data['username'] = $this->input->post('username');
$hash = $this->hashPassword($this->input->post('passwd'));
$data['passwd'] = $hash['password'];
$data['salt'] = $hash['salt'];
$data['email'] = $this->input->post('email');

$this->db->insert('users', $data);
}
?>

O método register guarda os dados, depois de codificar a password. Usando o método insert do Active Record a inserção de dados não poderia ser mais simples, basta um array associativo em que o indice corresponde ao nome do campo na tabela e depois passá-lo como argumento da função insert. Para obter os dados enviados pelo formulário podem usar o método $this->input->post('nome') ou aceder directamente ao respectivo elemento no array post: $_POST['nome'].

O método hashPassword é responsável apenas por gerar um hash e um salt para a password.

Por fim no controlador user vamos criar um método register que, neste exemplo, será responsável por criar as regras e executar o ciclo de validação.
<?php

function register()
{
//definição da lista de regras a aplicar
$config=array(
array(
'field' => 'username',
'label' => 'Nome de Utilizador',
'rules' => 'required|min_length[4]|max_length[20]'
),
array(
'field' => 'passwd',
'label' => 'Palavra Passe',
'rules' => 'required|min_length[6]|matches[confirm]'
),
array(
'field' => 'confirm',
'label' => 'Confirmação de Palavra Passe',
'rules' => 'required'
),
array(
'field' => 'email',
'label' => 'E-mail',
'rules' => 'required|valid_email'
)
);

//atribui a lista de regras à próxima execução da validação
$this->form_validation->set_rules($config);
//Teste de validação de dados
if(($this->form_validation->run())==FALSE)
{
//caso a validação não tenha tido sucesso
//ou ainda não foi invocada
$this->load->view('user/register');
}
else
{
//se a validação teve sucesso
$this->user_model->register();
//redirecciona para uma página de sucesso
redirect('user/success');
}
}

?>

Neste exemplo recorremos ao método de criar um array com todas as regras de validação a aplicar. Nesse array cada elemento possui três campos:
  • field: possui o nome do campo a avaliar, deverá obrigatoriamente corresponder ao id do elemento do formulário que fornecerá esses dados.
  • label: contém uma identificação do campo, compreensível pelos utilizadores, a aparecer na mensagem de erro.
  • rules: aqui são declaradas as regras a ser aplicadas ao campo, as mais comuns são: required, campo obrigatório; min_length/max_length, definem os limites de tamanho em caracteres dos dados inseridos; valid_email, endereço de correio electrónico válido; etc...
A estrutura if no exemplo garante a execução do ciclo de validação. Na primeira chamada ao controlador, invocada pelo utilizador, a validação falha sempre sem qualquer erro, por isso é exibida a view com o formulário. O utilizador ao enviar os dados faz com que o controlador seja de novo invocado e a validação executada. Se falhar será re-exibido o formulário com as respectivas mensagens de erro. Caso os dados estejam correctamente inseridos, estes serão guardados na base de dados. Após o armazenamento o controlador redirecciona-nos para uma página de sucesso que não foi listada neste exemplo, dada a sua simplicidade deixo-a ao critério de cada um.

Quinta-feira, 16 de Outubro de 2008

Tutorial MVC - 3 Acesso a dados

Depois de termos dado uma visão global ao padrão MVC e de dar os primeiros passos com a framework CodeIgniter vamos abordar uma parte também importante de uma aplicação web: o acesso a bases de dados.

Criação da base de dados e configurar o acesso

Ao contrário de algumas frameworks, p.e.: Symfony, Django, etc... o CodeIgniter não possui nenhum mecanismo próprio para criação de bases de dados, à semelhança do que acontece no Ruby on Rails e CakePHP, pelo que esta terá que ser criada com as ferramentas fornecidas pelo seu SGBD. O CodeIgniter segue um padrão de acesso a dados conhecido como Active Record que será demonstrado mais à frente. Para este capítulo apenas iremos utilizar a seguinte tabela utilizando o MySQL:

CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(20) character set latin1 NOT NULL,
`passwd` varchar(40) character set latin1 NOT NULL,
`salt` varchar(40) character set latin1 NOT NULL,
`email` varchar(100) character set latin1 NOT NULL,
PRIMARY KEY
(`id`)
);
Agora vamos configurar o acesso à base de dados no CodeIgniter, dentro do directório application/config editem o ficheiro database.php:
$active_group = "default";
$active_record = TRUE;

$db['default']['hostname'] = "localhost";
$db['default']['username'] = "o_meu_nick";
$db['default']['password'] = "a_minha_pass";
$db['default']['database'] = "nome_da_bd";
$db['default']['dbdriver'] = "mysql";
$db['default']['dbprefix'] = "";
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = "";
$db['default']['char_set'] = "utf8";
$db['default']['dbcollat'] = "utf8_general_ci";


O ficheiro em sí não tem muito que saber, basta alterar o valor de cada elemento com os dados respectivos. Para seguir este tutorial será necessário ter a variável $active_record a TRUE. O passo seguinte será configurar o acesso à classe de base de dados. Isto pode ser feito de dois modos: activar o carregamento automático da classe ou carregá-la no construtor de cada Controller ou Model:
//---Carregamento "Manual"---
//em cada controlador ou modelo invocar
//$this->load->database()
class User extends Controller
{
//em PHP4: function Nome()
function __construct
()
{
parent
::Controller();
$this->load->database();
}
}

//---Carregamento automático
//para carregamento automático editar config/autoload.php
//e adicionar 'database' ao array $autoload['libraries']
$autoload['libraries'] = array('database');


Para verificar se a conexão à base de dados está a funcionar basta correr o seu browser e abrir o url da sua aplicação. Se não foi informado de algum erro, então está tudo a funcionar.

Nota: A escolha de utilização de carregamento automático ou não classes está dependente do tipo de projecto. Se for uma classe utilizada por todos os controladores recomenda-se o carregamento automático de modo a poupar algum esforço. Se essa classe apenas for utilizada por algumas funcionalidades então recomendo que se faça o carregamento apenas quando necessárias para evitar sobrecarregar os controladores com material desnecessário.

Ler registos da base de dados

A exibição de dados é certamente a tarefa mais comum em aplicações web. Para demonstrar como realizar este tipo de tarefas vamos criar duas actividades num controlador User em controllers/user.php:
  • user/all
  • user/show/$id
Para testar este exemplo recomendo que introduzam alguns dados na tabela users.
<?php
class User extends Controller
{
function __construct
()
{
parent
::Controller();
$this->load->helper('url');
}

function all
()
{
//selecciona todos os registos de users
$data['list']=$this->db->get('users');
//envia a lista de dados para a view
$this->load->view('user/all',$data);
}

function show
()
{
//vai buscar o id ao url, 3º elemento
//se não for escrito retorna 0
$username = $this->uri->segment(3,0);

if
($username == 0)
{
//se o id não foi fornecido redireciona
redirect
('user/all');
}
else
{
$result = $this->db->get_where(
'users',
array
('username' => $username));
//selecciona a única linha do resultado
$data['user'] = $result->row();
$this->load->view('user/show',$data);
}
}
}
?>

E agora as duas user/all e user/show:

user/all:
<h1>Lista de utilizadores</h1>

<table>
<tr>
<th>Username</th>
<th>Email</th>
<th></th>
</tr>

<?php foreach($list->result() as $user): ?>
<tr>
<td><?php echo $user->username ?></td>
<td><?php echo $user->email ?></td>
<td><?php echo anchor('user/show/'.$user->username);?></td>
</tr>
<?php endforeach ?>
</table>

user/show:
<p><b>Username:</b> <?php echo $user->username ?></p>
<p><b>E-mail:</b> <?php echo $user->email ?></p>
Os metodos mais importantes para este exempo são:
  • $this->db->get('tabela'): Devolve todos os dados da tabela (SELECT * FROM tabela)
  • $this->db->get_where('tabela', critérios): selecciona todos os registo que correspondam a um critério.
Os registos devolvidos são armazenados numa estrutura em memória da qual se pode extrair cada um deles como instância de um objecto Active Record, como neste exemplo, ou alternativamente num array associativo.
Neste exemplo foram usados dois métodos: result() e row(). O método result, usado na view user/all gera uma lista de objectos row, cada um deles representa um registo. O método row extrai directamente uma linha, o que é muito útil para casos como este em que a consulta à base de dados devolve uma única linha, caso devolva mais que uma linha o método row devolverá apenas a primeira linha.

Neste exemplo utilizamos o helper url, este fornece, entre outras, a função anchor que nos permite criar hyperlinks de forma dinâmica para outras funcionalidades da nossa aplicação. No exemlo acima podemos ver que esta é utilizada para criar ligações para a página show de cada registo.

Outra função a destacar que utilizamos é a função uri->segment que extrai elementos da nossa url, neste caso o username. O segundo parâmetro é opcional e indica o valor por omissão caso se invoque esa funcionalidade sem fornecer o username.

Modelos

Como foi explicado no primeiro tutorial, no padrão MVC o Model é responsável pela abstracção do acesso a dados. O que se passa no CodeIgniter é que o padrão Active Record já faz isso de uma forma mais geral. Pelo que é possível criar um projecto sem ter que implementar classes modelo, mas para casos mais especificos como consultas mais complexas é recomendado implementar essas funções numa classe Model.

Podemos reestruturar o exemplo anterior tendo em conta os seguintes aspectos:
  • Estamos a pedir à tabela todos os campos de um registo quando apenas utilizamos dois deles
  • Podemos criar métodos para obter os dados prontos a ser exibidos, encurtando o tamanho dos nossos controladores
Para isso vamos criar uma classe user_model.php no directório models:
<?php
class User_model extends Model
{
function getDisplayableByUsername
($username)
{
//indica os campos
$this->db->select('username, email');
$result = $this->db->get_where(
'users',
array('username' => $username));
return $result->row();
}

function getAllDisplayable
()
{
$this->db->select('username, email');
$result = $this->db->get('users');
return $result->result();
}
}

Neste exemplo criamos duas funções que retornam os resultados apenas com os dados que desejamos exibir. O método select da classe db encarrega-se de definir os campos a retornar pela próxima consulta à base de dados.
Agora falta-nos alterar o controlador para utilizar o model.
<?php
class User extends Controller
{
function __construct()
{
parent::Controller();
$this->load->helper('url');
$this->load->model('user_model');
}

function all()
{
//selecciona todos os registos de users
//a ser exibidos
$data['list'] =
$this->user_model->getAllDisplayable();
$this->load->view('user/all',$data);
}

function show()
{
//vai buscar o id ao url, 3º elemento
//se não for escrito retorna 0
$username = $this->uri->segment(3,0);

if($username == 0)
{
redirect('user/all');
}
else
{
$data['user'] =
$this->user_model->GetDisplayableByUsername($username);
$this->load->view('user/show',$data);
}
}
}
?>


Para completar, como o método GetAllDisplayable já retorna uma lista result devemos ir ao código da view user/all e remover a chamada ao método result() no ciclo foreach para que tudo funcione devidamente.

Este capítulo fica por aqui, no próximo serão abordados os temas de adição e edição de registos, assim como a validação dos dados introduzidos pelos utilizadores.
Até à próxima.

Segunda-feira, 13 de Outubro de 2008

Tutorial MVC - 2 CodeIgniter, Primeiros Passos

Primeiros passos
Para utilizar esta framework basta qualquer servidor HTTP com suporte a PHP. É extremamente provável que a aplicação que deseja desenvolver necessite de uma base de dados, estão disponíveis os motores livres PostgreSQL, MySQL e SQLite juntamente com outros proprietários.

O primeiro passo é a instalação da framework. Nada mais simples.

  • Fazer o download da framework aqui.
  • Descomprimir o ficheiro.
  • E copiar o conteúdo o conteúdo do directório CodeIgniter_x.x.xx para o seu directório web.
Nota: Evite desenvolver a partir do seu servidor de alojamento. Se desejar trabalhar num ambiente semelhante a um ambiente real, será boa ideia recorrer a máquinas virtuais.

Depois no web browser abrir: http://localhost/index.php, se tudo correu bem então está pronto a começar a trabalhar.

Como ambiente de desenvolvimento recomendo a IDE Eclipse com o plugin PHP Edit (ou mesmo o PDT). É uma ferramenta bastante robusta, mas de fácil utilização e a de completação automática de código funciona bem com esta framework.

Podem fazer download a partir do site oficial e instalar o plugin PHP posteriormente, ou fazer o download personalizado com as funcionalidades que desejar no site yoxos.

Estrutura de directórios

A framework é constituída por um conjunto de directórios por onde estão espalhados os diversos componentes que a compõem. Ao instalar verá que existe um directório de documentação na raiz, poderá mantê-lo ou apagá-lo, poderá mantê-lo durante o desenvolvimento da aplicação, mas deverá apagá-lo quando disponibilizar a aplicação online, a não ser que queira disponibilizar essa documentação online.
Existe também um ficheiro index.php, este tem obrigatoriamente que existir pois todos os pedidos à aplicação arrancam a partir dele.
Por fim temos o directório system onde existem os seguintes directórios:
  • application: este será o directório onde fará o desenvolvimento da sua aplicação. Em condições normais será o único a sofrer alterações durante o desenvolvimento.
  • cache: a cache do sistema, onde são criados ficheiros temporários que permitem o aumento da performance da aplicação.
  • codeigniter: o "motor" da framework.
  • database: controladores de base de dados.
  • fonts: tipos de letra.
  • helpers: colecções de funções de auxilio ao desenvolvimento que, por exemplo, poderão ajudar a criar elementos de uma página de uma forma mais simples.
  • language: ficheiros com termos usados pela framework numa determinada língua. Instalação do Português padrão.
  • library: biblioteca com as classes de referência que podem ser utilizadas no desenvolvimento da aplicação.
  • logs: logs de sistema
  • plugins: similar aos helpers, só que em vez de uma colecção de funções os plugins apenas disponibilizam uma função.
  • scaffolding: motor de scaffolding. Permite a gerar automaticamente páginas de visualização, criação, edição e eliminação de dados a partir de tabelas da base de dados. Esta ferramenta apenas deve ser utilizada durante o desenvolvimento pois não oferece qualquer garantia de segurança. Este modelo de scaffolding deixou de ser oficialmente suportado e será substituido, pelo que não deverá ser abordado para já nesta série de tutoriais.
Dentro da pasta application temos um conjunto de directórios orientado para o desenvolvimento da aplicação. Os directórios helpers, languages e libraries são equivalentes aos que encontramos na directoria system, mas neste caso destinam-se a componentes criados especificamente para o seu projecto. Os restantes directórios são os seguintes:
  • config: ficheiros de configuração.
  • controllers: local onde criará os seus controllers.
  • errors: páginas de erro personalizadas, como por exemplo o famoso 404-Not Found
  • hooks: extensões ao CodeIgniter, onde poderá criar funções ou classes que serão executadas em determinados momentos do fluxo de processamento da aplicação.
  • models: local onde criará os seus models.
  • views: local onde criará as suas views.
Olá mundo

Para variar vamos começar com a criação de um programa Olá Mundo.

A maneira mais simples de o fazer é a utilizar apenas um controlador, vá à pasta controllers e crie um ficheiro chamado primeiro.php e edite-o. Nele escreva o seguinte código:

<?php
class Primeiro extends Controller
{
//em PHP4: function Primeiro()
function __construct()
{
parent::Controller();
}

function index()
{
echo 'Olá Mundo'
}
}
?>

Se digitar no seu web browser o endereço http://localhost/index.php/primeiro obterá uma página com o texto Olá Mundo.

O exemplo começa pela declaração de uma classe que herda as características de classe do CodeIgniter Controller, todos os controllers são subclasses de Controller, o nome da classe e do ficheiro são iguais, a única diferença é que o nome da classe deve começar sempre com maiúscula. Depois temos duas funções: a função Primeiro() é o construtor da classe, que neste caso apenas chama o construtor da sua superclasse, este pode ser usado para carregar qualquer tipo de componente do sistema; a segunda função é uma actividade do controlador, normalmente todos os controladores têm uma função index que é chamada sempre que esse controlador é invocado sem que seja invocada qualquer função.

Vamos acrescentar mais uma função ao nosso controlador:
<?php
class Primeiro extends Controller
{
(...)

function opiniao
()
{
echo
'Estou a gostar muito desta ferramenta';
}
}
?>

Esta função é similar à anterior, apenas serve para demonstrar o funcionamento dos endereços. Neste caso, para invocar esta actividade do controlador deverá digitar o endereço: http://localhost/index.php/primeiro/opiniao

A estrutura padrão dos endereços é:
...index.php/Nome do Controlador/Actividade/Informação Complementar
Caso não seja especificada nenhuma actividade será sempre executada a função index, como no primeiro exemplo, pelo que deverá existir sempre um index em cada controlador.

Views

As views são ficheiros PHP que contêm código HTML e chamadas PHP responsáveis pela apresentação dos dados. No exemplo anterior eram os controladores que apresentavam qualquer conteúdo na página, o que não é prático nem segue a norma MVC, pelo que vamos fazer algumas alterações.

Primeiro vamos acrescentar uma nova view, no directório views crie o ficheiro mensagem.php com o seguinte conteúdo:
<html>
<head>
<title><?php echo $title ?></title>
</head>

<body>
<h1><?php echo $message ?></h1>
</body>
</html>

Esta view será invocada por ambos os controladores e como podemos observar serão passadas duas variáveis: title que definirá o titulo a aparecer na janela do browser e message que terá a mensagem que aparecerá na página web.
Para utilizar a view vamos alterar ambas as acções do controlador.
<?php
class Primeiro extends Controller
{
//em PHP4: function Primeiro()
function __construct()
{

parent::Controller();
}

function index()
{
$data['title']='Primeiro Exemplo';
$data['message']='Olá Mundo!';
$this->load->view('mensagem',$data);
}

function opiniao()
{
$data['title']='A minha opinião';
$data['message']='Estou a gostar muito desta ferramenta'
;
$this->load->view('mensagem',$data);
}
}
?>

Agora o funcionamento ficou ligeiramente alterado. Para passar dados a uma view é necessário criar um vector associativo em que o índice terá correspondência a uma variável com o mesmo nome na view. A última linha de cada actividade é uma chamada à view onde o primeiro parâmetro corresponde ao nome do ficheiro da view (a extensão PHP pode ser omitida), e o segundo corresponde à estrutura com os dados a ser passados. Não há qualquer obrigação de passar dados para uma view, pelo que nestes casos deverá apenas invocar a view apenas com o primeiro parâmetro.

Para uma view podem ser passados dados de qualquer tipo, sejam eles numéricos, texto, objectos ou outras estruturas de dados. O próximo exemplo demonstra como passar um vector para a view e como este normalmente é tratado.
Crie uma nova view chamada topics.php com o seguinte conteúdo:
<html>
<head>
<title>Uma lista de tópicos</title>
</head>

<body>
<ul>
<?php foreach($topics as $topic): ?>
<li><?php echo $topic ?></li>
<?php endforeach ?>
</ul>

</body>

</html>
Aqui a única diferença é que é usado um ciclo foreach para escrever cada um dos elementos na página.

Para pôr esta view a funcionar necessitamos de criar uma actividade no nosso controlador que o invoque, por isso vamos adicionar a função topics():
function topics()
{
$data['topics'] = array('Model','View','Controller');
$this->load->view('topics',$data);
}

Nada mais simples.

No directório views é possível criar subdirectórios para as organizar. Para invocar essas views deverá incluir o nome do directório na sua chamada:
$this->load->view('directory/topics');
E assim terminamos este capítulo, no próximo serão abordados o uso de bases de dados e os modelos.