mapeamento objeto relacional com php - php conference brasil 2010

104
Flávio Lisboa - PHP Conference 2010 Mapeamento Objeto Relacional com PHP Flávio Gomes da Silva Lisboa @fgsl www.fgsl.eti.br @fgsl A reprodução é livre, apenas cite a fonte

Upload: flavio-lisboa

Post on 26-May-2015

4.213 views

Category:

Technology


4 download

DESCRIPTION

Apresentação feita na 5ª PHP Conference Brasil

TRANSCRIPT

Page 1: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Mapeamento Objeto Relacional com PHP

Flávio Gomes da Silva Lisboa@fgsl

www.fgsl.eti.br

@fgsl

A reprodução é livre, apenas cite a fonte

Page 2: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Quem sou eu

2007

2008 2009

A reprodução é livre, apenas cite a fonte

@fgsl

Page 3: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

►Problemas

►Soluções

►Opções:

Mapeamento Objeto Relacional

A reprodução é livre, apenas cite a fonte

@fgsl

Page 4: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Problemas

A maioria das aplicações Web persistem dados em um banco de dados.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 5: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Problemas

O processo de desenvolvimento utiliza conexões diferentes (desenvolvimento, teste, homologação

e produção).

Adaptado de sofist.com.br

A reprodução é livre, apenas cite a fonte

@fgsl

Page 6: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Problemas

Uma aplicação pode utilizar-se de várias fontes de dados diferentes ao mesmo tempo.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 7: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Problemas

E ela também pode ser distribuída para trabalhar com bases de dados que tem a mesma estrutura, mas são fornecidas por fabricantes diferentes, por

exemplo, MySQL e PostgreSQL.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 8: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Problemas

Por isso, código específico para manipular um determinado mecanismo de persistência e

recuperação de dados não deve ficar espalhado pela aplicação.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 9: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Problemas

Além disso, o banco de dados deve ficar desacoplado da aplicação, para que mudanças

no banco (como alterações em nomes de campos de tabelas) não gerem trabalho de recodificação.

infoescola.comA reprodução é livre, apenas cite a fonte

@fgsl

Page 10: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Mapear dados de objetos para bancos de dados relacionais

Referência:

Padrões de Arquiteturade Aplicações Corporativas

Martin Fowler,arquiteto de software

e consultor

A reprodução é livre, apenas cite a fonte

@fgsl

Page 11: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Momento Cultural

O que é um arquiteto de software?

A reprodução é livre, apenas cite a fonte

Page 12: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Momento Cultural

E o que é um consultor?

* to con: enganar* demeaning: degradante

Dilbert, by Scott Adams

A reprodução é livre, apenas cite a fonte

Page 13: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Momento Cultural

E o que é um consultor?

A reprodução é livre, apenas cite a fonte

Page 14: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Momento Cultural

E o que é um consultor?

Dilbert, by Scott Adams

A reprodução é livre, apenas cite a fonte

Page 15: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Embora existam várias técnicas para embutir SQL em uma

linguagem de programação, elas são todas um pouco

deselegantes.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 16: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Seria melhor acessar os dados usando mecanismos que se

encaixem com a linguagem de desenvolvimento da aplicação.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 17: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Por essas razões, é sábio separar o acesso SQL da lógica

de domínio e colocá-las em classes separadas.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 18: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Padrões de Projeto: soluções reutilizáveis de software orientado a objetos

The Greatest American Hero, by Stephen J. Cannell, starring William Kat

A reprodução é livre, apenas cite a fonte

@fgsl

Page 19: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Padrões de Projeto: soluções reutilizáveis de software orientado a objetos

Abin Sur, Hal Jordan, Green Lanterns created by John Broome and Gil KaneA reprodução é livre, apenas cite a fonte

@fgsl

Page 20: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Padrão Básico

GatewayUm objeto que encapsula o acesso a um sistema

ou recurso externo

A reprodução é livre, apenas cite a fonte

@fgsl

Page 21: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

GatewayAPI simples para lidar de forma orientada a

