1. Overview
      1. What It Is
      2. Why I Would Want to Use It
      3. Known Issues
    2. Getting Started
      1. Fundamentals
        1. Design Patterns
        2. xPDO, the Class
          1. As Object and Relational Mapper
          2. As PDO Wrapper
          3. As Service Layer
          4. The xPDO Constructor
            1. Hydrating Fields
      2. Introduction to the xPDO Object Model
        1. Understanding xPDOCriteria and xPDOQuery
      3. Creating a Model With xPDO
        1. Defining a Schema
          1. Defining Relationships
          2. Defining the Database and Tables
            1. Upgrading Models to Schema Version 1.1
          3. More Examples of xPDO XML Schema Files
          4. Validation Rules in your Schema
        2. Generating the Model Code
          1. Domain Classes
          2. O-R Maps
          3. Table Classes
      4. Using Your xPDO Model
        1. Creating Objects
        2. Database Connections and xPDO
        3. Loading Packages
        4. Removing Objects
          1. removeCollection
        5. Retrieving Objects
          1. getCollectionGraph
        6. Setting Object Fields
        7. Working with Related Objects
    3. Extending Your xPDO Model
      1. Overriding Derived Behavior
        1. Using Custom Object Loaders
      2. Working With Transient Object Classes
    4. Advanced Features
      1. Caching
        1. Caching Tutorial - Basic Snippets
        2. Caching Tutorial - Lifetimes
      2. Object Validation
        1. xPDOForeignKeyConstraint
        2. xPDOMaxLengthValidationRule
        3. xPDOMaxValueValidationRule
        4. xPDOMinLengthValidationRule
        5. xPDOMinValueValidationRule
        6. xPDOObjectExistsValidationRule
    5. xPDO Development
      1. Testing
      2. Building
      3. Documentation and Tutorials
    6. Class Reference
      1. xPDO
        1. xPDO.addPackage
        2. xPDO.beginTransaction
        3. xPDO.commit
        4. xPDO.connect
        5. xPDO.fromJSON
        6. xPDO.getCollection
        7. xPDO.getCollectionGraph
        8. xPDO.getCount
        9. xPDO.getDebug
        10. xPDO.getFields
        11. xPDO.getIterator
        12. xPDO.getManager
        13. xPDO.getObject
        14. xPDO.getObjectGraph
        15. xPDO.getOption
        16. xPDO.getTableName
        17. xPDO.loadClass
        18. xPDO.log
        19. xPDO.newObject
        20. xPDO.newQuery
        21. xPDO.query
        22. xPDO.setDebug
        23. xPDO.setLogLevel
        24. xPDO.setLogTarget
        25. xPDO.setOption
        26. xPDO.toJSON
      2. xPDOCacheManager
        1. xPDOCacheManager.copyFile
        2. xPDOCacheManager.copyTree
        3. xPDOCacheManager.delete
        4. xPDOCacheManager.deleteTree
        5. xPDOCacheManager.endsWith
        6. xPDOCacheManager.escapeSingleQuotes
        7. xPDOCacheManager.get
        8. xPDOCacheManager.getCachePath
        9. xPDOCacheManager.getCacheProvider
        10. xPDOCacheManager.matches
        11. xPDOCacheManager.replace
        12. xPDOCacheManager.set
        13. xPDOCacheManager.writeFile
        14. xPDOCacheManager.writeTree
      3. xPDOGenerator
        1. xPDOGenerator.parseSchema
        2. xPDOGenerator.writeSchema
      4. xPDOManager
        1. xPDOManager.createObjectContainer
        2. xPDOManager.createSourceContainer
        3. xPDOManager.removeObjectContainer
        4. xPDOManager.removeSourceContainer
      5. xPDOObject
        1. Configuration Accessors
          1. getOption
          2. setOption
        2. Field Accessors
          1. fromArray
          2. fromJSON
          3. get
          4. set
          5. toArray
          6. toJSON
        3. Metadata Accessors
          1. getFieldName
          2. getFKClass
          3. getFKDefinition
          4. getPK
          5. getPKType
          6. getSelectColumns
        4. Persistence Methods
          1. remove
          2. save
        5. Related Object Accessors
          1. addMany
          2. addOne
          3. getMany
          4. getOne
        6. State Accessors
          1. isDirty
          2. isLazy
          3. isNew
        7. Static Object Loaders
          1. _loadCollectionInstance
          2. _loadInstance
          3. _loadRows
          4. load
          5. loadCollection
          6. loadCollectionGraph
          7. Using Custom Loader Classes
        8. Validation
          1. addValidationRule
          2. getValidator
          3. isValidated
          4. removeValidationRules
          5. validate
      6. xPDOQuery
        1. xPDOQuery.andCondition
        2. xPDOQuery.groupby
        3. xPDOQuery.innerJoin
        4. xPDOQuery.leftJoin
        5. xPDOQuery.limit
        6. xPDOQuery.orCondition
        7. xPDOQuery.rightJoin
        8. xPDOQuery.select
        9. xPDOQuery.setClassAlias
        10. xPDOQuery.sortby
        11. xPDOQuery.where
      7. xPDORevisionControl
      8. xPDOTransport
        1. xPDOTransport.install
        2. xPDOTransport.pack
        3. xPDOTransport.uninstall
      9. xPDOValidator
        1. xPDOValidator.addMessage
        2. xPDOValidator.getMessages
        3. xPDOValidator.hasMessages
        4. xPDOValidator.validate

