phactory
TRANSCRIPT
© 2009 Vertive, Inc.
Proprietary and Confidential
A Database Factory for PHP
http://phactory.org
Chris Kite
Sr. Web Engineer at Offers.com
@chriskite
© 2009 Vertive, Inc.
Proprietary and Confidential
Who are we?
Chris Kite
» B.S. in Computer Science from UT Austin
» Senior Web Engineer at Offers.com
» Creator of Phactory
» Likes martial arts, sushi
Josh Butts
» B.A. in Computer Science from UT Austin
» Director of Web Development at Offers.com
» Likes fast cars, plain burgers
© 2009 Vertive, Inc.
Proprietary and Confidential
Phactory: What is it?
Alternative to database fixtures for unit
tests
Define and create objects in code
Provides a lightweight ORM
Works with MySQL, SQLite, and
MongoDB
Phactory is developed and used at
Offers.com
© 2009 Vertive, Inc.
Proprietary and Confidential
Databases in Unit Testing
© 2009 Vertive, Inc.
Proprietary and Confidential
Database Fixtures
A fixture is a statically defined set of data
Each test can have its own dataset
PHPUnit provides support for loading
fixtures:class DatabaseTest extends PHPUnit_Extensions_Database_TestCase{
protected function getConnection(){
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', '');return $this->createDefaultDBConnection($pdo, 'testdb');
}
protected function getDataSet(){
return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/bank-account-seed.xml');}
}
© 2009 Vertive, Inc.
Proprietary and Confidential
Database Fixtures
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<post
post_id="1"
title="My First Post"
date_created="2008-12-01 12:30:29"
contents="This is my first post" rating="5"
/>
<post
post_id="2"
title="My Second Post"
date_created="2008-12-04 15:35:25"
contents="This is my second post"
/>
</dataset>
Example from http://www.phpunit.de/manual/current/en/database.html
© 2009 Vertive, Inc.
Proprietary and Confidential
Database Fixtures
Drawbacks:
» Typically in an unfriendly format like XML
» You are left to your own devices to retrieve
and manipulate data in your test
» Can’t dynamically create objects
» No concept of associations
© 2009 Vertive, Inc.
Proprietary and Confidential
Database Factories
Define database test data in code, not flat
files
Create objects dynamically, rather than
loading them all at once
Define associations between objects
Can integrate with or provide ORM
functionality
© 2009 Vertive, Inc.
Proprietary and Confidential
Database Factories
<? Phactory::setConnection(new PDO('sqlite:test.db')); Phactory::define('user', array('name' => 'Test User', 'email' => '[email protected]')); $user = Phactory::create('user'); // creates a row in the 'users' table print("Hello, {$user->name}!"); // prints "Hello, Test User!"
Phactory Example
© 2009 Vertive, Inc.
Proprietary and Confidential
Using Phactory
© 2009 Vertive, Inc.
Proprietary and Confidential
Connecting to the Database
Phactory supports MySQL, SQLite, and
MongoDB
Uses PDO for SQL databases
<? require_once 'Phactory/lib/Phactory.php'; $pdo = new PDO('mysql:host=127.0.0.1; dbname=testdb', 'user', 'password'); Phactory::setConnection($pdo);
© 2009 Vertive, Inc.
Proprietary and Confidential
Defining Table Blueprints
A blueprint defines default values for
objects created in a particular table
<? Phactory::define('user', array('name' => 'test_user', 'age' => 20)); Phactory::define('post', array('text' => 'This is a post', 'created_at' => 1296663323));
© 2009 Vertive, Inc.
Proprietary and Confidential
Creating Objects
When creating an object with Phactory,
you can:
» Specify values for fields that weren’t in the
blueprint definition
» Override any of the default values
» Associate with other objects
© 2009 Vertive, Inc.
Proprietary and Confidential
Creating Objects
Phactory::define('user', array('name' => 'test_user', 'age' => 20)); $john = Phactory::create('user', array('name' => 'John Doe')); $joe = Phactory::create('user', array('name' => 'Joe Smith', 'activated' => 1)); $anon = Phactory::create('user'); // $john looks like this: $john->id == 1; $john->name == 'John Doe'; $john->activated == 0; // $joe looks like this: $joe->id == 2; $joe->name == 'Joe Smith'; $joe->activated == 1; // $anon looks like this: $anon->id == 3; $anon->name == 'test_user'; $anon->activated == 0;
© 2009 Vertive, Inc.
Proprietary and Confidential
Retrieving Objects
Basic ORM functionality allows you to
retrieve Phactory objects from the
database
Uses a simple get() method which takes
an array of columns to match
$joe = Phactory::get('user', array('id' => 2)); $joe = Phactory::get('user', array('age' => 20, 'activated' => 1));
© 2009 Vertive, Inc.
Proprietary and Confidential
Associations
For SQL databases, Phactory provides
many-to-one and many-to-many
associations
Foreign key columns and join tables are
filled in automatically when creating an
object with associations
© 2009 Vertive, Inc.
Proprietary and Confidential
Associations
Phactory::define('author'); Phactory::define('book', array('name' => 'Test Book'), array('primary_author' => Phactory::manyToOne('author'))); $twain = Phactory::create('author', array('name' => 'Mark Twain')); $book = Phactory::createWithAssociations('book', array('primary_author' => $twain)); $book->author_id == $twain->getId();
© 2009 Vertive, Inc.
Proprietary and Confidential
Sequences
Define blueprints with automatically
incrementing values
» Include ‘$n’ in the definition
» Sequence starts at 0, and is incremented
each time an object is created
Phactory::define('user', array('name' => 'user$n', 'age' => '$n')); $user0 = Phactory::create('user'); // name == 'user0', age == '0' $user1 = Phactory::create('user'); // name == 'user1', age == '1' $user2 = Phactory::create('user'); // name == 'user2', age == '2'
© 2009 Vertive, Inc.
Proprietary and Confidential
Phactory and PHPUnit
© 2009 Vertive, Inc.
Proprietary and Confidential
Basic Test Setuprequire_once 'Phactory/lib/Phactory.php';
class ExampleTest extends PHPUnit_Framework_TestCase
{ public static function setUpBeforeClass()
{ // create a db connection and tell Phactory to use it
$pdo = new PDO("sqlite:test.db");
Phactory::setConnection($pdo);
// reset any existing blueprints and empty any tables Phactory has used Phactory::reset();
// define default values for each user we will create Phactory::define('user', array('name' => 'Test User $n', 'age' => 18));
}
public static function tearDownAfterClass() {
Phactory::reset();
}
public function tearDown() {
// delete all objects from defined tables, but do not erase blueprints
Phactory::recall(); }
© 2009 Vertive, Inc.
Proprietary and Confidential
Structuring Shared Definitions
Usually many of the test classes in your
suite will need to use the same tables
Define blueprints in one place, share them
among many tests
» Can include() a file of definitions, put
definitions in a static class, etc.
© 2009 Vertive, Inc.
Proprietary and Confidential
Structuring Shared Definitions
<? Phactory::define('tag', array('name' => 'Tag $n')); Phactory::define('post', array('name' => 'Test Blog Post', 'content' => 'Test Content'), array('tag' => Phactory::manyToMany('tag', 'posts_tags')));
SharedDefinitions.php
class ExampleTest extends PHPUnit_Framework_TestCase { public static function setUpBeforeClass() { $pdo = new PDO("sqlite:test.db"); Phactory::setConnection($pdo); Phactory::reset(); // include definitions common to several tests include('SharedDefinitions.php'); } }
ExampleTest.php
© 2009 Vertive, Inc.
Proprietary and Confidential
Dynamic Objects
Recall that with fixtures, your test data is
all loaded at the start of the test
With Phactory, you can create or change
test data during a test
© 2009 Vertive, Inc.
Proprietary and Confidential
Dynamic Objects
Phactory::define('tag', array('name' => 'Tag $n'), array('post' => Phactory::manyToMany('post', 'posts_tags'))); Phactory::define('post', array('name' => 'Test Blog Post', 'content' => 'Test Content'), array('tag' => Phactory::manyToMany('tag', 'posts_tags'))); $post = Phactory::create('post'); $this->assertEquals(0, MyPostClass::countTagsForPost($post->getId())); $tag = Phactory::createWithAssociations('tag', array('post' => $post)); $this->assertEquals(1, MyPostClass::countTagsForPost($post->getId()));
class MyPostClass { public static function countTagsForPost($post_id) { $stmt = $pdo->prepare("SELECT COUNT(*) AS num_tags FROM `posts_tags` WHERE `post_id` = ?"); $stmt->execute(array($post_id)); $result = $stmt->fetch(); return $result['num_tags']; } }
© 2009 Vertive, Inc.
Proprietary and Confidential
Using Phactory with MongoDB
require_once 'Phactory/lib/PhactoryMongo.php';
Uses the Mongo PHP driver
Almost exactly the same as regular SQL
Phactory, except:
» Documents returned as arrays rather than
Phactory_Row objects
» Phactory::get() supports all Mongo queries
» Associations: embedsOne and embedsMany
http://phactory.org/mongodb-guide/
© 2009 Vertive, Inc.
Proprietary and Confidential
QUESTIONS
© 2009 Vertive, Inc.
Proprietary and Confidential
We're Hiring!
Offers.com is hiring PHP Developers and
Software Engineers in Austin, TX
http://www.vertive.com/careers
© 2009 Vertive, Inc.
Proprietary and Confidential
Thank You!
These slides are online at
http://slidesha.re/phactory
Contact us on Twitter: @chriskite and
@jimbojsb
Visit http://phactory.org to download and
for more information