objetos com coisas que não são objetos

A reprodução é livre, apenas cite a fonte

@fgsl

Page 22: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Padrões de Mapeamento Objeto Relacional

Row Data GatewayUm objeto para cada linha retornada por uma

consultaUtilidade: trabalhar com um registro específico

Table Data GatewayUm objeto para cada tabela

Utilidade: trabalhar com um conjunto de registros

A reprodução é livre, apenas cite a fonte

@fgsl

Page 23: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Padrões de Mapeamento Objeto Relacional

Active RecordUm objeto de domínio cliente sabe como interagir

com tabelas do banco de dados

Data MapperIsola os objetos do domínio do banco de dados

A reprodução é livre, apenas cite a fonte

@fgsl

Page 24: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Diferenças

Active RecordO objeto de domínio sabe como incluir, alterar,

excluir e pesquisar.

Data MapperO objeto de domínio invoca um objeto que sabe

como incluir, alterar, excluir e pesquisar.

A reprodução é livre, apenas cite a fonte

@fgsl

Page 25: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

Preciso implementar tudo isso?

A reprodução é livre, apenas cite a fonte

@fgsl

Page 26: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

A reprodução é livre, apenas cite a fonte

@fgsl

Page 27: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Soluções

A reprodução é livre, apenas cite a fonte

@fgsl

Use um framework ORM

framework... ...abstração que une códigos comuns entre vários projetos de software provendo uma funcionalidade genérica...Fonte: http://pt.wikipedia.org/wiki/Framework

Aplicações

Camada de ModeloCamada de Modelo

Objeto de domínio

Framework ORM

Page 28: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Opções

A reprodução é livre, apenas cite a fonte

@fgsl

Page 29: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Opções

A reprodução é livre, apenas cite a fonte

@fgsl

Estudo de caso: cadastro de livros e categorias de livros

Page 30: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Opções

A reprodução é livre, apenas cite a fonte

@fgsl

Estudo de caso: cadastro de livros e categorias de livros

CREATE TABLE `livrosdb`.`livros` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,

`titulo` VARCHAR( 80 ) NOT NULL ,

`id_categoria` INT UNSIGNED NOT NULL ,

PRIMARY KEY ( `id` ) ,

INDEX ( `titulo` , `id_categoria` ) ) ENGINE = InnoDB

CREATE DATABASE `livrosdb` ;

CREATE TABLE `livrosdb`.`categorias` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,

`nome` VARCHAR( 80 ) NOT NULL ,

PRIMARY KEY ( `id` ) ,

INDEX ( `nome` ) ) ENGINE = InnoDB

Page 31: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Opções

A reprodução é livre, apenas cite a fonte

@fgsl

Projeto PHP com três implementações:

propel

doctrine

zenddb

exemploormphp

Page 32: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

http://www.propelorm.org

Pode ser instalado pelo Depende de

PHing Is Not GNU makebaseado em

que também pode instalar

►Versão 1.5.2 requer PHP 5.2.4►Usa PDO►É usado pelo

http://www.phing.info

Page 33: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Possui dois componentes:

Um gerador de modelos: não obrigatório.(mas se não usar vai ficar muito difícil...)

propel-1.5.2/generator/bin/propel-gen (Linux)propel-1.5.2/generator/bin/propel-gen.bat (Windows)

Uma biblioteca de classes: necessária

Page 34: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Neste caso, foram instalados manualmente o propel 1.5.2 e o phing 2.4.2 no Linux.

Foram criados links simbólicos para os scripts do Propel e do Phing no diretório /usr/bin. O script do Phing precisa de permissão de execução para ser chamado pelo Propel.

Page 35: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Diretiva Valor

ze1_compatibility_mode Off

magic_quotes_gpc Off

magic_quotes_sybase Off

Configuração do php.ini

Page 36: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

schema.xml (raiz do projeto)

