SlideShare una empresa de Scribd logo
1 de 87
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

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
 

La actualidad más candente (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
 

Destacado

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
 

Destacado (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
 

Similar 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
 

Similar 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
 

Más 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
 

Más 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

Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 

Último (20)

Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 

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