SlideShare uma empresa Scribd logo
1 de 87
Baixar para ler offline
Enterprise Database Design
      Patterns in PHP
     Hugo Hamon – OSIDays 2011
By Martin Fowler

§    Table Module
§    Transaction Script
§    Row Data Gateway
§    Table Data Gateway
§    Active Record
§    Data Mapper
§    Unit of Work
§    Identity Map
§    Data Transfer Object
§    …
Table Data Gateway
« An object that acts as a Gateway to

a database table. One instance

handles all the rows in the table. »
                                  Martin Fowler	
  
Same as
Data Access Object
                     Martin Fowler	
  
CRUD
$table = new OrderGateway(new Connection('...'));

$table->insert('XX123456789', 358.80, 'unpaid');

$table->update(42, 'XX123456789', 358.80, 'paid');

$table->delete(42);
class OrderGateway
{
    private $conn;

    public function __construct(Connection $conn)
    {
        $this->conn = $conn;
    }
}
class OrderGateway
{
    public function insert($reference, $amount, $status)
    {
        $query = 'INSERT INTO orders (reference, amount,
status) VALUES (?, ?, ?)';

        $data = array($reference, $amount, $status);

        $this->conn->executeQuery($query, $data);

        return $this->conn->lastInsertId();
    }
}
class OrderGateway
{
    public function update($pk, $ref, $amount, $status)
    {
        $query = 'UPDATE orders SET reference = ?, amount
= ?, status = ? WHERE id = ?';

        $data = array($ref, $amount, $status, $pk);

        return $this->conn->executeQuery($query, $data);
    }
}
class OrderGateway
{
    public function delete($pk)
    {
        return $this->conn->executeQuery(
            'DELETE FROM orders WHERE id = ?',
            array($pk)
        );
    }
}
Finders
$orders = $table->findAll();

$orders = $table->findPaidOrders();
$orders = $table->findUnpaidOrders();

$orders = $table->findBy(array(
    'status' => 'paid',
    'amount' => 250.00
));

$order = $table->find(42);
$order = $table->findOneBy(array('reference' => '...'));
class OrderGateway
{
    public function findAll()
    {
        $query = 'SELECT * FROM orders';
        return $this->conn->fetchAll($query);
    }

    public function find($pk)
    {
        $rs = $this->conn->findBy(array('id' => $pk));
        return 1 === count($rs) ? $rs[0] : false;
    }
}
public function findBy(array $criteria)
{
    $where = array();
    foreach ($criteria as $field => $value) {
        $where[] = sprintf('%s = ?');
    }

    $q = sprintf(
        'SELECT * FROM orders WHERE %s',
        implode(' AND ', $where)
    );

    return $this->conn->fetchAll($q, array_values($criteria));
}
public function findPaidOrders()
{
    return $this->findBy(array('status' => 'paid'));
}



public function findUnpaidOrders()
{
    return $this->findBy(array('status' => 'unpaid'));
}
When to use it?
Row Data Gateway
« An object that acts as a Gateway to

a single record in a data source. There

is one instance per row. »
                                  Martin Fowler	
  
CRUD
class Order
{
    private   $id;
    private   $reference;
    private   $amount;
    private   $vat;
    private   $total;
    private   $createdAt;

    // Getters and setters for each property
    // ...
}
$conn = new Connection('...');

$order = new OrderGateway();
$order->setReference('XX12345678');
$order->setAmount(300.00);
$order->setVat(58.80);
$order->setTotal(358.80);
$order->setCreatedAt(new DateTime());

$order->insert($conn);
class OrderGateway
{
    public function insert(Connection $conn)
    {
        $query = 'INSERT INTO orders (reference, amount, vat,
total, created_at) VALUES (?, ?, ?, ?, ?)';

        $data = array(
            $this->reference, $this->amount, $this->vat,
            $this->total, $this->createdAt->format('Y-m-d H:i:s')
        );

        $conn->executeQuery($query, $data);
        $this->id = $conn->lastInsertId();
    }
}
Finders
OrderFinder::setConnection($conn);

$order = OrderFinder::findByReference('XX12345678');

echo sprintf('%01.2f euros', $order->getTotal());
class OrderFinder
{
    static public function findByReference($reference)
    {
       $query = 'SELECT * FROM orders WHERE reference = ?';

        $rs = static::getConnection()
            ->fetchSingle($query, array($reference))
        ;

        return $rs ? OrderGateway::load($rs) : false;
    }
}
class OrderGateway
{
    static public function load(array $rs)
    {
        $order = new OrderGateway($rs['id']);
        $order->setReference($rs['reference']);
        $order->setAmount($rs['amount']);
        $order->setVat($rs['vat']);
        $order->setTotal($rs['total']);
        $order->setCreatedAt(new DateTime($rs['created_at']));

        return $order;
    }
}
OrderFinder::setConnection($conn);

