So you have followed the simple example and you want to do more with EZPDO.
In this tutorial, we are going to use a simple book-author example to show you some more features EZPDO has, especially its capability of handling object relationships.
Get ready
Download and install
The example in this tutorial is included in the EZPDO package. If you have not installed the package, grab the latest release and install now.
Once you have installed the package, go to the examples/book directory.
cd /path/to/install/ezpdo cd examples/books
The config file
The directory structure for this example looks like this.
+-- books
|
+-- classes
|
+-- compiled
We put all our class files under directory classes and compiled information under compiled. You need to make sure that the compiled directory is writable to the user who runs the scirpts. In the case of SQLite, you also need to make sure the directory to hold your database is writeable (books in this example).
Please note that this example is configured to use SQLite (which, by default, is a built-in extension in PHP 5) and should be able to run without any customization (except that you need to make sure the directories, books and books/compiled are writable to the user who runs the scripts).
All customization can be done in the EZPDO config file.
Let us make sure we have set up our configuration right. We will need to have an EZPDO config file. It normally sits in the directory the scripts live (which is also the default directory that EZPDO will look for the config file), but it’s totally up to you - you can put it anywhere you’d like. If this is the case, make sure you load it explicitly1).
The configuration file for the tutorial looks like this.
One thing worth mentioning is that the auto_flush and auto_compile options are enabled in the config file.
- auto_flush: EZPDO saves all objects it creates before quitting the script (except example objects used in query).
- auto_compile: EZPDO compiles a new class for mapping information on the fly when you persist its objects for the first time.
For this tutorial, unless you want to customize, you can safely skip database setup and go directly to the data model.
Database setup
In case you want your objects to be stored in a different database than the pre-configured one, you only need to change the default_dsn tag in the config file.
Suppose the DSN to the database you like to use is
mysql://username:password@localhost/mydb
Edit config.xml and put the above DSN between <default_dsn> and </default_dsn>.
<default_dsn>mysql://username:password@localhost/mydb</default_dsn>
Once this is done, EZPDO will generate tables for you automatically in the database you specified. If you would like to set up your database from scratch and do more customization, check out this guide.
The data model
In this example, we have two classes, Author and Book. The relationship -
- An author can write more than one book.
- A book can have more than one authors.
So this is an M:N (many-to-many) association. As described in Object relationships, EZPDO handles object relationship automatically, which you will see below.
The Base class
The classes Author and Book are based off class Base. The Base class does not do anything other than providing a tracking id, which could be used by, say, a bookstore, to easily track books and authors. Note that the var $trackId is simply a regular var that will be persisted in the database. Please don’t confuse it with the object identifier (OID) that EZPDO maintains internally. Using this superclass for Author and Book, we want to show you how EZPDO handles inheritance2).
class Base { /** * tracking id (used by bookstore) * @var string * @orm char(64) */ public $trackId; /** * Constructor */ public function __construct() { $this->trackId = uniqid('track-'); } }
The @orm tag says variable $trackId is mapped to a 64-char column.
The Author class
An author has a name ($name) and a collection (array) of books ($books) s/he has written.
class Author extends Base { /** * Name of the author * @var string * @orm char(64) */ public $name; /** * Books written by the author * @var array of Book * @orm has many Book */ public $books = array(); /** * Constructor * @param string $name author name */ public function __construct($name = '') { parent::__construct(); $this->name = $name; } // the rest of the code in the class omitted... }
Notice the @orm tag above for the variable $books. It uses
@orm has many Book
which indicates that the variable is a relationship field, of which its associated class is Book and it can have many Book objects.
The Book class
A book has a title ($title) and page number. A book can have more than one authors.
class Book extends Base { /** * Bool title * @var string * @orm title char(80) */ public $title; /** * Number of pages * @var integer * @orm integer */ public $pages = -1; /** * Book author (assuming many co-authors) * @var Author * @orm has many Author */ public $authors = array(); /** * Constructor * @param string */ public function __construct($title = '') { parent::__construct(); $this->title = $title; } // the rest of the code in the class omitted... }
Notice above the @orm tag for the variable $authors. It uses
@orm has many Author
which indicates that the variable is a relationship field, of which its associated class is Author and it can have many Author objects.
Create objects (and associations)
This following snippets show you how to add authors and books using EZPDO. Check out the source file examples/books/add.php.
Include runtime API
You need to include the EZPDO runtime API and get the persistence manager before you persist any objects.
// need EZPDO runtime API include_once(dirname(__FILE__) . '/../../ezpdo_runtime.php'); // load config.xml (optional: not needed if in currrent dir) //epLoadConfig('/path/to/your/config.xml'); // get the persistence manager (a singleton) $m = epManager::instance();
Create authors
Now let us call the manager to work to create authors.
// create authors $a1 = $m->create('Author'); $a1->name = 'Erich Gamma'; $a2 = $m->create('Author'); $a2->name = 'Richard Helm'; $a3 = $m->create('Author'); $a3->name = 'Ralph Johnson'; $a4 = $m->create('Author'); $a4->name = 'John Vlissides';
Just in case you are a getter/setter lover, you can always do
// setter $a1->setName('Erich Gamma'); // getter echo $a1->getName();
You don’t have to define them in the class. The getter/setter methods all come free. The EZPDO core is responsible for setting them up.
You can also access an EZPDO object as an array and its variables as array keys. (You think I am crazy, don’t you? :) Example,
$a1['name'] = 'Erich Gamma'; echo $a1['name'];
The magic is that all EZPDO objects have the ArrayAccess interface implemented.
Every road goes to Rome. You choose whichever way you like to play with your objects. :)
Create books
Now let us create the books.
// create books $b1 = $m->create('Book'); $b1->title = 'Design Patterns'; $b1->pages = 395; $b2 = $m->create('Book'); $b2->title = 'Contributing to Eclipse: Principles, Patterns, and Plugins'; $b2->pages = 320; // add more books... // ......
Associate authors and books
If you have come this far, you know what we are getting into. Object relationships!
With EZPDO, make associations among objects is as easy as assignement (
. What makes it even nicer is all these associations made in memory are persisted into your database automatically. You are also totally (and I mean totally! :) free from maintaining them in later updating or deleting, all of which are taken care of by the EZPDO core.
// add authors to books $b1->authors = array($a1, $a2, $a3, $a4); $b2->authors = array($a1); $b3->authors = array($a2); $b4->authors = array($a3); $b5->authors = array($a4); // add books to authors $a1->books = array($b1, $b2); $a2->books = array($b1, $b3); $a3->books = array($b1, $b4); $a4->books = array($b1, $b5);
Or you might prefer adding objects to a many-valued relationship field one by one, then you can do
// add authors to books $b1->authors[] = $a1; $b1->authors[] = $a2; $b1->authors[] = $a3; $b1->authors[] = $a4;
Simply array operations!
Commit and auto_flush
Since we have enabled auto_flush in config.xml, we don’t even need to commit. Upon quitting the script, all objects are commited to database.
In case you don’t want to use the auto_flush feature, you need to commit objects explicitly. You can either commit objects individually
$m->commit($b1);
or flush them all at once
$m->flush();
or flush only objects of a certain class
$m->flush('Book');
Once you have an EZPDO object, you can call commit() or delete() directly through the object without “bothering” the manager. For example,
// commit an object $o->commit(); // delete an object $o->delete();
Object operations
Check stored objects
Go to the example/books directory and type
php add.php
All the objects you have created will be stored in the database. Now it’s time to check stored objects.
In case you are curious about how EZPDO maps classes into database tables, you may take a look at the database yourself. You may use tools such as phpMyAdmin to help you visualize the database tables.
In this example, we have a script, print.php, to print out all objects stored. It reads all objects into memory and print them out. Code snippet:
// get the persistence manager $m = epManager::instance(); // get all authors and books $authors = $m->get('Author'); $books = $m->get('Book'); // loop through authors/books // ......
So type
php print.php
and you should see something like this:
author {
trackId: track-421dbcad4b12e
name: Erich Gamma
books:
1. Contributing to Eclipse: Principles, Patterns, and Plugins
2. Design Patterns
}
Find objects
Say you want to find all books written by Eric Gamma, do the following -
// get the persistence manager $m = epManager::instance(); // create the example object $ea = $m->create('Author'); // set name to search $ea->name = 'Erich Gamma'; // null variable is ignored in searching // !!!important if the class constructor set non-null values!!! $ea->trackId = null; $ea->books = null; // use the example object to find if (!($as = $m->find($ea))) { echo "Cannot find author [" . $ea->name . "]\n"; exit(); } // go through each author and print foreach($as as $a) { echo $a; echo "\n"; }
You need to create an example object for the search using the same API method create(), then you call find() with the example object3). Objects found are put into an array.
You can now also use EZOQL to do object query with more flexibility. The following code does the same thing you just did.
// ... // use EZOQL to find objects $as = $m->query("from Author as author where author.name = 'Erich Gamma'"); if (!$as) { echo "Cannot find author [Erich Gamma]\n"; exit(); } // ...
Delete objects
You can delete objects individually
$m->delete($o);
or delete all objects of a class
$m->deleteAll('Book');
Keep in mind that when you delete an object, all associations from and to this object are also removed from the storage. There is no (absolutely no!) need for you to maintain associations for consistency. EZPDO does it all.
For our example in this tutorial, we have a script delete.php in the directory. Type
php delete.php
and all books and authors along with their associations are deleted from the database. Type `php print.php` to check if objects are deleted.
What's next?
Congratulations! You have just finished the EZPDO quickstart tutorial! I am sure you have had enough clue and desire to explore more.
It is a good idea to take a moment to skim through the minimalistic runtime API first. And then copy an example directory and start to mess around with it.
You may also want to see what EZOQL (the mini object query language) can do for you. You will be surprised! :)
Don’t forget to help us improve this tutorial by posting your comments below. Use the forums to seek help from other EZPDO users and developers.
Have fun!
include_once('/path/to/ezpdo/ezpdo_runtime.php'); epLoadConfig('/path/to/your/config.ini'); // .ini //epLoadConfig('/path/to/your/config.xml'); // .xml
$eo->epSetCommittable(true);
for it to be committable. Especially when you choose to auto-flush all uncommited objects including the example objects before quiting the script, make sure you have flagged all of them committable.