<?xml version="1.0" encoding="UTF-8"?><database name="livrosdb" defaultIdMethod="native"><table name="livros" phpName="Livro"><column name="id" type="integer" required="true" primaryKey="true"autoIncrement="true" /><column name="titulo" type="varchar" size="80" required="true" /><column name="id_categoria" type="integer" required="true" /><foreign-key foreignTable="categorias"><reference local="id_categoria" foreign="id" /></foreign-key></table><table name="categorias" phpName="Categoria"><column name="id" type="integer" required="true" primaryKey="true"autoIncrement="true" /><column name="nome" type="varchar" size="80" required="true" /></table></database>

Page 37: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

build.properties (raiz do projeto)

# Database driverpropel.database = mysql

# Project namepropel.project = propel

Page 38: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Entre no diretório do projeto e execute o comando:

propel-gen om

Page 39: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

[propel-om] Processing: schema.xml[propel-om] Processing Datamodel : schema.xml[propel-om] - processing database : livrosdb[propel-om] + livros[propel-om] -> BaseLivroPeer [builder: PHP5PeerBuilder][propel-om] -> BaseLivro [builder: PHP5ObjectBuilder][propel-om] -> LivroTableMap [builder: PHP5TableMapBuilder][propel-om] -> BaseLivroQuery [builder: QueryBuilder][propel-om] -> LivroPeer [builder: PHP5ExtensionPeerBuilder][propel-om] -> Livro [builder: PHP5ExtensionObjectBuilder][propel-om] -> LivroQuery [builder: ExtensionQueryBuilder][propel-om] + categorias[propel-om] -> BaseCategoriaPeer [builder: PHP5PeerBuilder][propel-om] -> BaseCategoria [builder: PHP5ObjectBuilder][propel-om] -> CategoriaTableMap [builder: PHP5TableMapBuilder][propel-om] -> BaseCategoriaQuery [builder: QueryBuilder][propel-om] -> CategoriaPeer [builder: PHP5ExtensionPeerBuilder][propel-om] -> Categoria [builder: PHP5ExtensionObjectBuilder][propel-om] -> CategoriaQuery [builder: ExtensionQueryBuilder][propel-om] Object model generation complete - 14 files written

Page 40: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Pra cada tabela, Propel cria 3 arquivos (que você vai utilizar diretamente):

Uma classe modelo que representa uma linha no banco de dados;

Uma classe peer com constantes estáticas e métodos para compatibilidade com versões anteriores de Propel;

Uma classe query, para operar sobre a tabela para recuperar e atualizar registros

Page 41: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Propel pode gerar o script SQL das tabelas com o comando:

propel-gen sql

O script é gravado no arquivo build/sql/schema.sql

Page 42: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Propel também pode criar as tabelas no banco, configurando a conexão no build.properties.

propel.database.url = [DSN padrão PDO]propel.database.user = propel.database.password =

propel-gen sql

Page 43: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

É preciso criar o arquivo runtime-conf.xml na raiz do projeto, com as conexões a serem usadas em tempo de execução

<?xml version="1.0" encoding="UTF-8"?><config> <propel> <datasources default="livrosdb"> <datasource id="livrosdb"> <adapter>mysql</adapter> <!-- sqlite, mysql, myssql, oracle, or pgsql --> <connection> <dsn>mysql:host=localhost;dbname=livrosdb</dsn> <user>root</user> <password>admin</password> </connection> </datasource> </datasources> </propel></config>

Page 44: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Por questão de performance (tempo de parsing do XML), Propel usa uma versão PHP do arquivo runtime-conf.xml.

Essa versão é gerada com o comando:

propel-gen convert-conf

Page 45: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Para usar o Propel na aplicação, você precisa:

1)Adicionar o diretório runtime/lib do Propel no include_path do PHP 2)Importar o arquivo Propel.php3)Carregar a configuração das conexões4)Adicionar o diretório build/classes da aplicação no include_path do PHP

Page 46: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Algo assim:

<?php

define(BASE_PATH, substr(realpath(__FILE__),0,strpos(realpath(__FILE__),'exemploormphp')));

define(APPLICATION, 'exemploormphp/propel');