$orders = OrderFinder::findMostExpensiveOrders(10);

foreach ($orders as $order) {

    echo $order->getReference(), "n";
    echo sprintf('%01.2f euros', $order->getTotal()), "n";
    echo "n-----n";
}
class OrderFinder
{
    static public function findMostExpensiveOrders($limit)
    {
        $orders = array();
        $query = 'SELECT * FROM orders ORDER BY total DESC LIMIT ?';
        $rs = static::getConnection()->fetchAll($query, array($limit));

        foreach ($rs as $data) {
            $orders[] = OrderGateway::load($data);
        }

        return $orders;
    }
}
When to use it?
Active Record
« An object that wraps a row in a
database table or view, encapsulates
the database access, and adds
domain logic on that data. »
                                Martin Fowler	
  
Active Record
                =
Row Data Gateway + Business Logic
Active Record
       =
Data + Behaviors
Active Record
          =
Properties + Methods
class Order
{
    private   $id;
    private   $reference;
    private   $amount;
    private   $vat;
    private   $vatRate;
    private   $total;
    private   $createdAt;
    private   $status;
    private   $isPaid;

    // Getters and setters for each property
    // ...
}
class Order
{
    public function __construct($id = null)
    {
        if (null !== $id) {
            $this->id = $id;
        }

        $this->vatRate = 0.00;
        $this->vat = 0.00;
        $this->amount = 0.00;
        $this->total = 0.00;
        $this->isPaid = false;
        $this->status = 'processing';
        $this->createdAt = new DateTime();
    }
}
$conn = new Connection('...');

$order = new Order();
$order->setReference('XX12345678');
$order->setAmount(300.00);
$order->setVatRate(0.196);

$order->applyDiscount(20.00);
$order->updateTotal();

$order->save($conn);
class Order
{
    public function applyDiscount($discount)
    {
        $this->amount -= $discount;
    }

    public function updateTotal()
    {
        if ($this->vatRate) {
            $this->vat = $this->amount * $this->vatRate;
        }

        $this->total = $this->amount + $this->vat;
    }
}
class Order
{
    public function isPaid()
    {
        return $this->isPaid;
    }

    public function setPaid()
    {
         $this->isPaid = true;
    }
}
class Order
{
    public function isReadyForShipment()
    {
        return $this->isPaid() && 'complete' == $this->status;
    }

    public function ship($address)
    {
        $this->doShipment($address);
        $this->status = 'shipped';
    }
}
class OrderController
{
    public function confirmAction($reference)
    {
        $conn = $this->getDatabaseConnection();
        $order = ...;
        $order->setPaid();
        $order->save($conn);

        if ($order->isReadyForShipment()) {
            $order->ship();
            return $this->view->render('ship.php', array('order' => $order));
        }

        return $this->view->render('pending.php', array('order' => $order));
    }
}
Refactoring
abstract class ActiveRecord
{
    protected $fields = array();

    abstract public function getTableName();

    public function save(Connection $conn)
    {
        // insert or update $fields in the database
    }

    public function delete(Connection $conn)
    {
        // delete the object from the database
    }
}
class Order extends ActiveRecord
{
    private $amount;

    abstract public function getTableName()
    {
        return 'tbl_orders';
    }

    public function setAmount($amount)
    {
        $this->amount = $amount;
        $this->fields['amount'] = $amount;
    }
}
When to use it?
Data Mapper
« A layer of Mappers that moves data
between objects and a database
while keeping them independent of
each other and the mapper itself. »
                                 Martin Fowler	
  
« Man in the Middle »
http://martinfowler.com	
  
class OrderMapper
{
    private $conn;

    public function __construct(Connection $conn) {
        $this->conn = $conn;
    }

    public function store(Order $order) {
        // Execute the query to persist the object to the DB
    }

    public function remove(Order $order) {
        // Executes the query to remove the object to the DB
    }
}
$order = new Order();
$order->setReference('XX12345678');
$order->setAmount(300.00);
$order->setVatRate(0.196);
$order->updateTotal();

$conn = new Connection('mysql:host=localhost ...');

$mapper = new OrderMapper($conn);
$mapper->store($order);
class OrderMapper
{
    public function findAll()
    {
        $objects = array();
        $query = 'SELECT id, reference, vat ... FROM orders';

        foreach ($this->conn->fetchAll($query) as $data) {
            $object = new Order($data['id']);
            $object->load($data);
            $objects[] = $object;
        }

        return $objects;
    }
}
class OrderMapper
{
    public function find($pk)
    {
        $query = 'SELECT id, vat ... FROM orders WHERE id = ?';

        $object = false;
        if (false !== $data = conn->fetch($query, array($pk))) {
            $object = new Order($data['id']);
            $object->load($data);
        }

        return $object;
    }
}
$conn = new Connection('mysql:host=localhost ...');