Retrieving Objects

Last edited by YJ Tso on Jul 2, 2014.

To get Objects in xPDO, there are a variety of methods. The two basic themes we'll concern ourselves with here is in the differences between an Object, a Collection, and an Iterator.

An Object is a single xPDOObject, nothing more, nothing less. It is retrieved via xPDO::getObject(). Think of it like one row in a database table.

A Collection is an array of xPDOObjects. They are retrieved via xPDO::getCollection. Think of it as a list of rows in a table.

An Iterator is a special kind of Collection which is accessed sequentially so that all of the rows and objects representing them are not loaded into memory all at once.

xPDO::getObject

getObject takes 3 arguments: $className, $criteria, and $cacheFlag. The first argument is the class name you'd like to retrieve; the second is the criteria by which you'd like to search for it; and the final argument is the caching option for the object.

If an integer value is provided for the $cacheFlag argument, then it specifies the time to live in the object cache; if cacheFlag === false, caching is ignored for the object and if cacheFlag === true, the object will live in the cache indefinitely.

Back to $criteria. This can be one of 3 things:

  • A primary key value
  • An array of field(s)
  • An xPDOCriteria object or derivative.

We'll get back to the third option later. First, an example of the first option:

$box23 = $xpdo->getObject('Box',23);

If an object doesn't exist, it will return 'null'.

You can specify multiple filter criteria inside your 2nd argument:

// GroupUser is a table with 2 PKs - 'user' and 'group'
$gu = $xpdo->getObject('GroupUser',array(
   'user' => 12,
   'group' => 4,
));

Or, let's say we wanted to grab the first Box object we find with a width of 150:

$bigbox = $xpdo->getObject('Box',array('width' => 150));
Handy Hint
If your criteria matches multiple objects, getObject will only return the first one. Which one will be first? It's up to the database and how its natural sort order.

We'll discuss the third option, xPDOCriteria, later in the xPDOQuery and xPDOCriteria sections.

xPDO::getCollection

xPDO::getCollection takes the same three arguments as getObject; except the $criteria field must be either an array or xPDOCriteria object.

Let's say we wanted to grab all the Box objects with width of 14:

// assume we have 3 boxes
$boxes = $xpdo->getCollection('Box',array(
  'width' => 14,
));
foreach ($boxes as $box) {
   echo $box->get('color')."\n";
}
// If our 3 boxes had a color of 'red','blue' and 'yellow', this would print:
// red
// blue
// yellow

xPDO::getIterator

The xPDO::getIterator method is identical to xPDO::getCollection except that you can only access one xPDOObject from the Collection of rows at one time. If you need access to all of the objects/rows at once, use getCollection. Otherwise, it is more efficient in terms of memory usage, to use getIterator to loop through a Collection of xPDOObjects.

The code for iterating over the Box objects with width of 14 is almost identical to that of getCollection:

// assume we have 3 boxes, with colors 'red','blue' and 'yellow'
$boxes = $xpdo->getIterator('Box', array(
  'width' => 14,
));
foreach ($boxes as $box) {
   echo $box->get('color')."\n";
}
// this would print:
// red
// blue
// yellow
Note that the index for each object when iterated over is not the primary key, unlike the array index when using getCollection.

