Tutorial

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 -

  1. An author can write more than one book.
  2. 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!

1) In case the script you run is in a different directory from the one your config.xml is in, you need to use function epLoadConfig() to load your config.xml after including ezpdo_runtime.php, for example,

include_once('/path/to/ezpdo/ezpdo_runtime.php');
epLoadConfig('/path/to/your/config.ini'); // .ini
//epLoadConfig('/path/to/your/config.xml'); // .xml

2) To learn more on how EZPDO handles inheritance mapping, please refer to Inheritance. Persistence for more complex inheritance can also be found in the composite example under /examples/composite in the package. The example implements the Composite design pattern, in which each object can have a parent object and a set of child objects.
3) Please note that if an object is used as an example object to find other objects, it’s instantly marked as an uncommitable object. you need to explicitly reset the flag,

$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.

27 user comments

  1. EZPDO: An O/R Mapping and Persistence Solution for PHP5 on April 25th, 2005:

    […] know how it’s done. Please take a look at object relationships and also refer to the book-author example. Post your comments Name (required) Mail […]

  2. TRB on June 27th, 2005:

    After running these tests, I was inspecting the _ezpdo_relation_ table and noticed that there did not seem to be anything mapping John Vlissides to any books, but he was listed as an author to Design Patterns.

    The problem lies in this bit of code.
    // add authors to books (two-way M:N associations between authors and books)
    $a1->books = array($b1, $b2);
    $a2->books = array($b1, $b3);
    $a3->books = array($b1, $b4);
    $a3->books = array($b1, $b5);

    The last line should be

    $a4->books = array($b1, $b5)

    This might cause some confusion to some future developer following this tutorial. So far this project looks great, I’m going to try and use it to build my next project.

    Cheers,

    TRB

  3. ezpdo4php on June 27th, 2005:

    Great catch! Will be fixed in the next nightly. Thank you.

  4. TRB on July 11th, 2005:

    You need to create an example object for the search using the same API method create(), then you call find() with the example object.

    I was looking to create and persist the user if there was not already a record in the UserCustomer table with the same email address.

    From the source file:

    /**
    * if an object is used as an example object to find other objects,
    * it’s instantly marked as a uncommitable object. you need to
    * explicitly unset the flag for it to be committable.
    * especially when you choose to auto-flush all uncommited objects
    * before quiting the script, make sure you have flagged all of them
    * committable. or you have to commit them explicitly.
    */

    $customer = $m->create(’UserCustomer’);
    $customer->emailaddress = “jim@somesite.com”;
    if ($rs = $m->find($customer) {
    echo “Some Error Message/Throw Exception\n”;
    } else {
    echo “Creating Customer with email “.$customer->emailaddress.”\n”;
    $customer->firstname = “Jim”;
    $customer->lastname = “Smith”;
    $customer->company = “Jim Smith, Inc.”;
    $customer->login = “jimsmith”;
    $customer->password = “jimsmith”;
    }

    I was expecting the example object passed to find($o) to be available to my script in the else statement, if the result of find($o) was false, meaning that no record was found with my criteria.

    I found the explanation to this when I read the excellent documentation in the source file, but in this example or in the API docs (five methods) a little more explanation might save another “user” like myself from having the same trouble I did and make ezpdo a bit easier to use.

    I added these two lines in the else block just below the echo and it worked as expected.

    $customer = $m->create(’UserCustomer’);
    $customer->emailaddress = “jim@somesite.com”;

  5. TRB on July 11th, 2005:

    or you have to commit them explicitly.

    One other note, I don’t think calling $m->commit($customer) with auto_flush=true or auto_flush=false on these example objects worked either.

    Cheers,

    TRB

  6. ezpdo4php on July 12th, 2005:

    trb, thanks for your comments. we have added a footnote in the above tutorial. hope it helps.

    p.s. it is true that if an object is used as an example object to find others, it’s marked as uncommittable and unless it’s reset to commmittable, it cannot be committed no matter what mode it is in. correction has been made in the comments.

  7. xfactorx on July 21st, 2005:

    Hello,
    Am currently looking at the examples and I was wondering, if I have about hundred tables in my db, how do I handle such amount of tables…

    Thanks

  8. ezpdo4php on July 21st, 2005:

    Am currently looking at the examples and I was wondering, if I have about hundred tables in my db, how do I handle such amount of tables…

    Do you mean how you can migrate those tables into EZPDO? So far we don’t have a script to automate the process, but if there is such a need and the requirements can be made clear and generic, you can put it into our bug tracking system. Also you can post your question in the user forums to see if anyone out there has done such a thing.

  9. EZPDO: An O/R Mapping and Persistence Solution for PHP5 on July 26th, 2005:

    […] Some improvements are also made in the user manual, tutorial and online API documents. Please alway post your comments to help us improve those documents. Thank you. […]

  10. Nick on November 19th, 2005:

    Typos:

    There are two occurrences of “inheritence”. Should be “inheritance”.

    // add authors to books
    $a1->books = array($b1, $b2);
    $a2->books = array($b1, $b3);
    $a3->books = array($b1, $b4);
    $a4->books = array($b1, $b5);

    Should be “add books to authors”.

    // add books to authors
    $b1->authors[] = $a1;
    $b1->authors[] = $a2;
    $b1->authors[] = $a3;
    $b1->authors[] = $a4;

    Should be “add authors to books”.

    Now let us create the authors.

    Should be “Now let us create the books”.

  11. ezpdo4php on November 19th, 2005:

    nick, thanks for letting us know the typos. now all corrected.

  12. Raymond Irving on December 22nd, 2005:

    Just a few questions:

    1) Can I use EZPDO with an existing database?

    2) Can I connect to multiple databases at the same time? In other words can I have an application that connects to a MySQL database and an SQLite database at the sametime? If not then I think this is a necessary feature to have as some developers would love to be able to do this by creating multiple instances of the manager.

  13. ezpdo4php on December 22nd, 2005:

    Answer to 1): Yes but with some limitations as column types in some dbs may not be supported by EZPDO. See this manual page for more info.

    Answer to 2): Yes. Two ways:

    1. O/R mapping in EZPDO can be done on class level. The following example shows how you can map one class to mysql and the other to sqlite:

    // @orm mysql://dbuser:secret@localhost/db4a
    class A {
    // ……
    }

    // @orm sqlite://db4b.db
    class B {
    // ……
    }

    2. you can also have two different config files, one points to mysql and the other points to sqlite by setting the ‘default_dsn’ options. use the global method epLoadConfig() to load the config file you desire.

    currently the persistence manager is a singleton meaning there is only one instance. for most cases it is sufficient. but this can be easily relaxed.

  14. Raymond Irving on December 23rd, 2005:

    Hi,

    Thnaks for the information.

    How about making ezpdo so that the create() function can accept an opional DSN parameter?

    // get books from sqlite
    $m->create(’Books’,’sqlite://db4b.db’);

    // get contacts from mysql
    $m->create(’Contacts’,'mysql://dbuser:secret@localhost/db4a’);

  15. Raymond Irving on December 23rd, 2005:

    Re: Existing database

    Do you have an example of how this can be done? I’ve notices that ezpdo tbales does not create foriegn keys relationships inside the db between two related tables

  16. Alex on January 30th, 2006:

    I am getting a Fatal Error “Uncaught exception ‘epExceptionDbAdodb’” saying that the table was not found when running the “find.php” script from the example. It seems to have something to do with the auto table prefixing of ezpdo. When executing the “‘from Book where title like ?’” EZQQL query it raises the exception. Bug?

  17. ezpdo4php on January 30th, 2006:

    This has been fixed. Please try the latest nightly.

  18. ppmm2004 on February 1st, 2006:

    Hello everyone.

    I’ve just noticed a problem. I can’t find the SQL instruction “update aTable set anAttribute = newValue” in object query languages, why?

    I think that I have read out every details in this web site, but I haven’t found it.

  19. ezpdo4php on February 1st, 2006:

    ppmm2004, if you have objects you’d want to alter its attributes (vars), you simply make assignments to the vars and then commit. changes are then persisted into db. example,

    $o->var1 = 'new value';
    $o->commit();

  20. Nicolas Gieczewski on February 14th, 2006:

    or delete all objects of a class

    $m->delete('Book');

    This doesn’t work; it should be $m->deleteAll('Book');

  21. ezpdo4php on February 14th, 2006:

    nicolas, thanks for pointing that out. now corrected.

  22. john doo the noob on February 15th, 2006:

    1)I am wondering what is oid aka e_oid in config.(xml|ini) what is that field ?
    2)Should i add e_oid field to my my tables in the database ?

    NOTE:i am have all the primary keys set to auto_increment (mysqld).


    mysql> SELECT album_id,artist_id,name,lastmod FROM sk_albums LIMIT 2;
    +----------+-----------+----------------------+---------------------+
    | album_id | artist_id | name | lastmod |
    +----------+-----------+----------------------+---------------------+
    | 1 | 1 | Antichrist Superstar | 2006-01-04 15:47:10 |
    | 2 | 1 | Demos & Remixes | 2006-01-04 15:47:10 |
    +----------+-----------+----------------------+---------------------+

    test.php source code:

    $a=$m->create('sk_albums');
    $a->album_id=1;
    $test=$m->find($a);
    print_r($test);

    Produce:


    Fatal error:
    Uncaught exception 'epExceptionDbAdodb' with message 'Column [e_oid] not found' in 285

    [omitted]

  23. ezpdo4php on February 15th, 2006:

    if you have an existing db that you want to use ezpdo to access, you can set the oid column by option ‘default_oid_column’. see this page for more info.

    in your case, say ‘album_id’ is the primary key and auto_incremental, then in your config file (.ini), you can write

    default_oid_column = album_id

    or if you use .xml config,

    <default_oid_column>album_id</default_oid_column>

  24. André de Castro Zorzo on March 18th, 2006:

    Hi,

    I cannot find how to perform an update in Author class?

  25. Ruby on July 5th, 2006:

    I wonder where to change the default associate table name “prefix_relation_author_book” to ,say,”author_book”,and it could be better if I could change the fields name like e_oid,oid_a,oid_b in the prefix_ez_relation_author_book table.

    Cheers,
    Ruby!

  26. ezpdo4php on July 5th, 2006:

    at the moment, except the prefix part, a relationship table name is unchangeable. so are its column names. if you’d like, you can write up a feature request in our bug tracking system. we may be able to provide options for users to change them. thanks.

  27. slawdan on June 10th, 2007:

    Wonderful ! ezpdo is such a pretty framework i’ve ever seen , and plans to use it with zf 1.0 to enhance it.

    But there is a question , if i can filter the result objects like objects->filter(”name like ‘%slaw%’”) or $objects->filter(”pages > 320″) . I’ve known that ezoql can do this , just expect a more OO method.

    I’m glad to recieve your reply , no matter your mail or reply here.

    cheers,
    slawdan from China

Post your comments

XHTML: tags you can use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Couldn't find your convert utility. Check that you have ImageMagick installed.