$mapper = new OrderMapper($conn);

$order = $mapper->find(42);

$order->setAmount(399.00);
$order->updateTotal();

$mapper->store($order);
Unit testing
class OrderTest extends PHPUnit_Framework_TestCase
{
    public function testUpdateTotal()
    {
        $order = new Order();
        $order->setAmount(299.00);
        $order->setVatRate(0.196);
        $order->updateTotal();

        $this->assertEquals(58.60, $order->getVat());
        $this->assertEquals(357.60, $order->getTotal());
    }
}
When to use it?
IdentityMap
« Ensures that each object gets
loaded only once by keeping every
loaded object in a map. »
                              Martin Fowler	
  
$conn = new Connection('mysql:host=localhost ...');

$mapper = new OrderMapper($conn);

$orderA = $mapper->find(42);
$orderB = $mapper->find(42);
$orderC = $mapper->find(42);

// 3 SQL queries for getting the same object
The solution
class IdentityMap implements IdentityMapInterface
{
    private $entities;

    public function fetch($class, $pk)
    {
        $key = $this->getKey($class, $pk);
        if (isset($this->entities[$key])) {
            return $this->entities[$key];
        }

        return false;
    }
}
class IdentityMap implements IdentityMapInterface
{
    public function store(ValueObjectInterface $entity)
    {
        $key = $this->getKey($class, $entity->getId());

        $this->entities[$key] = $entity;
    }

    private function getKey($class, $pk)
    {
        return $class.'-'.$pk;
    }
}
class Order   implements ValueObjectInterface
{
    private   $id;
    private   $reference;
    private   $amount;
    // ...

    public function getId()
    {
        return $this->id;
    }
}
class OrderMapper extends DatabaseMapper
{
    private $map;

    public function __construct(IdentityMap $map, ...)
    {
        parent::__construct($conn);

        $this->map = $map;
    }
}
class OrderMapper extends DatabaseMapper
{
    public function store(Order $order)
    {
        parent::store($order);

        $this->map->store('Order', $object);
    }
}
class OrderMapper extends DatabaseMapper
{
    public function find($pk)
    {
        if (false !== $object = $this->map->fetch($pk)) {
            return $object;
        }

        if (false !== $object = parent::find($pk)) {
            $this->map->store('Order', $object);
        }

        return $object;
    }
}
$conn = new Connection('mysql:host=localhost ...');
$mapper = new OrderMapper(new IdentityMap(), $conn);

$orderA = $mapper->find(42);            // Query
$orderB = $mapper->find(42);            // No query

$orderB->setAmount(299.00);
$orderB->setVatRate(0.196);
$orderB->updateTotal();

$mapper->store($orderB);

$orderC = $mapper->find(42);            // No query
Query Object
$query = Query::create()
  ->select(array('id', 'reference', 'amount', 'status'))
  ->from('orders')
  ->where(Criteria::equals('status', 'paid'))
  ->where(Criteria::greaterThan('amount', 2000))
  ->where(Criteria::like('reference', 'XX123%'))
  ->orderBy('amount', 'desc')
  ->getSql()
;

// SELECT id, reference, amount, status
// WHERE status = ? AND amount > ? AND reference LIKE ?
// ORDER BY amount DESC
class Criteria
{
    private $field;
    private $operator;
    private $parameters;

    public function __construct($field, $operator, $value)
    {
        $this->field = $field;
        $this->operator = $operator;
        $this->parameters[] = $value;
    }
}
class Criteria
{
  static public function equal($field, $value, $vars)
  {
    return new Criteria($field, '=', $vars);
  }

    static public function notEqual($field, $value, $vars)
    {
      return new Criteria($field, '<>', $vars);
    }
}
Custom Queries
class OrderQuery extends Query
{
    public function filterByPriority($amount)
    {
        return $this
            ->where(Criteria::equal('status', 'paid'))
            ->where(Criteria::greaterThan('amount', $amount))
        ;
    }

    public function filterByReference($like)
    {
        return $this->where(Criteria::like('reference', $like));
    }
}
$query = OrderQuery::create()
    ->filterByPriority(2000)
    ->filterByReference('%567%')
    ->orderByAmount('DESC')
    ->getSql()
;
ORM Tools
Zend_DB            Propel

    Doctrine 2.x

Pomm        Doctrine 1.2
Quizz

Can you guess the patterns?
$table = new Author();

