Introdução a ORM em PHP com Doctrine

Como instalar e utilizar o Doctrine, uma excelente biblioteca de ORM para PHP que possui também uma camada para DBAL, abaixo uma visão geral básica.

Doctrine layers

Utilizar o mapeamento objecto-relacional é algo muito útil no desenvolvimento, pode agilizar bastante seu trabalho. Se ainda não conhece aqui esta um pequeno exemplo de como inserir um novo registro no banco de dados:

<?php
 
$user = new User();
$user->firstname = 'John';
$user->lastname = 'Doe';
$user->save();
// se a coluna id for a chave primaria
print 'Novo usuário criado, id: ' . $user->id;

Outro exemplo mostrando duas formas simples para buscar um registro:

<?php
 
$userTable = Doctrine::getTable('User');
// busca pela chave primaria
$user = $userTable->find(155);
print $user->firstname . ' ' . $user->lastname . '<br />';
// busca por uma coluna
$user = $userTable->findOneBy('lastname', 'Doe');
print $user->firstname . ' ' . $user->lastname;

Como pode ver nenhum SQL foi necessário nos exemplos, você pode realizar qualquer operação de CRUD sem escrever nenhuma query, além dos métodos que realizam essas operações existe a Doctrine Query Language que lhe permite escrever query’s totalmente orientadas a objeto. Como o Doctrine possui uma DBAL os exemplos acima e os que criar usando a DQL podem ser utilizados em qualquer um dos bancos suportados, atualmente são FrontBase, InterBase / Firebird, Microsoft SQL Server, MySQL, Oracle 7/8/9/10, PostgreSQL, QuerySim e SQLite 2.

Instalação e configuração

A versão mais recente do Doctrine pode ser baixada a partir da home do projeto ou aqui, vou utilizar MySQL e um esquema demonstrativo chamado sakila.

Crie um diretório como “intro-doctrine” em sua pasta web e extraia a versão baixada do Doctrine. A configuração básica do Doctrine ficara em um arquivo “bootstrap.php” que será assim:

<?php
// Verifique o caminho de acordo a versão baixada
require 'Doctrine-1.2.2/lib/Doctrine.php';
 
spl_autoload_register(array('Doctrine', 'autoload'));
spl_autoload_register(array('Doctrine_Core', 'modelsAutoload'));
 
$manager = Doctrine_Manager::getInstance();
 
try { 
  // Insira aqui os dados de sua conexão 
  $conn = Doctrine_Manager::connection('mysql://usuario:senha@localhost/sakila');
 
  $manager->setAttribute(Doctrine_Core::ATTR_MODEL_LOADING, Doctrine_Core::MODEL_LOADING_CONSERVATIVE); 
  $manager->setAttribute(Doctrine_Core::ATTR_EXPORT, Doctrine_Core::EXPORT_ALL);
 
  $profiler = new Doctrine_Connection_Profiler();
  $manager->setListener($profiler);
 
} catch (Doctrine_Manager_Exception $e) {
  print $e->getMessage();
}
 
Doctrine_Core::loadModels('models');

Nesse arquivo foi definido o Data Source Name usado pelo PDO, o caminho para carregar os modelos em “models” e sua forma de carregamento.

Gerar modelos

Os modelos gerados são baseado no padrão ActiveRecord criado por Martin Fowler e já bem utilizado em outras bibliotecas e frameworks.

O Doctrine pode gerar seus modelos a partir de um arquivo YAML criado manualmente ou então diretamente do banco, prefiro gerar meus modelos direto do banco pois assim posso modelar meu banco a partir de uma ferramenta como o MySQL Workbench ou mesmo fazer alguma alteração em um banco existente com o MySQL Query Browser e então gerar ou atualizar os modelos. Crie um arquivo chamado “build-models.php” na sua pasta “intro-doctrine” que será responsável pela geração dos modelos, o arquivo será o seguinte:

<?php
 
require 'bootstrap.php';
 
set_time_limit(90);
 
$modelsPath = 'models';
$title = 'Modelos gerados com sucesso!';
$msg = '';
 
try {
    Doctrine::generateModelsFromDb(realpath($modelsPath));
} catch (Doctrine_Import_Builder_Exception $e) {
    $title = 'Falha ao gerar modelos!';
    $msg = $e->getMessage();
}
 
print $title . "\n" . $msg;

Agora você pode gerar seus modelos, vamos criar uma pasta “models” e executar o script:

$ mkdir models
$ php build-models.php
Modelos gerados com sucesso em "models"
$ ls models
ActorInfo.php  Category.php  CustomerList.php  FilmCategory.php  FilmText.php   Language.php                Rental.php               StaffList.php
Actor.php      City.php      Customer.php      FilmList.php      generated      NicerButSlowerFilmList.php  SalesByFilmCategory.php  Staff.php
Address.php    Country.php   FilmActor.php     Film.php          Inventory.php  Payment.php                 SalesByStore.php         Store.php

Como pode ver, dentro da models foi criado um arquivo para cada uma de suas tabelas e uma pasta chamada “generated”, os arquivos que estão na “models” podem ser modificados normalmente enquanto os arquivos dentro da pasta “generated” não deveram ser modificados pois qualquer modificação será perdida quando gerar novamente seus modelos.

Utilizando o Doctrine

Veremos agora um pequeno exemplo de como buscar todos os registros de uma tabela e imprimir os resultados, crie um arquivo “intro.php” que ficara assim:

<?php
 
require 'bootstrap.php';
 
try {
    $actorTable = Doctrine::getTable('Actor');
    $actors = $actorTable->findAll();
 
    foreach($actors as $a)
        print $a->first_name . ' ' . $a->last_name . '<br />';
} catch (Doctrine_Exception $e) {
    print $e->getMessage();
}

Sempre que for utilizar a biblioteca precisara de seu “bootstrap.php”, ao executar o script acima você devera ver uma listagem com todos os nomes de “atores” cadastrados na tabela “Actor”.

Esta foi uma pequena introdução, no site do projeto você pode encontrar uma boa documentação incluindo um guia de uso e a referencia da API.

12 Responses to “Introdução a ORM em PHP com Doctrine”

  1. André says:

    Muito Bom!!!!!!
    Estou precisando muito de ajuda com o doctrine!
    foi muito esclarecedor
    vlw

  2. [...] o setup do post de introdução a orm em php vamos ver como ficou a classe gerada para o tabela City dentro da pasta “generated”, [...]

  3. Lucas says:

    Muito boa a explicação.
    Estou começando a mexer com o Doctrine e estava meio perdido.
    Não sabia por onde começar..

    Obrigado.

  4. Michael Paul says:

    Já uso ela a um bom tempo, se precisar de alguma ajuda pode contar comigo :)

  5. Lucas says:

    Otimo, Muito Obrigado.

  6. Lucas says:

    Michael como usar o JOIN com Doctrine?

    Obrigado.

  7. Michael Paul says:

    Usando a DQL, da uma olha na sintaxe do join.

  8. André says:

    Olá!
    Tudo bem?
    Tenho um arquivo txt com 2.000.000 linhas e gostaria de salvar em banco.
    Escrevi o seguinte código:

    if($arqSerial = fopen(‘serial.txt’, ‘r’)){
    while (!feof($arqSerial)){
    $serial = new Serial();
    $serial->ser_serial = substr(fgets($arqSerial),0,6);
    $serial->save();
    unset($serial);
    }
    fclose($arqSerial);
    }

    Acontece que dá estouro de memória.
    Mas quando faço sem o uso do Doctrine ele salva os 2.000.000 de registros.

    O que posso fazer pra consertar isso?

    Muito Obrigado

  9. Michael Paul says:

    Olá André,

    Não use o Doctrine para fazer isso, cada instância de sua classe “Serial” estará herdando a classe Doctrine_Record que consome bastante recursos. Você pode usar o PDO diretamente e executar o insert.

    < ?php
    // seu arquivo de configuração do doctrine
    require 'bootstrap.php';
    // obter instância do PDO
    $conn = Doctrine_Manager::getInstance()->getCurrentConnection()->getDbh();
    // executar qualquer query
    $conn->query('SELECT now()');

    Se ainda tiver problemas com memória você pode aumentar o limite usado pelo php com a função “ini_set”:

    < ?php

    ini_set('memory_limit', '256M');

  10. Gustavo says:

    Como posso fazer relacionamento entre duas tabelas no Doctrine..
    Obrigado.

  11. Michael Paul says:

    Olá Gustavo, fiz 2 post’s sobre o assunto, o “Definindo relacionamento dos modelos em ORM” e “Relacionamentos Many-to-Many no Doctrine”. Ambos são para o Doctrine.

  12. Ruan Carlos says:

    Cara se tu fosse mulher eu te dava um beijo =D. Mano, você conseguiu explicar claramente em uma página o que o manuel não ensinou em 20 =D. Simples e descomplicado.

    OBS: A nova cersão .3 o caminho é diferente.

    require 'Doctrine-1.2.3/Doctrine.php';

    Obrigado

Leave a Reply