xPDO::newQuery

One of the most powerful parts of xPDO is its ability to create complex queries in a simple fashion using the xPDOQuery wrapper. This class lets you build SQL queries using OOP methods that extend the xPDOCriteria class – you can pass its instance right into getObject or getCollection calls. The newQuery function creates an xPDOQuery object, and takes 3 parameters:

  • $class - The class name to create the query for.
  • $criteria - This is optional; but you can specify criteria here.
  • $cacheFlag - Similar to getObject's cacheFlag, you can specify the caching behavior for this query.

First, let's just show how you might use newQuery to define the criteria we used before: width = 14. We'll just add a sorting option to sort the results.

$c = $xpdo->newQuery('Box');
$c->where(array('width' => 14));
$c->sortby('name','ASC');
$boxes = $xpdo->getCollection('Box',$c);

Once you have your result, you can iterate over the array (see above). You can see the similarity between the defining a query object and passing a simple array to getObject or getCollection. So why use xPDOQuery? It's more flexible. Did you see how we could use it to specify the sorting order?

Next, let's use the query to join on a related table using xPDOQuery.innerJoin. Let's create an example query using xPDOQuery that will grab the first 5 Boxes with width of 5 and an owner of ID 2, sorted by their name. Our "Box" table has a many-to-many relationship with the "BoxOwner" table.

$c = $xpdo->newQuery('Box');
$c->innerJoin('BoxOwner','Owner'); // arguments are className, alias
$c->where(array(
   'width' => 5,
   'Owner.user' => 2,
));
$c->sortby('name','ASC');
$c->limit(5);
$boxes = $xpdo->getCollection('Box',$c);

We can join on 3rd table ("Owner") by using another call to xPDOQuery.innerJoin. Let's also grab the 2nd 5 Boxes by specifying an offset – it's a 2nd argument to the limit() function:

$c = $xpdo->newQuery('Box');
$c->innerJoin('BoxOwner','Owner'); // arguments are: className, alias
$c->innerJoin('User','User','Owner.user = User.id');
// note the 3rd argument that defines the relationship in the innerJoin
$c->where(array(
   'Box.width' => 5,
   'User.user' => 2,
));
$c->sortby('Box.name','ASC');
$c->limit(5,5); // limit, offset
$boxes = $xpdo->getCollection('Box',$c);

You can see that the sortby and where functions can take dot syntax on their parameters; they can prefix their columns with alias – sometimes they have to do this to prevent collisions!

More information on xPDOQuery can be found here.

The xml schema can be found in your MODX installation's core folder, here: model/schema/modx.mysql.schema.xml (helpful to get classNames, aliases, etc for your queries)

Debugging

Often it can be confusing to rely on an ORM layer, so you can force xPDO to print out the raw database queries.

$c = $xpdo->newQuery('Box');
// ... add some more criteria...
$c->prepare();
print $c->toSQL();

xPDOCriteria

One of the most powerful parts of xPDO is its ability to create complex queries in a simple fashion using the xPDOQuery wrapper. This class lets you build SQL queries using OOP methods that extend the xPDOCriteria class – you can pass its instance right into getObject or getCollection calls. The newQuery function creates an xPDOQuery object, and takes 3 parameters:

  • $class - The class name to create the query for.
  • $criteria - This is optional; but you can specify criteria here.
  • $cacheFlag - Similar to getObject's cacheFlag, you can specify the caching behavior for this query.

First, let's just show how you might use newQuery to define the criteria we used before: width = 14. We'll just add a sorting option to sort the results.

$c = $xpdo->newQuery('Box');
$c->where(array('width' => 14));
$c->sortby('name','ASC');
$boxes = $xpdo->getCollection('Box',$c);

Once you have your result, you can iterate over the array (see above). You can see the similarity between the defining a query object and passing a simple array to getObject or getCollection. So why use xPDOQuery? It's more flexible. Did you see how we could use it to specify the sorting order?

Next, let's use the query to join on a related table using xPDOQuery.innerJoin. Let's create an example query using xPDOQuery that will grab the first 5 Boxes with width of 5 and an owner of ID 2, sorted by their name. Our "Box" table has a many-to-many relationship with the "BoxOwner" table.

