segunda-feira, 12 de setembro de 2011

Codeigniter 2 + STI + PHPActiveRecord (parte 3)



Para validar o funcionamento do ORM em conjunto com o Codeigniter e o CIUnit vamos criar uma  simples aplicação acadêmica como prova de conceito guiada por testes e aplicando alguns design paterns quando necessário.

Requisitos:
  1. Um Curso é composto por uma ou mais Disciplinas
  2. Uma Disciplina pode fazer parte de nenhum ou muitos Cursos
  3. Cada Disciplina deve ser Ministrada por um único Professor Responsável
  4. Cada Disciplina pode pode ter outros Professores Adjuntos
  5. Um Professor pode Ministrar uma ou muitas Disciplinas diferentes
  6. Cada Turma de Estudantes deve ter uma Sala de aula
  7. Um Estudante pode se Matricular em um ou vários Cursos ou ainda em Disciplinas Isoladas
As palavras destacadas sugerem possíveis classes que compõe o sistema e são expressas no diagrama inicial abaixo.  O diagrama ainda deve crescer e ser alterado durante o desenvolvimento do sistema.

Diagrama de classes de exemplo

Iniciando com TDD

Iniciar criando a hierarquia de classes que representa a herança entre Teachers (Professores) e Students (Estudantes)

Criar um testes para validar a classe Person (Pessoas) que é a classe pai da herança.  Para simplificar vamos mapear a herança para nosso modelo relaciona em uma tabela simples (STI-Single Table Inheritance) incluindo um campo type na tabela para definir o tipo de objeto a que se refere a linha.

Criar a primeira classe de teste. Somente para prova de conceito vamos testar a herança na própria classe Person, mais tarde este testes devem ser refatorado.

Rodar os testes e ver falhar o primeiro teste que valida as classes que fazem parte da herança STI e representam os papéis Teachers e Students
$  phpunit --testdox --color models/PersonTest.php
$  phpunit --testdox --group models => só as classes com anotações


Tabelas para comportar o STI

Criar a tabela do topo da herança (peoples) e definir um campo (type) que vai receber o tipo de classe ao qual o registro se refere.
CREATE TABLE  `academico_test`.`people` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `type` varchar(150) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Criar uma view para mapear cada subclasse pelo tipo definido

CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` 
SQL SECURITY DEFINER VIEW  `academico_test`.`students` AS 
select `people`.`id` AS `id`,`people`.`name` AS `name`,`people`.`type` AS `type` 
from `people` 
where (`people`.`type` = 'Student')

CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` 
SQL SECURITY DEFINER VIEW  `academico_test`.`teachers` AS 
select `people`.`id` AS `id`,`people`.`name` AS `name`,`people`.`type` AS `type`,
`people`.`created_at` AS `created_at`,`people`.`updated_at` AS `updated_at` 
from `people` 
where (`people`.`type` = 'teacher')


Ajustando as classes de modelo
Sobreescrever o construtor das classes filhas para passar o tipo de cada uma delas. Também é necessário declarar a variáel stática que define o campo da chave primária da tabela.
<?php
class Student extends Person {
    static $primary_key = 'id';
    public function  __construct(array $attributes = array()) {
        parent::__construct(array_merge($attributes,array('type'=> __CLASS__ )));
    }
}
<?php
class Teacher extends Person {
   static $primary_key = 'id';
    public function  __construct(array $attributes = array()) {
        parent::__construct(array_merge($attributes,array('type'=> __CLASS__ )));
    }
}

Agora é só rodar novamente os testes e ver a barra verde, indicando que a implementação da herança com tabela simples está funcionando.
$  phpunit --testdox --color models/PersonTest.php
$  phpunit --testdox --group models => só as classes com anotações

quarta-feira, 7 de setembro de 2011

Codeigniter 2 + PHPActiveRecord (parte 2)