// New empty row
$row = $table->createRow();

// Insert a new row
$row->firstName = 'Jules';
$row->lastName = 'Verne';
$row->save();
$pax1 = new Passenger('Hugo Hamon', '7B');
$pax2 = new Passenger('John Smith', '3A');

$aircraft = new Plane();
$aircraft->setCapacity(120);
$aircraft->addPassenger($pax1);
$aircraft->addPassenger($pax2);
$aircraft->save();

$pax2->changeSeat('2C');
$pax2->save();

$aircraft->isAvailableSeat('3A') ? 'Yes' : 'No';
$post = new BlogPost();
$post->setTitle('My First Blog Post');
$post->setBody('Some content...');

$author = new Author();
$author->setName('Hugo Hamon');
$author->addPost($post);

$em->persist($user);
$em->persist($post);
$em->flush();
$data = array(
    'first_name' => 'Jules',
    'last_name' => 'Vernes',
);

$table = new AuthorTable();
$table->insert($data);
Conclusion
By Martin Fowler

§    Table Module
§    Transaction Script
§    Row Data Gateway
§    Table Data Gateway
§    Active Record
§    Data Mapper
§    Unit of Work
§    Identity Map
§    Data Transfer Object
§    …
Ques&ons?	
  




 92-98, boulevard Victor Hugo
 92 115 Clichy Cedex
 trainings@sensio.com (+33 (0)1 40 99 82 11)

 sensiolabs.com - symfony.com – trainings.sensiolabs.com

Mais conteúdo relacionado

Mais procurados

Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Leonardo Proietti
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of LithiumNate Abele
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionNate Abele
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm OldRoss Tuck
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201Fabien Potencier
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHPGuilherme Blanco
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPChad Gray
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016Kacper Gunia
 
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011Masahiro Nagano
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsGuilherme Blanco
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix itRafael Dohms
 
Electrify your code with PHP Generators
Electrify your code with PHP GeneratorsElectrify your code with PHP Generators
Electrify your code with PHP GeneratorsMark Baker
 

Mais procurados (20)

Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of Lithium
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm Old
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHP
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix it
 
Electrify your code with PHP Generators
Electrify your code with PHP GeneratorsElectrify your code with PHP Generators
Electrify your code with PHP Generators
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 

Destaque

Data Antipatterns
Data AntipatternsData Antipatterns
Data AntipatternsInes Sombra
 
Database Anti Patterns
Database Anti PatternsDatabase Anti Patterns
Database Anti PatternsRobert Treat
 
Architectural Patterns of Resilient Distributed Systems
 Architectural Patterns of Resilient Distributed Systems Architectural Patterns of Resilient Distributed Systems
Architectural Patterns of Resilient Distributed SystemsInes Sombra
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2Hugo Hamon
 
This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?
This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?
This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?Mark Heckler
 
API 101 Workshop from APIStrat Conference
API 101 Workshop from APIStrat ConferenceAPI 101 Workshop from APIStrat Conference
API 101 Workshop from APIStrat ConferenceKirsten Hunter
 
Designing for developers
Designing for developersDesigning for developers
Designing for developersKirsten Hunter
 
Prototyping in the cloud
Prototyping in the cloudPrototyping in the cloud
Prototyping in the cloudKirsten Hunter
 
Développeurs, cachez-moi ça ! (Paris Web 2011)
Développeurs, cachez-moi ça ! (Paris Web 2011)Développeurs, cachez-moi ça ! (Paris Web 2011)
Développeurs, cachez-moi ça ! (Paris Web 2011)Hugo Hamon
 
Monitor the quality of your Symfony projects
Monitor the quality of your Symfony projectsMonitor the quality of your Symfony projects
Monitor the quality of your Symfony projectsHugo Hamon
 
Symfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 PerformantSymfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 PerformantHugo Hamon
 
Symfony2 en pièces détachées
Symfony2 en pièces détachéesSymfony2 en pièces détachées
Symfony2 en pièces détachéesHugo Hamon
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console componentHugo Hamon
 

Destaque (20)

Data Antipatterns
Data AntipatternsData Antipatterns
Data Antipatterns
 
Database Anti Patterns
Database Anti PatternsDatabase Anti Patterns
Database Anti Patterns
 
Architectural Patterns of Resilient Distributed Systems
 Architectural Patterns of Resilient Distributed Systems Architectural Patterns of Resilient Distributed Systems
Architectural Patterns of Resilient Distributed Systems
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?
This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?
This stuff is cool, but...HOW CAN I GET MY COMPANY TO DO IT?
 
Quantifying fitness
Quantifying fitnessQuantifying fitness
Quantifying fitness
 