set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH.'propel-1.5.2/runtime/lib');

// Inclui o script principal do Propelrequire_once 'Propel.php';

// Inicia a configuração de tempo de execuçãoPropel::init(BASE_PATH.APPLICATION.'/build/conf/propel-conf.php');

// Adiciona o diretório 'classes' ao include pathset_include_path(BASE_PATH.APPLICATION.'/build/classes' . PATH_SEPARATOR . get_include_path());

exemploormphp/propel/init.php

Page 47: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de inclusão

exemploormphp/propel/create.php

require_once 'init.php';

$nome = (string) $_GET['nome'];

$categoria = new Categoria();$categoria->setNome($nome);$categoria->save();

unset($categoria);

Page 48: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta

exemploormphp/propel/recover.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

if (!empty($id))$categoria = CategoriaQuery::create()->findPk($id);

if (!empty($nome))$categoria = CategoriaQuery::create()->findOneByNome($nome);

echo 'Nome: ' . $categoria->getNome();

Page 49: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de alteraçãoexemploormphp/propel/update.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

if (!empty($id))$categoria = CategoriaQuery::create()->findPk($id);

if (!empty($nome))$categoria = CategoriaQuery::create()->findOneByNome($nome);

$categoria->setNome('Huguinho');$categoria->save();

$id = $categoria->getId();

unset($categoria);

$categoria = CategoriaQuery::create()->findPk($id);

echo 'Nome: ' . $categoria->getNome();

Page 50: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de exclusãoexemploormphp/propel/delete.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

if (!empty($id))$categoria = CategoriaQuery::create()->findPk($id);

if (!empty($nome))$categoria = CategoriaQuery::create()->findOneByNome($nome);

print_r($categoria);

$categoria->delete();

print_r($categoria);

O objeto ainda existe, mas não é mais persistente. O estado pode serverificado com o método isDeleted().

Page 51: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta a vários registros

exemploormphp/propel/listing.php

require_once 'init.php';

$categoria = CategoriaQuery::create()->findOneByNome('suspense');

$livros = LivroQuery::create()->filterByCategoria($categoria)->orderByTitulo()->find();

foreach($livros as $livro){print_r($livro);}

Page 52: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta SQL customizadaexemploormphp/propel/sqlisting.php

require_once 'init.php';

$con = Propel::getConnection(LivroPeer::DATABASE_NAME);$sql = 'SELECT l.id, l.titulo,c.nome ';$sql .= 'FROM livros as l INNER JOIN categorias as c ';$sql .= 'ON l.id_categoria = c.id ';$sql .= 'WHERE c.nome = :name';

$stmt = $con->prepare($sql);

$stmt->execute(array(':name' => 'suspense'));

$formatter = new PropelObjectFormatter();$formatter->setClass('Livro');$formatter->setPeer('LivroPeer');

$livros = $formatter->format($stmt);

foreach($livros as $livro){print_r($livro);}

Page 53: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de efeito de relacionamentoexemploormphp/propel/relation.php

require_once 'init.php';

$categoria = new Categoria();$categoria->setNome('suspense');

$livro = new Livro();$livro->setTitulo('A volta dos que não foram');$livro->setCategoria($categoria);

$livro->save();

unset($livro);

$livro = LivroQuery::create()->filterByCategoria($categoria)->findOne();

echo 'Título: ' . $livro->getTitulo().'<br/>';echo 'Categoria: ' . $livro->getCategoria()->getNome();

Page 54: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Transações

$con = Propel::getConnection(CategoriaPeer::DATABASE_NAME);

$con->beginTransaction(); Try { O QUE VOCÊ ESPERA QUE OCORRA

$con->commit(); } catch (Exception $e) { O QUE OCORRE

$con->rollback(); throw $e; }

Page 55: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Ganchos

Class Livro extends BaseLivro public function postSave(PropelPDO $con) { ... } public function postDelete(PropelPDO $con) { ... } public function updateCategoria(PropelPDO $con) { ... }}

