1. Object-Relational
Mapping in PHP
PHPNW May 2009
Rob Knight - Lead Technical Architect, PRWD
http://robknight.org.uk
2. What is ORM?
⢠Manages the translation of objects into
relational databases, and vice-versa
⢠Most modern programming languages,
including PHP, have a concept of objects
⢠Most databases, including MySQL, Oracle
and Postgresql are relational
3. Objects
⢠Classes deďŹne the blueprint
⢠Classes can inherit from each other
⢠Objects contain data and methods
⢠Object data can include other objects and
arrays/lists
4. A very simple class
<?php
class Employee extends Person {
protected $salary; // ďŹoating-point value
protected $contractLength; // integer value
protected $manager; // reference to another Employee object
protected $skills; // array of 'Skill' objects
protected $history; // array of 'History' objects
protected $id; // unique identiďŹer
}
5. Relational Databases
⢠Basic unit of storage is the table
⢠Tables can only contain simple data types
(numbers, strings, binary data)
⢠No concept of arrays or lists
⢠Tables can be related by foreign keys
6. A simple table
id name salary manager_id contract_length
1 Alice 20000.00 2 12
2 Bob 150000.00 NULL NULL
3 Clive 15000.00 5 6
4 Derek 32000.00 2 NULL
5 Edith 45000.00 2 NULL
7. Advantages of ORM
⢠Everything in PHP
⢠In theory, no need to understand the
underlying data storage system
⢠ORM can make it easy to adopt good
patterns and good database design
⢠Enables familiar OO concepts for
manipulating data
8. Use of PHP concepts
<?php
$employee = new Employee(); // use of object-oriented concepts
$employee->setName(âJoeâ);
$employee->save();
// underlying database could by MySQL, SQLite, Postgres or Oracle
$peopleToFire = EmployeeTable::select(âidâ)->where(âsalary < 20000â)
->andWhere(ârelatedToChairman = 0â)->limit(5);
$subordinates = EmployeeTable::select(â*â)->where(âmanager_id = ?â,
$manager->id); // automatic variable escaping
?>
9. Disadvantages of ORM
⢠The extra layer of abstraction can be a
performance hit
⢠Can be verbose
⢠OO isnât always best
⢠Can be inďŹexible
⢠Sometimes framework-dependent
10. ORM can be verbose
Using Propel
$c = new Criteria();
$cton1 = $c->getNewCriterion(ArticlePeer::TITLE, '%FooBar%',
Criteria::LIKE);
$cton1 = $c->getNewCriterion(ArticlePeer::SUMMARY, '%FooBar%',
Criteria::LIKE);
$cton1->addOr($cton2);
$c->add($cton1);
$c->add(ArticlePeer::PUBLISHED_AT, $begin, Criteria::GREATER_THAN);
$c->addAnd(ArticlePeer::PUBLISHED_AT, $end, Criteria::LESS_THAN);
$article = ArticlePeer::doSelect($c);
SQL Query
SELECT * FROM article WHERE (title LIKE â%FooBar%â OR summary LIKE
â%FooBarâ) AND published_at > {$begin} AND published_at < {$end}
11. There are many ORMs
⢠Doctrine <http://www.doctrine-project.org>
⢠Propel<http://propel.phpdb.org>
⢠Zend_Db <http://framework.zend.com/manual/en/zend.db.html>
⢠Kohana ORM <http://docs.kohanaphp.com/libraries/orm>
⢠PDO <http://www.php.net/pdo>
12. ORM Patterns
From âPatterns of Enterprise Application
Architectureâ - Martin Fowler, 2003
⢠Table Data Gateway
Zend_Db_Table, Propel âPeerâ classes
⢠Row Data Gateway
Zend_Db_Table_Row
⢠Data Mapper
⢠Active Record
Doctrine_Record, Propel classes
13. Table Data Gateway
⢠One class per table
⢠Basic functions for inserting, updating,
deleting data from the table
⢠Can also handle related tables
⢠Data is returned in simple array/POPO
form
14. An example
<?php
// Using Zend_Db_Table to fetch a record from a table
class BlogPostTable extends Zend_Db_Table_Abstract { ... }
$table = new BlogPostTable();
$select = $table->select()->where(âauthor_id = ?â, $id)->order(âcreated
DESCâ);
$row = $table->fetchRow($select); // row is an array, with keys for
column names
echo $row->title; // prints the title of the blog post
?>
15. Row Data Gateway
⢠Class for manipulating a single row
⢠Separates ďŹnding/searching from
manipulation
⢠Database access logic only
16. An example
<?php
// create row using table object
$table = new BlogPostTable();
$newRow = $table->createRow();
// set values
$newRow->title = âNew Blog Postâ;
$newRow->body = âLorem ipsum....â;
// save the row to the table
$newRow->save();
?>
17. Active Record
⢠Similar to Row Data Gateway
⢠Classes inherit from a âRecordâ class
⢠Classes may also implement âbehavioursâ
⢠Some mixing of data storage code and
domain logic
18. An example
<?php
class User extends Doctrine_Record {
public function setTableDeďŹnition() {
$this->hasColumn('username', 'string', 255);
$this->hasColumn('password', 'string', 255);
}
public function authenticate($username, $password) {
return ($this->username == $username) && ($this->password == $password);
}
}
$table = Doctrine::getTable(âUserâ);
$user = $table->ďŹnd($id);
if ($user->authenticate($username, $password)) {
print âAuthenticated as user id {$id}â;
}
19. Data Mapper
⢠Takes your object model and saves it to
database
⢠Mapper is external to your classes
⢠Good separation of concerns, keeps the
OO away from the data
⢠Hard to implement as a âdrop-inâ system
20. Extras
⢠Schema management
⢠Auto-creation of forms, admin areas
⢠Database creation and migration
⢠Plugins and behaviours
23. Performance Issues
⢠Joins
Complex relationships between objects can
lead to inefďŹcient joins between tables
⢠âHydrationâ
Transforming database rows into full PHP
objects can be expensive
⢠Many classes, many ďŹles
Loading many auto-generated classes can
cause performance issues
25. Object and Document
Databases
⢠Persevere
Stores JavaScript/JSON objects
⢠CouchDB
Stores documents in JSON format
⢠Abdera
Stores documents using the Atom
Publishing Protocol
28. Conclusions
⢠Choose your ORM library carefully
⢠Experiment
⢠Donât over-complicate
⢠Maybe ORM is the wrong choice anyway?
⢠Alternatives are starting to become viable