Liberating your data
Liberating your dataLiberating your data
Liberating your data
 
API 101 Workshop from APIStrat Conference
API 101 Workshop from APIStrat ConferenceAPI 101 Workshop from APIStrat Conference
API 101 Workshop from APIStrat Conference
 
Designing for developers
Designing for developersDesigning for developers
Designing for developers
 
Prototyping in the cloud
Prototyping in the cloudPrototyping in the cloud
Prototyping in the cloud
 
Développeurs, cachez-moi ça ! (Paris Web 2011)
Développeurs, cachez-moi ça ! (Paris Web 2011)Développeurs, cachez-moi ça ! (Paris Web 2011)
Développeurs, cachez-moi ça ! (Paris Web 2011)
 
API First
API FirstAPI First
API First
 
Facebook appsincloud
Facebook appsincloudFacebook appsincloud
Facebook appsincloud
 
Monitor the quality of your Symfony projects
Monitor the quality of your Symfony projectsMonitor the quality of your Symfony projects
Monitor the quality of your Symfony projects
 
Liberating your data
Liberating your dataLiberating your data
Liberating your data
 
Symfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 PerformantSymfony2 - Un Framework PHP 5 Performant
Symfony2 - Un Framework PHP 5 Performant
 
Symfony2 en pièces détachées
Symfony2 en pièces détachéesSymfony2 en pièces détachées
Symfony2 en pièces détachées
 
Api 101
Api 101Api 101
Api 101
 
Polyglot copy
Polyglot copyPolyglot copy
Polyglot copy
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console component
 

Semelhante a Database Design Patterns

Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 
The Art of Transduction
The Art of TransductionThe Art of Transduction
The Art of TransductionDavid Stockton
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにYuya Takeyama
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011Alessandro Nadalin
 
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...Mateusz Zalewski
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShellGaetano Causio
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolveXSolve
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency InjectionRifat Nabi
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...DevClub_lv
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Developmentjsmith92
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceIvan Chepurnyi
 

Semelhante a Database Design Patterns (20)

Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
The Art of Transduction
The Art of TransductionThe Art of Transduction
The Art of Transduction
 
Drupal7 dbtng
Drupal7  dbtngDrupal7  dbtng
Drupal7 dbtng
 
Presentation1
Presentation1Presentation1
Presentation1
 
Database api
Database apiDatabase api
Database api
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くために
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShell
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
Oops in php
Oops in phpOops in php
Oops in php
 
Taming Command Bus
Taming Command BusTaming Command Bus
Taming Command Bus
 
Drupal 8 database api
Drupal 8 database apiDrupal 8 database api
Drupal 8 database api
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
 

Mais de Hugo Hamon

Intégration Continue PHP avec Jenkins CI
Intégration Continue PHP avec Jenkins CIIntégration Continue PHP avec Jenkins CI
Intégration Continue PHP avec Jenkins CIHugo Hamon
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Hugo Hamon
 
Intégration continue des projets PHP avec Jenkins
Intégration continue des projets PHP avec JenkinsIntégration continue des projets PHP avec Jenkins
Intégration continue des projets PHP avec JenkinsHugo Hamon
 
Mieux Développer en PHP avec Symfony
Mieux Développer en PHP avec SymfonyMieux Développer en PHP avec Symfony
Mieux Développer en PHP avec SymfonyHugo Hamon
 
Introduction à Symfony2
Introduction à Symfony2Introduction à Symfony2
Introduction à Symfony2Hugo Hamon
 
Exposer des services web SOAP et REST avec symfony 1.4 et Zend Framework
Exposer des services web SOAP et REST avec symfony 1.4 et Zend FrameworkExposer des services web SOAP et REST avec symfony 1.4 et Zend Framework
Exposer des services web SOAP et REST avec symfony 1.4 et Zend FrameworkHugo Hamon
 

Mais de Hugo Hamon (6)

Intégration Continue PHP avec Jenkins CI
Intégration Continue PHP avec Jenkins CIIntégration Continue PHP avec Jenkins CI
Intégration Continue PHP avec Jenkins CI
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2
 
Intégration continue des projets PHP avec Jenkins
Intégration continue des projets PHP avec JenkinsIntégration continue des projets PHP avec Jenkins
Intégration continue des projets PHP avec Jenkins
 
Mieux Développer en PHP avec Symfony
Mieux Développer en PHP avec SymfonyMieux Développer en PHP avec Symfony
Mieux Développer en PHP avec Symfony
 
Introduction à Symfony2
Introduction à Symfony2Introduction à Symfony2
Introduction à Symfony2
 