Page 56: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Propel

A reprodução é livre, apenas cite a fonte

@fgsl

Page 57: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

http://www.doctrine-project.org

Mapeador Objeto

Relacional

Camada de Abstração de Banco de Dados

ORM DBAL

usa

►Versão 1.2.3 requer PHP 5.2.3►Usa PDO►Versão 2.0 usa PHP 5.3 e é... TOTALMENTE DIFERENTE!

Pode ser instalado pelo

MongoDBODM

Migrations Common

Page 58: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Neste caso, foi instalado manualmente o Doctrine 1.2.3 no Linux.

Foi criado um diretório lib/vendor/doctrine no projeto, com links simbólicos para o diretório Doctrine e para o arquivo Doctrine.php.

Page 59: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Inicialização do Doctrine:

exemploormphp/doctrine/init.php

set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/lib/vendor/doctrine');

require_once('Doctrine.php');

spl_autoload_register(array('Doctrine', 'autoload'));spl_autoload_register(array('Doctrine', 'modelsAutoload'));

$manager = Doctrine_Manager::getInstance();

$dsn = 'mysql:dbname=livrosdb;host=127.0.0.1';$user = 'root';$password = 'admin';

$dbh = new PDO($dsn,$user,$password);$conn = $manager->connection($dbh,'livrosdb');

Doctrine_Core::loadModels(array('models','models/generated'));

Page 60: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Doctrine possuía uma ferramenta de linha de comando, com geração de código, similar ao Propel. Essa ferramenta estava disponível na distribuição Sandbox.

Só que a Sandbox não está mais disponível...

Na versão 2.0, a linha de comando já vem incluída no ORM padrão. Mas aqui não estamos falando da versão 2.0...

Page 61: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Mas isso não significa que não dê pra gerar os

modelos!

Duffy Duck by Warner Bros

Page 62: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Crie no projeto uma pasta chamada models.

Crie na raiz do projeto um script que invoque o método generateModelsFromDb da classe Doctrine_Core:

require_once 'init.php';

Doctrine_Core::generateModelsFromDb('models', array('livrosdb'),array('generateTableClasses' => true));

exempoormphp/doctrine/gom.php

Page 63: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

São geradas três classes para cada tabela...

As classes com prefixo Base contém a definição da tabela e são herdadas pela classe que implementa ActiveRecord.

A classe com sufixo Table é um Data Mapper.

Page 64: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Para não gerar os Data Mappers, a chave generateTableClasses do método generateModelsFromDb deve ser configurada para false.

Você pode apagar os arquivos

também, se quiser.

Dee Dee & Dexter by Hanna Barbera

Page 65: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Mas é preciso verificar os relacionamentos gerados. No nosso exemplo, o Doctrine gera um relacionamentos um-pra-muitos entre Livros e Categorias, sendo que na verdade é um-pra-um.

class Livros extends BaseLivros{public function setUp(){parent::setUp();

$this->hasOne('Categorias', array( 'local' => 'id_categoria', 'foreign' => 'id' ) );

}}

Page 66: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Também é possível criar scripts com as definições das tabelas com o Doctrine DBAL. Esses scripts podem ter a conexão parametrizada, de forma a gerar tabelas para qualquer banco de dados.

