23 de novembro de 2012

Formulários no Doctrine: Validando a existência do login no Banco de Dados

Ao gerar um formulário para cadastro de usuários em um sistema, usualmente é desejado validar as informações inseridas pelos usuários nos campos do formulário. No caso de um campo que vai ser utilizado na identificação do usuário para este se logar no sistema (login), como por exemplo um apelido (nick) ou um email, deve-se ter a restrição de que nenhum outro usuário já esteja cadastrado com este campo. Um exemplo de duplicação do 'login' ocorre no filme Irmão Gêmeos, onde Julius Benedict (Arnold Schwarzenegger) e Vincent Benedict (Danny DeVito) possuem o mesmo código genético  


Figura 1: exemplo de duplicação de login.  


Neste artigo vai ser apresentado como a classe Zend_Validate_Abstract pode ser estendida para utilizar o Doctrine para validar se o login que está sendo inserido não existe no Banco de Dados. Caso já exista um usuário cadastrado com este login é retornado um código de erro. Além disto, vai ser apresentado um exemplo de como esta classe pode ser utilizado em um formulário de cadastro. 

A seguir é apresentada a classe DoctrineValidateEmail, que estende a Zend_Validate_Abstract e permite a validação do login (neste exemplo básico login = email do usuário). Basicamente nesta classe foi realizada a reimplementação da função isValid que utiliza uma classe DAO (UsuarioDAO) que valida o login passado como parâmetro.


Classe DoctrineValidateEmail


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php 

class DoctrineValidateEmail extends \Zend_Validate_Abstract{
    
    const NOK = 1;
    
    protected $_messageTemplates = array(
        self::NOK => "%value% já cadastrado"
    );
    
    public function isValid($email){
        
        try{
            $user = UserDAO::getUserByEmail($email);
        }catch(\Doctrine\ORM\NoResultException $e){
            // Email livre para cadastro
            $user = null;
        }
        if($user){
            $this->_error(self::NOK,$email);
            return false;
        }
        return true;
    }
}

A classe UserDAO que interage com o Banco de Dados utilizando o Doctrine é apresentada a seguir. Para simplificar o exemplo é apresentado apenas a implementação do método getUserByEmail utilizado pela DoctrineValidateEmail.


Classe DAO para interação com o Banco de Dados


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php

class UserDAO {
    
    public static function getUserByEmail($email) {
        /**Obtendo o container contendo as operações básicas envolvendo o doctrine. */
        $doctrine_container = \Zend_Registry::get('doctrine');
        $em = $doctrine_container->getEntityManager();
               
        
        $query = $em->createQuery('select s from UserDoctrine s WHERE s.email = ?1');
        $query->setParameter(1, $email);
        
        $user_doctrine = $query->getSingleResult();
        
        return $user_doctrine;        
    } // function
} // class


E finalmente é apresentado como a classe DoctrineValidateEmail implementada pode ser utilizada em um formulário para validar o login que está sendo inserido pelo usuário.


Exemplo de Formulário


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php

class Cadastro_Form_Cadastro extends Zend_Form
{

    public function init()
    {
        $this->setMethod('post');

        // CSFR (www.frameword.zend.com/manual/en/learning.quickstart.create-form.html)
        $this->addElement('hash', 'csrf', array('ignore' => true));

        // Nome
        $this->addElement(
                'text', 'nome', array(
            'label' => 'Nome:',
            'required' => true,
            'maxlength' => '30',        
            'filters' => array('StringTrim'),
        ));

        // Sobrenome
        $this->addElement(
                'text', 'sobrenome', array(
            'label' => 'Sobrenome:',
            'required' => true,
            'maxlength' => '30',        
            'filters' => array('StringTrim'),
        ));
  
        // Email vai ser utilizado como login
        $email = new Zend_Form_Element_Text('email');
        $email->setLabel('Email:');
        $email->addFilters(array('StringTrim', 'StripTags'));
        $email->addValidator('EmailAddress', TRUE);
        $email->setRequired(true);

        //Adicionando a validação no email                
        $email->addValidator(new DoctrineValidateEmail($email->getValue()));

        $this->addElement($email);

        //Senha        
        $this->addElement(
                'password', 'password', array(
            'label' => 'Senha:',
            'required' => true,
            'maxlength' => '20',        
                )
        );

        $this->addElement(
                'submit', 'submit', array(
            'ignore' => true,
            'label' => 'Cadastrar',
                )
        );
    }
}



