Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Object Relational Mapping in PHP
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 define 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; // floating-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 identifier
}
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 inflexible
• 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 finding/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 setTableDefinition() {
$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->find($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 inefficient joins between tables
• ‘Hydration’
Transforming database rows into full PHP
objects can be expensive
• Many classes, many files
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