Também é possivel criar definições de tabelas no formato YAML (Yaml Ain't Markup Language), criar os modelos a partir delas e aí gerar as tabelas.

DBAL

Page 67: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de inclusão

exemploormphp/doctrine/create.php

require_once 'init.php';

$nome = (string) $_GET['nome'];

$categoria = new Categorias();$categoria->nome = $nome;$categoria->save();

unset($categoria);

Page 68: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consultaexemploormphp/doctrine/recover.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

$categoriaTable = CategoriasTable::getInstance();

if (!empty($id))$categoria = $categoriaTable->findOneById($id);

if (!empty($nome))$categoria = $categoriaTable->findOneByNome($nome);

echo 'Nome: ' . $categoria->nome;

Page 69: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de alteraçãoexemploormphp/doctrine/update.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

if (!empty($id))$categoria = CategoriasTable::getInstance()->find($id);

if (!empty($nome))$categoria = CategoriasTable::getInstance()->findOneByNome($nome);

$categoria->nome = 'Huguinho';$categoria->save();

$id = $categoria->id;

unset($categoria);

$categoria = CategoriasTable::getInstance()->find($id);

echo 'Nome: ' . $categoria->nome;

Page 70: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de exclusãoexemploormphp/doctrine/delete.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

if (!empty($id))$categoria = CategoriasTable::getInstance()->find($id);

if (!empty($nome))$categoria = CategoriasTable::getInstance()->findOneByNome($nome);

print_r($categoria);

$categoria->delete();

print_r($categoria);

O objeto ainda existe, mas não é mais persistente. O estado pode ser verificado com o método state().

Page 71: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta a vários registros

exemploormphp/doctrine/listing.php

require_once 'init.php';

$livros = LivrosTable::getInstance()->findByDql('Categorias.nome = ?','romance');

echo 'Quantidade de livros: ' . count($livros);

foreach($livros as $livro){print_r($livro);}

Page 72: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta SQL customizadaexemploormphp/doctrine/sqlisting.php

require_once 'init.php';

$q = Doctrine_Query::create($conn)->select('l.id, l.titulo, c.nome')->from('Livros l')->innerJoin('l.Categorias c')->where('c.nome = ?');

$livros = $q->execute(array('romance'));

echo 'Quantidade de livros: ' . count($livros);

foreach($livros as $livro){print_r($livro);}

Page 73: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de efeito de relacionamento

exemploormphp/doctrine/relation.php

require_once 'init.php';

$categoria = new Categorias();$categoria->nome = 'romance';

$livro = new Livros();$livro->titulo = 'Poeira em alto mar';$livro->Categorias = $categoria;

$livro->save();

unset($livro);

$livro = LivrosTable::getInstance()->findByDql('Categorias.nome = ?','romance')->get(0);

echo 'Título: ' . $livro->titulo . '<br/>';echo 'Categoria: ' . $livro->Categorias->nome;

Page 74: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

$conn = beginTransaction();

Try { O QUE VOCÊ ESPERA QUE OCORRA

$conn->commit(); } catch (Exception $e) { O QUE OCORRE

$conn->rollback(); throw $e; }

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Transações

Page 75: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Ganchos

Class Livros extends BaseLivros public function preSave($event) { ... } public function postSave($event) { ... } public function postDqlSelect($event) { ... }}

Page 76: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Doctrine

A reprodução é livre, apenas cite a fonte

@fgsl

Page 77: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

►Versão 1.10 requer PHP 5.2.4►Usa PDO►Versão 2.0 usará PHP 5.3 e terá ...MUITA COISA LEGAL!

Pode ser instalado pelo

...mas por um canal não oficial Zend_Db_AdapterZend_Db_ProfilerZend_Db_SelectZend_Db_StatementZend_Db_Table

Page 78: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Neste caso, foi instalado manualmente o Zend Framework 1.10.6. Na raiz do projeto foi criado um link simbólico para o diretório ZendFramework-1.10.6/library/Zend.

Na raiz do projeto foi criada uma pasta models para armazenar os modelos.

Page 79: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

require_once 'Zend/Loader/Autoloader.php';

set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/models');

$autoloader = Zend_Loader_Autoloader::getInstance();$autoloader->registerNamespace('Zend_Config_*');$autoloader->registerNamespace('Zend_Db_*');

$config = new Zend_Config_Ini('config.ini','database');

$conn = Zend_Db::factory($config->db->adapter,$config->db->params->toArray());

exemploormphp/zenddb/init.php

Inicialização do Zend_Db:

Page 80: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

exemploormphp/zenddb/config.ini

Configuração da conexão:

[database]db.adapter = PDO_MYSQLdb.params.username = rootdb.params.password = admindb.params.dbname = livrosdbdb.params.host = localhost