1 de setembro de 2012

Herança no Doctrine: Persistindo e recuperando objetos

Em um artigo anterior foi apresentando como a herança pode ser implementada no Doctrine. Neste artigo vamos apresentar como os elementos da herança podem ser persistidos e recuperados do Banco de Dados

Para exemplo didático vamos utilizar novamente o exemplo épico do filme Star Wars (Figura 1), onde existe uma relação de herança entre: DarthVader, Luke Skywalker e Leia Organa.


Figura 1: Exemplo didático de herança. 



As classes que representam cada um dos elementos podem ser visualizadas no post anterior


Para o conjunto de classes do exemplo vamos implementar uma classe DAO (Data Access Object) para interagir com o Banco de Dados. A seguir é apresentado o código da classe DAO:

Classe JediDAO



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

class JediDAO {
   
    public static function add($object) {
        /**Obtendo o container contendo as operações básicas envolvendo o doctrine. */
        $doctrine_container = \Zend_Registry::get('doctrine');
        $em = $doctrine_container->getEntityManager();

        if($object->getId()){
            $object = $em->merge($object);
        }else{
            $em->persist($object);
        }
        
        $em->flush();

        return $object;
    }
    
    public static function get($id) {
        /**Obtendo o container contendo as operações básicas envolvendo o doctrine. */
        $doctrine_container = \Zend_Registry::get('doctrine');
        $em = $doctrine_container->getEntityManager();
                       
        $object = $em->getRepository('DarthVader')->find($id);

        return $object;
    } // function
    
    public static function listAll(){
        /**Obtendo o container contendo as operações básicas envolvendo o doctrine. */
        $doctrine_container = \Zend_Registry::get('doctrine');
        $em = $doctrine_container->getEntityManager();
        
        $query = $em->createQuery('select obj from DarthVader obj');
        
        $object_list = $query->getResult();
        
        return $object_list;        
    }
    
} // class

Agora vamos testar os métodos implementados na classe DAO:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php 
        //Criando um objeto DarthVader
        $darth_1 = new  DarthVader();
        $darth_1->setAtributoAnakin(1);
        
        //Persistindo o objeto
        JediDAO::add($darth_1);
        
        // Criando um objeto Luke
        $luke_1 = new Luke();
        $luke_1->setAtributoLuke(2);
        
        JediDAO::add($luke_1);
        
        //Criando um objeto Leia
        $leia_1 = new Leia();
        $leia_1->setAtributoLeia(3);
        
        JediDAO::add($leia_1);
        
        // Recuperando o objeto de id = 2
        $jedi_2 = JediDAO::get(2);
        
        /*
          var_dump em $jedi_2 retorna: 
            object(Luke)[198]
              private 'atributo_luke' => int 2
              private 'id' (DarthVader) => int 2
              private 'atributo_anakin' (DarthVader) => null

        */
        
        //Listando todos os objetos jedi's do Banco
        $lista_jedis = JediDAO::listAll();
        
        /*
        var_dump em $lista_jedis retorna um array:

        array (size=3)
        0 => 
            object(DarthVader)[209]
              private 'id' => int 1
              private 'atributo_anakin' => int 1
        1 => 
            object(Luke)[198]
              private 'atributo_luke' => int 2
              private 'id' (DarthVader) => int 2
              private 'atributo_anakin' (DarthVader) => null
        2 => 
            object(Leia)[208]
              private 'atributo_leia' => int 3
              private 'id' (DarthVader) => int 3
              private 'atributo_anakin' (DarthVader) => null
 
        */        


1 de agosto de 2012

Projeto exemplo integrando o Zend Framework e o Doctrine

O início sempre é a parte mais complicada para alguém que está aprendendo um determinado framework, pois ele se depara com uma quantidade grande de novas informações a serem "digeridas".  



Na maioria das vezes falta um projeto básico "Hello World" para apresentar as funcionalidades básicas para o usuário. Algo semelhante ao que ocorre no aprendizado das linguagens de programação.

Em função disto, o ZeDoc foi criado para apresentar um projeto (ou template) exemplo utilizando o Zend Framework em conjunto com o Doctrine, contendo algumas funcionalidades básicas para um projeto, como por exemplo:


Informações mais detalhadas sobre o projeto (como instalar, funcionalidades implementadas) podem ser encontradas em: no site do projeto, aqui mesmo (em futuros artigos) e ali (em futuros artigos).  