Existem várias formas para instalar o pacote do PHPActiveRecord, vamos instalar usando o gerenciador de pacotes GetSpark.
$ cd my-cia-app/
$ php -r "$(curl -fsSL http://getsparks.org/go-sparks)”
Agora é só instalar o phpactiverecord
$ php tools/spark install php-activerecord
Em alguns ambientes o firewall bloqueia o download de arquivos binarios. Um simples hack pode resolver isso. Acrescente a linha abaixo no final do construtor da classe my-ci-app/tools/lib/spark/spark_types/gt_spark.php
 # TODO: hack para o protocolo git bloqueado no firewall
 $this->base_location = str_replace("git://","https://",$this->base_location);

Testar o funcionamento
Criar a tabela users no database
DROP TABLE IF EXISTS users;
CREATE TABLE users (
    id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    email_address VARCHAR(200) NOT NULL DEFAULT ''
) ENGINE=InnoDB;
Criar um novo modelo User
<?php
class User extends ActiveRecord\Model {
}
Criar ou editar o controlador Welcome

Para fazer o autoload dos pacotes gerenciados pelo Spark inclua a linha abaixo no arquivo config/autoload.php
$autoload['sparks'] = array('php-activerecord/0.0.1');

Hack para o CIUnit funcionar com o Spark
Alterar o arquivo application/core/MY_Loader.php
define('SPARKPATH', 'sparks/'); 

para
define('SPARKPATH', BASEPATH . '../sparks/');

Referências:
https://bitbucket.org/kenjis/my-ciunit/wiki/Home http://beau.frusetta.com/2011/07/07/codeigniter-php-activerecord/http://getsparks.org
http://www.phpactiverecord.org/ https://github.com/kla/php-activerecord

terça-feira, 6 de setembro de 2011

Codeigniter 2 + CIUnit (parte 1)

Baixar  a versão mais recente do Codeigniter no repositório do projeto
$ wget https://github.com/EllisLab/CodeIgniter
Instalar a versão mais recente do phpunit
$ pear channel-discover pear.phpunit.de
$ pear channel-discover components.ez.no
$ pear channel-discover pear.symfony-project.com
$ pear install phpunit/PHPUnit

Integrando o phpunit ao CI 2
A equipe de desenvolvimento do Codeigniter está trabalhando para integrar o PHPUnit com o core do framework. Um novo branch foi criado e pode ser acompanhado no github do projeto.
https://github.com/EllisLab/CodeIgniter/tree/feature/unit-tests

Enquanto a integração oficial não é finalizada uma boa alternativa é o uso do CIUnit.
Baixar o fork para o CI 2.0 do CIUnit no repositório. (https://bitbucket.org/kenjis/my-ciunit ou https://github.com/fukata/CIUnit-for-CI2) e seguir as instruções de instalação e uso do arquivo README.
cp ciunit/application/third_party/CIUnit   my-ci-app//application/third_party/CIUnit
cp ciunit/tests   my-ci-app/tests


Configurar as conexões
Alterar o arquivo de configuração de conexões da aplicação para trabalhar com o database de teste:
$ vi /my-ci-app/application/config/database.php
Criar os databases:
$ mysqladmin -u root -p create exercise academico
$ mysqladmin -u root -p create exercise academico_test

Verificar se tudo funciona
O CIUnit vem com alguns arquivos de teste de exemplo que podemos utilizar para verificar se tudo está corretamente instalado.

Criar a tabela do modelo
Testar a aplicação no melhor estilo TDD, baby-steps. Red-Green
$ cd my-ci-app/tests
$ phpunit
Barra vermelha (red)!  Criar o modelo em my-ci-app/application/models/phone_carrier_model.phpe rodar os testes mais uma vez.
$ phpunit
Agora é só implementar a funcionalidade do modelo e ver a barra verde
Código da aplicação em https://github.com/fcm/codeigniter-academic

Referências:
https://github.com/EllisLab/CodeIgniter/tree/feature/unit-testsasdasd
https://bitbucket.org/kenjis/my-ciunit
https://github.com/fukata/CIUnit-for-CI2
https://github.com/EllisLab/CodeIgniter