Page 81: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Zend Framework possui um componente chamado Zend_Tool, que é uma ferramenta de linha de comando para geração de código.

Zend_Tool cria as classes de modelo, mas somente quando o projeto foi criado com a estrutura do Zend_Framework, porque as definições são lidas e gravadas no arquivo .zfproject.xml.

Page 82: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Por outro lado, você não precisa necessariamente criar a classe de modelo para manipular o banco porque o Zend_Db provê uma implementação genérica de Data Mapper.

Basta instanciar Zend_Db_Table passando para o construtor o nome da tabela no banco de dados.

Page 83: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de inclusão com Data Mapper:

exemploormphp/zenddb/createwithoutmodel.php

require_once 'init.php';

$categoria = new Zend_Db_Table('categorias');$categoria->insert(array('nome'=>'infantil'));

unset($categoria);

Page 84: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Mas a criação do modelo é fácil:

exemploormphp/zenddb/models/Categoria.php

class Categoria extends Zend_Db_Table_Abstract{protected $_name = 'categorias';}

Page 85: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de inclusão com modelo definido:

exemploormphp/zenddb/create.php

require_once 'init.php';

Zend_Loader::loadClass('Categoria');

$dm = new Categoria();$categoria = $dm->createRow(); $categoria->nome = 'infantil';$categoria->save();

unset($categoria);

Page 86: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta

exemploormphp/zenddb/recover.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

Zend_Loader::loadClass('Categoria');$dm = new Categoria();

if (!empty($id))$categoria = $dm->find($id)->current();

if (!empty($nome))$categoria = $dm->fetchRow($dm->getDefaultAdapter()->quoteInto('nome = ?',$nome));

echo 'Nome: ' . $categoria->nome;

Page 87: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de alteraçãoexemploormphp/zenddb/update.php

require_once 'init.php';

$id = (int) $_GET['id']; $nome = (string) $_GET['nome'];

Zend_Loader::loadClass('Categoria');$dm = new Categoria();

if (!empty($id))$categoria = $dm->find($id)->current();

if (!empty($nome))$categoria = $dm->fetchRow($dm->getDefaultAdapter()->quoteInto('nome = ?',$nome));

$categoria->nome = 'Huguinho';$categoria->save();

$id = $categoria->id;

unset($categoria);

$categoria = $dm->find($id)->current();

echo 'Nome: ' . $categoria->nome;

Page 88: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de exclusãoexemploormphp/zenddb/delete.php

require_once 'init.php';

$id = (int) $_GET['id'];$nome = (string) $_GET['nome'];

Zend_Loader::loadClass('Categoria');$dm = new Categoria();

if (!empty($id))$categoria = $dm->find($id)->current();

if (!empty($nome))$categoria = $dm->fetchRow($dm->getDefaultAdapter()->quoteInto('nome = ?',$nome));

print_r($categoria->id);

$categoria->delete();

print_r($categoria->id);

O objeto ainda existe, mas seus atributosestão vazios.

Page 89: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Relacionamento no Modelo

exemploormphp/zenddb/models/Livro.php

class Livro extends Zend_Db_Table_Abstract{protected $_name = 'livros';

protected $_referenceMap = array('Categoria' => array('columns' => 'id_categoria','refTableClass' => 'Categoria','refColumns' => 'id'));}

Page 90: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta a vários registros

exemploormphp/zenddb/listing.php

require_once 'init.php';

Zend_Loader::loadClass('Categoria');$dm = new Categoria();

$categoria = $dm->fetchRow($dm->getDefaultAdapter()->quoteInto('nome = ?','infantil'));

$livros = $categoria->findDependentRowset('Livro');

echo 'Quantidade de livros: ' . count($livros);

foreach($livros as $livro){print_r($livro);}

Page 91: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de consulta SQL customizada

exemploormphp/zenddb/sqlisting.php

require_once 'init.php';

Zend_Loader::loadClass('Livro');$dm = new Livro();