25 de julho de 2012

Implementando Herança no Doctrine

Nesse artigo vamos falar um pouco sobre a implementação de Herança no Doctrine. Como exemplo didático de herança, temos a épica relação entre DarthVader,  Luke Skywalker e Leia Organa no filme Star Wars (Figura 1). Em vista disto, vamos utilizar esta relação entre os personagens para ilustrar a implementação da herança no Doctrine


Figura: "I am your father". 

A herança no Doctrine é especificada através dos atributos: 


  • @DiscriminatorColumn: utilizado para indicar qual o elemento da hierarquia (classe) a linha da tabela representa.
  • @DiscriminatorMap: utilizado para identificar quais os valores que existirão na coluna do discriminador e qual classe da hierarquia está mapeado para cada valor.
  • @InheritanceType: representa a estrutura no Banco de Dados que irá armazenar a herança. SINGLE_TABLE indicada que tanto o objeto pai quanto os objetos filhos estarão representados na mesma tabela. JOINED indica que cada classe da hierarquia terá uma tabela própria. 


Por exemplo, tendo o mapa:

@DiscriminatorMap({"father" = "DarthVader", "son" = "Luke", "daughter" = "Leia"})

temos que father representa um objeto da classe DarthVader, já son representa um elemento da classe Luke.


A seguir um exemplo de implementação da herança. São especificadas três classes: DarthVader (classe pai), Luke (classe filho) e Leia (classe filho). 


Classe DarthVader (pai)



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

/**
 * @Entity
 * @InheritanceType("SINGLE_TABLE")
 * @Table(name="father")
 * @DiscriminatorColumn(name="discr", type="string")
 * @DiscriminatorMap({"father" = "DarthVader", "son" = "Luke", "daughter" = "Leia"})
 */
class DarthVader{
    
    /**
     * @var integer $id
     * @Column(type="integer")
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    
    public function setId($id){$this->id = $id;}
    public function getId(){return $this->id;}
    
    /**
     * 
     * @Column(type="integer")
     * 
     */
    private $atributo_anakin;    
    
    public function setAtributoAnakin($atributo){$this->atributo_anakin=$atributo;}
    public function getAtributoAnakin(){return $this->atributo_anakin;}
}


Classe Luke (filho)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php

/**
 * 
 * @Entity
 * @Table(name="son") 
 */
class Luke extends DarthVader {
    
    /**
     * 
     * @Column(type="integer")
     * 
     */
    private $atributo_luke;    
    
    public function setAtributoLuke($atributo){$this->atributo_luke=$atributo;}
    public function getAtributoLuke(){return $this->atributo_luke;}
    
}


Classe Leia (filho)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

/**
 * 
 * @Entity
 * @Table(name="daughter") 
 */
class Leia extends DarthVader {
    
    /**
     * 
     * @Column(type="integer")
     * 
     */
    private $atributo_leia;    
    
    public function setAtributoLeia($atributo){$this->atributo_leia=$atributo;}
    public function getAtributoLeia(){return $this->atributo_leia;}
    
    
}


A seguir a estrutura gerada no Banco de Dados para cada tipo de herança (InheritanceType).

JOINED


Figura: Tipo de Herança JOINED



SINGLE_TABLE



Figura: Tipo de Herança SINGLE_TABLE



Num próximo artigo vamos falar como ocorre o processo de persistência dos objetos Doctrine no Banco de Dados.

18 de julho de 2012

Adicionando Javascript nas views do Zend Framework



A inserção de código Javascript nas view do Zend Framework pode ser realizada através da utilização de dois helpers (classes) existentes: HeadScript Helper e InlineScriptHelper. Essas entidades podem ser utilizadas para adicionar diretamente o código javascript ou anexar um arquivo contendo o código javascript



HeadScript é utilizado quando se deseja referenciar o código javascript na marcação (tag) header da página. Para utilização(*) do HeadScript deve-se:

1) No Controller adicionar o endereço do arquivo no HeadScript da view.

  • Exemplo: <?php $this->view->headScript()->appendFile('/js/funcoes.js'); ?>


2) Na view imprimir o conteúdo do HeadScript:

  • Exemplo: <?php echo $this->headScript(); ?>



InlineScript é utilizado quando se deseja referenciar o código javascript na marcação (tag) body da página, ou seja, deseja-se anexar o javascript em uma determinada "posição" da página.  Para utilização(*) do InlineScript deve-se: 