Exposer des services web SOAP et REST avec symfony 1.4 et Zend Framework
Exposer des services web SOAP et REST avec symfony 1.4 et Zend FrameworkExposer des services web SOAP et REST avec symfony 1.4 et Zend Framework
Exposer des services web SOAP et REST avec symfony 1.4 et Zend Framework
 

Último

Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 

Último (20)

Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 

Database Design Patterns

  • 1.
  • 2. Enterprise Database Design Patterns in PHP Hugo Hamon – OSIDays 2011
  • 3. By Martin Fowler §  Table Module §  Transaction Script §  Row Data Gateway §  Table Data Gateway §  Active Record §  Data Mapper §  Unit of Work §  Identity Map §  Data Transfer Object §  …
  • 5. « An object that acts as a Gateway to a database table. One instance handles all the rows in the table. » Martin Fowler  
  • 6. Same as Data Access Object Martin Fowler  
  • 8. $table = new OrderGateway(new Connection('...')); $table->insert('XX123456789', 358.80, 'unpaid'); $table->update(42, 'XX123456789', 358.80, 'paid'); $table->delete(42);
  • 9. class OrderGateway { private $conn; public function __construct(Connection $conn) { $this->conn = $conn; } }
  • 10. class OrderGateway { public function insert($reference, $amount, $status) { $query = 'INSERT INTO orders (reference, amount, status) VALUES (?, ?, ?)'; $data = array($reference, $amount, $status); $this->conn->executeQuery($query, $data); return $this->conn->lastInsertId(); } }
  • 11. class OrderGateway { public function update($pk, $ref, $amount, $status) { $query = 'UPDATE orders SET reference = ?, amount = ?, status = ? WHERE id = ?'; $data = array($ref, $amount, $status, $pk); return $this->conn->executeQuery($query, $data); } }
  • 12. class OrderGateway { public function delete($pk) { return $this->conn->executeQuery( 'DELETE FROM orders WHERE id = ?', array($pk) ); } }
  • 14. $orders = $table->findAll(); $orders = $table->findPaidOrders(); $orders = $table->findUnpaidOrders(); $orders = $table->findBy(array( 'status' => 'paid', 'amount' => 250.00 )); $order = $table->find(42); $order = $table->findOneBy(array('reference' => '...'));
  • 15. class OrderGateway { public function findAll() { $query = 'SELECT * FROM orders'; return $this->conn->fetchAll($query); } public function find($pk) { $rs = $this->conn->findBy(array('id' => $pk)); return 1 === count($rs) ? $rs[0] : false; } }
  • 16. public function findBy(array $criteria) { $where = array(); foreach ($criteria as $field => $value) { $where[] = sprintf('%s = ?'); } $q = sprintf( 'SELECT * FROM orders WHERE %s', implode(' AND ', $where) ); return $this->conn->fetchAll($q, array_values($criteria)); }
  • 17. public function findPaidOrders() { return $this->findBy(array('status' => 'paid')); } public function findUnpaidOrders() { return $this->findBy(array('status' => 'unpaid')); }
  • 18. When to use it?
  • 20. « An object that acts as a Gateway to a single record in a data source. There is one instance per row. » Martin Fowler  
  • 21. CRUD
  • 22. class Order { private $id; private $reference; private $amount; private $vat; private $total; private $createdAt; // Getters and setters for each property // ... }
  • 23. $conn = new Connection('...'); $order = new OrderGateway(); $order->setReference('XX12345678'); $order->setAmount(300.00); $order->setVat(58.80); $order->setTotal(358.80); $order->setCreatedAt(new DateTime()); $order->insert($conn);
  • 24. class OrderGateway { public function insert(Connection $conn) { $query = 'INSERT INTO orders (reference, amount, vat, total, created_at) VALUES (?, ?, ?, ?, ?)'; $data = array( $this->reference, $this->amount, $this->vat, $this->total, $this->createdAt->format('Y-m-d H:i:s') ); $conn->executeQuery($query, $data); $this->id = $conn->lastInsertId(); } }
  • 27. class OrderFinder { static public function findByReference($reference) { $query = 'SELECT * FROM orders WHERE reference = ?'; $rs = static::getConnection() ->fetchSingle($query, array($reference)) ; return $rs ? OrderGateway::load($rs) : false; } }
  • 28. class OrderGateway { static public function load(array $rs) { $order = new OrderGateway($rs['id']); $order->setReference($rs['reference']); $order->setAmount($rs['amount']); $order->setVat($rs['vat']); $order->setTotal($rs['total']); $order->setCreatedAt(new DateTime($rs['created_at'])); return $order; } }
  • 29. OrderFinder::setConnection($conn); $orders = OrderFinder::findMostExpensiveOrders(10); foreach ($orders as $order) { echo $order->getReference(), "n"; echo sprintf('%01.2f euros', $order->getTotal()), "n"; echo "n-----n"; }
  • 30. class OrderFinder { static public function findMostExpensiveOrders($limit) { $orders = array(); $query = 'SELECT * FROM orders ORDER BY total DESC LIMIT ?'; $rs = static::getConnection()->fetchAll($query, array($limit)); foreach ($rs as $data) { $orders[] = OrderGateway::load($data); } return $orders; } }
  • 31. When to use it?
  • 33. « An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. » Martin Fowler  
  • 34. Active Record = Row Data Gateway + Business Logic
  • 35. Active Record = Data + Behaviors
  • 36. Active Record = Properties + Methods
  • 37. class Order { private $id; private $reference; private $amount; private $vat; private $vatRate; private $total; private $createdAt; private $status; private $isPaid; // Getters and setters for each property // ... }
  • 38. class Order { public function __construct($id = null) { if (null !== $id) { $this->id = $id; } $this->vatRate = 0.00; $this->vat = 0.00; $this->amount = 0.00; $this->total = 0.00; $this->isPaid = false; $this->status = 'processing'; $this->createdAt = new DateTime(); } }
  • 39. $conn = new Connection('...'); $order = new Order(); $order->setReference('XX12345678'); $order->setAmount(300.00); $order->setVatRate(0.196); $order->applyDiscount(20.00); $order->updateTotal(); $order->save($conn);
  • 40. class Order { public function applyDiscount($discount) { $this->amount -= $discount; } public function updateTotal() { if ($this->vatRate) { $this->vat = $this->amount * $this->vatRate; } $this->total = $this->amount + $this->vat; } }
  • 41. class Order { public function isPaid() { return $this->isPaid; } public function setPaid() { $this->isPaid = true; } }
  • 42. class Order { public function isReadyForShipment() { return $this->isPaid() && 'complete' == $this->status; } public function ship($address) { $this->doShipment($address); $this->status = 'shipped'; } }
  • 43. class OrderController { public function confirmAction($reference) { $conn = $this->getDatabaseConnection(); $order = ...; $order->setPaid(); $order->save($conn); if ($order->isReadyForShipment()) { $order->ship(); return $this->view->render('ship.php', array('order' => $order)); } return $this->view->render('pending.php', array('order' => $order)); } }
  • 45. abstract class ActiveRecord { protected $fields = array(); abstract public function getTableName(); public function save(Connection $conn) { // insert or update $fields in the database } public function delete(Connection $conn) { // delete the object from the database } }
  • 46. class Order extends ActiveRecord { private $amount; abstract public function getTableName() { return 'tbl_orders'; } public function setAmount($amount) { $this->amount = $amount; $this->fields['amount'] = $amount; } }
  • 47. When to use it?
  • 49. « A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself. » Martin Fowler  
  • 50. « Man in the Middle »
  • 52. class OrderMapper { private $conn; public function __construct(Connection $conn) { $this->conn = $conn; } public function store(Order $order) { // Execute the query to persist the object to the DB } public function remove(Order $order) { // Executes the query to remove the object to the DB } }
  • 53. $order = new Order(); $order->setReference('XX12345678'); $order->setAmount(300.00); $order->setVatRate(0.196); $order->updateTotal(); $conn = new Connection('mysql:host=localhost ...'); $mapper = new OrderMapper($conn); $mapper->store($order);
  • 54. class OrderMapper { public function findAll() { $objects = array(); $query = 'SELECT id, reference, vat ... FROM orders'; foreach ($this->conn->fetchAll($query) as $data) { $object = new Order($data['id']); $object->load($data); $objects[] = $object; } return $objects; } }
  • 55. class OrderMapper { public function find($pk) { $query = 'SELECT id, vat ... FROM orders WHERE id = ?'; $object = false; if (false !== $data = conn->fetch($query, array($pk))) { $object = new Order($data['id']); $object->load($data); } return $object; } }
  • 56. $conn = new Connection('mysql:host=localhost ...'); $mapper = new OrderMapper($conn); $order = $mapper->find(42); $order->setAmount(399.00); $order->updateTotal(); $mapper->store($order);
  • 58. class OrderTest extends PHPUnit_Framework_TestCase { public function testUpdateTotal() { $order = new Order(); $order->setAmount(299.00); $order->setVatRate(0.196); $order->updateTotal(); $this->assertEquals(58.60, $order->getVat()); $this->assertEquals(357.60, $order->getTotal()); } }
  • 59. When to use it?
  • 61. « Ensures that each object gets loaded only once by keeping every loaded object in a map. » Martin Fowler  
  • 62. $conn = new Connection('mysql:host=localhost ...'); $mapper = new OrderMapper($conn); $orderA = $mapper->find(42); $orderB = $mapper->find(42); $orderC = $mapper->find(42); // 3 SQL queries for getting the same object
  • 64. class IdentityMap implements IdentityMapInterface { private $entities; public function fetch($class, $pk) { $key = $this->getKey($class, $pk); if (isset($this->entities[$key])) { return $this->entities[$key]; } return false; } }
  • 65. class IdentityMap implements IdentityMapInterface { public function store(ValueObjectInterface $entity) { $key = $this->getKey($class, $entity->getId()); $this->entities[$key] = $entity; } private function getKey($class, $pk) { return $class.'-'.$pk; } }
  • 66. class Order implements ValueObjectInterface { private $id; private $reference; private $amount; // ... public function getId() { return $this->id; } }
  • 67. class OrderMapper extends DatabaseMapper { private $map; public function __construct(IdentityMap $map, ...) { parent::__construct($conn); $this->map = $map; } }
  • 68. class OrderMapper extends DatabaseMapper { public function store(Order $order) { parent::store($order); $this->map->store('Order', $object); } }
  • 69. class OrderMapper extends DatabaseMapper { public function find($pk) { if (false !== $object = $this->map->fetch($pk)) { return $object; } if (false !== $object = parent::find($pk)) { $this->map->store('Order', $object); } return $object; } }
  • 70. $conn = new Connection('mysql:host=localhost ...'); $mapper = new OrderMapper(new IdentityMap(), $conn); $orderA = $mapper->find(42); // Query $orderB = $mapper->find(42); // No query $orderB->setAmount(299.00); $orderB->setVatRate(0.196); $orderB->updateTotal(); $mapper->store($orderB); $orderC = $mapper->find(42); // No query
  • 72. $query = Query::create() ->select(array('id', 'reference', 'amount', 'status')) ->from('orders') ->where(Criteria::equals('status', 'paid')) ->where(Criteria::greaterThan('amount', 2000)) ->where(Criteria::like('reference', 'XX123%')) ->orderBy('amount', 'desc') ->getSql() ; // SELECT id, reference, amount, status // WHERE status = ? AND amount > ? AND reference LIKE ? // ORDER BY amount DESC
  • 73. class Criteria { private $field; private $operator; private $parameters; public function __construct($field, $operator, $value) { $this->field = $field; $this->operator = $operator; $this->parameters[] = $value; } }
  • 74. class Criteria { static public function equal($field, $value, $vars) { return new Criteria($field, '=', $vars); } static public function notEqual($field, $value, $vars) { return new Criteria($field, '<>', $vars); } }
  • 76. class OrderQuery extends Query { public function filterByPriority($amount) { return $this ->where(Criteria::equal('status', 'paid')) ->where(Criteria::greaterThan('amount', $amount)) ; } public function filterByReference($like) { return $this->where(Criteria::like('reference', $like)); } }
  • 77. $query = OrderQuery::create() ->filterByPriority(2000) ->filterByReference('%567%') ->orderByAmount('DESC') ->getSql() ;
  • 79. Zend_DB Propel Doctrine 2.x Pomm Doctrine 1.2
  • 80. Quizz Can you guess the patterns?
  • 81. $table = new Author(); // New empty row $row = $table->createRow(); // Insert a new row $row->firstName = 'Jules'; $row->lastName = 'Verne'; $row->save();
  • 82. $pax1 = new Passenger('Hugo Hamon', '7B'); $pax2 = new Passenger('John Smith', '3A'); $aircraft = new Plane(); $aircraft->setCapacity(120); $aircraft->addPassenger($pax1); $aircraft->addPassenger($pax2); $aircraft->save(); $pax2->changeSeat('2C'); $pax2->save(); $aircraft->isAvailableSeat('3A') ? 'Yes' : 'No';
  • 83. $post = new BlogPost(); $post->setTitle('My First Blog Post'); $post->setBody('Some content...'); $author = new Author(); $author->setName('Hugo Hamon'); $author->addPost($post); $em->persist($user); $em->persist($post); $em->flush();
  • 84. $data = array( 'first_name' => 'Jules', 'last_name' => 'Vernes', ); $table = new AuthorTable(); $table->insert($data);
  • 86. By Martin Fowler §  Table Module §  Transaction Script §  Row Data Gateway §  Table Data Gateway §  Active Record §  Data Mapper §  Unit of Work §  Identity Map §  Data Transfer Object §  …
  • 87. Ques&ons?   92-98, boulevard Victor Hugo 92 115 Clichy Cedex trainings@sensio.com (+33 (0)1 40 99 82 11) sensiolabs.com - symfony.com – trainings.sensiolabs.com