$select = $dm->select(Zend_Db_Table::SELECT_WITH_FROM_PART) ->setIntegrityCheck(false);$select->join('categorias','livros.id_categoria = categorias.id')->where('categorias.nome = ?','infantil');

$livros = $dm->fetchAll($select);

echo 'Quantidade de livros: ' . count($livros);

foreach($livros as $livro){print_r($livro);}

Page 92: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Exemplo de efeito de relacionamento

exemploormphp/zenddb/relation.phprequire_once 'init.php';

Zend_Loader::loadClass('Livro');Zend_Loader::loadClass('Categoria');Zend_Loader::loadClass('LivroRow');

$dmc = new Categoria();$categoria = $dmc->createRow();$categoria->nome = 'tragédia';

$dml = new Livro(array('rowClass' => 'LivroRow'));$livro = $dml->createRow();$livro->titulo = 'Afoguei meu peixinho dourado';$livro->setCategoria($categoria);

$livro->save(); $livro->refresh();

$id = $livro->id; unset($livro);

$livro = $dml->find($id)->current();

echo 'Título: ' . $livro->titulo . '<br/>';echo 'Categoria: ' . $livro->findParentRow('Categoria')->nome;

Page 93: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Atualização automática de registro relacionado

exemploormphp/zenddb/models/LivroRow.php

class LivroRow extends Zend_Db_Table_Row_Abstract{private $_categoria;

public function setCategoria($categoria){$this->_categoria = $categoria;}

public function save(){$this->_categoria->save();$this->id_categoria = $this->_categoria->id;parent::save();}}

Page 94: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Este é um projeto GPLv2, que já faz de forma transparente o que foi feito usando a extensão de Zend_Db_Table_Row_Abstract. Ele vem pronto para trabalhar com MySQL, mas por herança é possível criar adaptadores para outros bancos.

Há uma proposta em construção, para criar uma nova implementação de ActiveRecord no Zend Framework:

http://code.google.com/p/zend-framework-orm

http://framework.zend.com/wiki/display/ZFPROP/Zend_Db_ActiveRecord

Page 95: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Transações

$conn = Zend_Db_Table_Abstract::getDefaultAdapter();

$conn->beginTransaction(); Try { O QUE VOCÊ ESPERA QUE OCORRA

$conn->commit(); } catch (Exception $e) { O QUE OCORRE

$conn->rollback(); throw $e; }

Page 96: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Ganchos

Class LivroRow extends Zend_Db_Table_Row_Abstract protected function _insert() { ... } protected function _postInsert() { ... } protected function _update() { ... }}

Page 97: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Zend_Db

A reprodução é livre, apenas cite a fonte

@fgsl

Page 98: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

E agora, o que eu faço?

A reprodução é livre, apenas cite a fonte

@fgsl

Page 99: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Sei lá, entende?

A reprodução é livre, apenas cite a fonte

@fgsl

Patropi, interpretado por Orival Pessini

Page 100: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010 A reprodução é livre, apenas cite a fonte

@fgsl

Bill Karwin

Mas como com qualquer ORM, há, inevitavelmente, algumas consultas SQL e

operações que você não pode fazer através da interface OO.

Nenhum ORM pode servir como um shopping que tem

apenas uma loja.

Page 101: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010 A reprodução é livre, apenas cite a fonte

@fgsl

Independente de sua escolha, saiba que Zend Framework

trabalha com todos!

Wheelie and The Chopper Bunch (NBC,1974)

Page 102: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Coisas Realmente Importantes

A reprodução é livre, apenas cite a fonte

@fgsl

Flexibilidade

Page 103: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Coisas Realmente Importantes

A reprodução é livre, apenas cite a fonte

@fgsl

Facilidade de Manutenção

Kombi, Volkswagen

Page 104: Mapeamento Objeto Relacional com PHP - PHP Conference Brasil 2010

Flávio Lisboa - PHP Conference 2010

Obrigado!

A reprodução é livre, apenas cite a fonte

@fgsl

www.fgsl.eti.br

Little Einsteins by Walt Disney