$c = $xpdo->newQuery('Box');
$c->innerJoin('BoxOwner','Owner'); // arguments are className, alias
$c->where(array(
   'width' => 5,
   'Owner.user' => 2,
));
$c->sortby('name','ASC');
$c->limit(5);
$boxes = $xpdo->getCollection('Box',$c);

We can join on 3rd table ("Owner") by using another call to xPDOQuery.innerJoin. Let's also grab the 2nd 5 Boxes by specifying an offset – it's a 2nd argument to the limit() function:

$c = $xpdo->newQuery('Box');
$c->innerJoin('BoxOwner','Owner'); // arguments are: className, alias
$c->innerJoin('User','User','Owner.user = User.id');
// note the 3rd argument that defines the relationship in the innerJoin
$c->where(array(
   'Box.width' => 5,
   'User.user' => 2,
));
$c->sortby('Box.name','ASC');
$c->limit(5,5); // limit, offset
$boxes = $xpdo->getCollection('Box',$c);

You can see that the sortby and where functions can take dot syntax on their parameters; they can prefix their columns with alias – sometimes they have to do this to prevent collisions!

More information on xPDOQuery can be found here.

See also xPDOQuery.where

Graphs

A graph extends the idea of an object (or objects in collections). Instead of a simple object, a graph includes references to related objects. Graphs are a useful alternative to JOINs.

xPDO::getObjectGraph

This is the same as getCollectionGraph, but it returns a single object. See getCollectionGraph below for info.

xPDO::getCollectionGraph

$collection= $xpdo->getCollectionGraph('Zip', '{"TZ":{},"ST":{},"CT":{}}');
if ($collection) {
        foreach ($collection as $obj) {
                $out = $obj->toArray();
                $out['timezone'] = $obj->TZ->get('tzname');
                $out['state'] = $obj->ST->get('statename');
                $out['county'] = $obj->CT->get('countyname');
                print_r($out);
        }
}
Aliases in JSON
Remember that the JSON hash passed to getObjectGraph or getCollectionGraph needs to use aliases, not class names.

You have direct access to all of the fields (table rows) in the Collection Graph comprised in these four tables. The alias is used to create the graph. In this example, the 'Zip' table is the primary table, so we look at that table and we define relationships from the perspective of that primary table.

As with getObject and getCollection, we can supply a $criteria object to getCollectionGraph. Let's add some criteria to our getCollectionGraph() query. In this example, we can search for zipcodes in California (CA)

$criteria = $modx->newQuery('Zip');
$criteria->where(array('ST.statename' => 'CA'));
$collection= $xpdo->getCollectionGraph('Zip', '{"TZ":{},"ST":{},"CT":{}}', $criteria);
if ($collection) {
        foreach ($collection as $obj) {
                $out = $obj->toArray();
                $out['timezone'] = $obj->TZ->get('tzname');
                $out['state'] = $obj->ST->get('statename');
                $out['county'] = $obj->CT->get('countyname');
                print_r($out);
        }
}
Aliases in Criteria
The table names you specify in your criteria must use the aliases, not the class names (just like the JSON hashes).

Let's show one more example, this time using MODX tables. This is only an example: filtering on Template Variables is a bit dangerous because the values stored in the database are not always the verbatim values you experience in the manager or in your templates. But this example should help demonstrate the usage of aliases and that you must be aware of the relationships between the objects (some related objects are singular, some are arrays).

$criteria = array();
$criteria['modResource.id:IN'] = array(1,2,3);
$criteria['TemplateVarResources.tmplvarid'] = 5;
$criteria = $modx->newQuery('modResource', $criteria);
$pages = $modx->getCollectionGraph('modResource', '{"TemplateVarResources":{"TemplateVar":{}}}', $criteria);
if ($pages) {
    foreach ($pages as $p) {
        print $p->get('pagetitle');
        foreach ($p->TemplateVarResources as $tvr) {
            $name = $tvr->TemplateVar->get('name');
            print $name . ' is '. $tvr->get('value');
        }
    }
}

Please view the dedicated page: getCollectionGraph

See Also

Suggest an edit to this page on GitHub (Requires GitHub account. Opens a new window/tab) or become an editor of the MODX Documentation.