1) No Controller adicionar o endereço do arquivo no InlineScript da view.

  • Exemplo: <?php $this->view->inlineScript()->appendFile('/js/funcoes.js'); ?>


2) Na view imprimir o conteúdo do InlineScript na posição desejada para o código javascript. Geralmente é recomendado imprimir o conteúdo no fim do código da view.

  • Exemplo: <?php echo $this->inlineScript(); ?>


(*) Apenas salientando que os exemplos englobam a inserção de uma referência a um arquivo .js na página. Para inserir diretamente um código javascript  verificar a função appendScript().

2 de julho de 2012

Singletons

''Se o singleton fosse um meme ele seria o Forever Alone.'' 


Forever Alone é um meme que representa o nível extremo de solidão de uma pessoa. O Singleton é um Padrão de Projeto de Software (do inglês Design Patternque restringe a instanciação de uma classe a apenas um único objeto. Como podemos observar o comportamento do Singleton é semelhante ao de nosso amigo Forever Alone, ou seja, por mais que deseje, ele não vai encontrar ninguém de seu tipo (classe) para consolá-lo na solidão e terá que chorar as pitangas sozinho.

Deixando um pouco o blablablá envolvendo Singletons, Padrões de Projetos e memes a seguir estão 2 exemplos de implementação de uma classe Singleton em PHP, sendo que apenas a primeira implementação está correta. Para cada uma das implementações são realizados alguns testes básicos para validar (ou invalidar) a implementação.  


Implementação correta: definindo um objeto estático para a classe e deixar o construtor e o método clone como privados.

1:  <?php   
2:  class ForeverAlone{  
3:       /**Instância única*/  
4:       private static $instancia = null;  
5:       /**Quantidade de chamadas ao construtor*/  
6:       private static $chamadas_construtor = 0;  
7:       /**Quantidade de chamadas ao método getInstance()*/  
8:       private static $chamadas_instancia = 0;  
9:       private function __construct(){  
10:            // construtor privado  
11:            self::$chamadas_construtor;  
12:       }  
13:       private function __clone(){  
14:            // clone privado   
15:       }  
16:       public static function getInstance(){  
17:            self::$chamadas_instancia++;  
18:            if(self::$instancia == null){  
19:                 self::$instancia = new self();  
20:            }  
21:            return self::$instancia;  
22:       }  
23:       public function getStats(){  
24:            return 'Chamadas ao Construtor: ' . self::$chamadas_construtor . "\n" .   
25:            'Chamadas ao getInstance(): ' . self::$chamadas_instancia . "\n";  
26:       }  
27:  }  



Implementação errada: deixando o construtor e o método clone como public (ou esquecendo de implementar eles).


1:  <?php   
2:  class ForeverAlone{  
3:       /**Instância única*/  
4:       private static $instancia = null;  
5:       /**Quantidade de chamadas ao construtor*/  
6:       private static $chamadas_construtor = 0;  
7:       /**Quantidade de chamadas ao método getInstance()*/  
8:       private static $chamadas_instancia = 0;  
9:       public function __construct(){  
10:            // construtor privado  
11:            self::$chamadas_construtor;  
12:       }  
13:       public function __clone(){  
14:            // clone privado   
15:       }  
16:       public static function getInstance(){  
17:            self::$chamadas_instancia++;  
18:            if(self::$instancia == null){  
19:                 self::$instancia = new self();  
20:            }  
21:            return self::$instancia;  
22:       }  
23:       public function getStats(){  
24:            return 'Chamadas ao Construtor: ' . self::$chamadas_construtor . "\n" .   
25:            'Chamadas ao getInstance(): ' . self::$chamadas_instancia . "\n";  
26:       }  
27:  }  


Testes


1:  <?php  
2:  include('ForeverAlone.php');  
3:  $objeto1 = ForeverAlone::getInstance();  
4:  // Imprimindo as estatísticas  
5:  echo $objeto1->getStats();  
6:  // Nova chamada ao getInstance  
7:  $objeto2 = ForeverAlone::getInstance();  
8:  echo $objeto2->getStats();  
9:  // Chamada ao construtor  
10:  $objeto3 = new ForeverAlone(); // Na implementação correta gera erro, pois o método é privado  
11:  echo $objeto3->getStats();  
12:  // Chamada ao clone  
13:  $objeto4 = clone $objeto1;  // Na implementação correta gera erro, pois o método é privado
14:  echo $objeto4->getStats();  
15:  ?>