xPDO

xPDO modx For developers

xPDO is an object-relational bridge built into MODX. Simply put, it is how MODX connects to the database and how it interacts with the various tables.

In MODX 2.x, the modX class directly extends xPDO. While this is not the best design pattern in hindsight, it means that whenever you have access to a modX instance, you can use any of the xPDO methods on it.

What is xPDO?

xPDO (open eXtensions to PDO) is a lightweight ORB (object-relational bridge) library running on PHP 5 that helps you take advantage of the latest database standard in PHP, the PDO (PHP Data Objects) extension. xPDO implements a very simple but powerful pattern for accessing Active Record data, and a flexible domain model that allows you to separate domain logic from database-specific logic when you need it.

But xPDO is a bit more than just a pattern implementation. It is also a way to abstract the business logic of an application from the actual SQL queries and prepared statements used to access the data in the database, and to easily describe and provide object model implementations for multiple target database platforms.

The goal of xPDO is to quickly provide a foundation for a web application that can be easily extended into a full-fledged object model that can be optimized as much as possible without platform dependencies.

Glossary

In the context of xPDO, it is important to know the following terms:

  • Packages – collections of models. In MODX core, all models are part of the modx package, plus there are a few subpackages like modx.media and modx.package. In order for xPDO to know about the models in a package, it needs to be registered with $xpdo->addPackage().
  • Models – classes that represent a specific database table. This is the abstraction you’ll use most often: instead of interacting with SQL directly, you load a model, set its properties, and save it.
  • Schemas – XML files that define the different models available in the package and their fields (properties). These are only used in development, where they will be processed (usually called “inlined”) in model classes and maps.
  • Maps – php files containing arrays that define metadata for packages and schemas. They are located in the database driver’s model directory (eg: model/modx/mysql/modresource.map.inc.php). These files are not usually processed manually, but are created from the schema file.

There are many more things to learn about xPDO, but if you understand these 4, you have a solid foundation to understand the rest of the documentation.

Example

You can learn more about the different ways to work with data in xPDO on the various pages. If you are more of a code nerd, the example below will show you the different interactions xPDO has.

if (!$modx->addPackage('education', '/path/to/model/')) {
   die('Can\'t load package, try again later.');
}

// Go to Harvard (or create a new school with the same name)
$school = $modx->getObject('School', ['name' => 'Harvard']);
if (!$school) {
    $school = $modx->newObject('School');
    $school->set('name', 'Harvard');
    $school->save();
}

// Find 100 students who are graduates and sort by last name
$c = $modx->newQuery('Student');
$c->where([
    'school' => $school->get('id'),
    'is_alumni' => true,
    'start_year' => $_GET['start_year'] ?? date('Y') - 5,
]);
$c->sortby('lastname', 'ASC');
$c->limit(100);

foreach ($modx->getIterator('Student', $c) as $student) {
    echo $student->get('firstname') . ' ' . $student->get('lastname') . ' started studying in ' . $student->get('start_year');

    if ($graduation = $student->getOne('Graduation')) {
        echo ' and graduated in ' . $graduation->get('year') . ".\n";
    }
    else {
        echo " and has not graduated.\n";
    }
}

Some notes on the code above:

  • This is purely hypothetical, there is no package/model code for you to use.
  • On line 6, we specify the conditions to load the School object as an array. You could also specify an integer to get the object by its primary key, provide an xPDOQuery, or provide raw SQL. Always be clear about the type of condition you are specifying; cast to an int if you are using a primary key (especially if it comes from user input), or provide array syntax.
  • On line 14, we create a new xPDOQuery instance for our Student model. This is the query builder. The variable name $c, short for condition, appears quite often in xPDOQuery instances. xPDOQuery can do conditions, joins, sorts and more. To debug the generated query, you can add $c->prepare(); echo $c->toSQL();
  • On line 18, we use the $_GET data without doing any cleanup. Luckily for us, xPDO uses prepared statements, so you are automatically protected from SQL injection when using the query Builder.
  • Lines 23, 26 and 29 use Echo to return data. You should never (rarely) do this in real code. Ideally, you should provide the data ($student->toArray()) to the template (e.g. chunk, with $modx->getChunk(), which is a modX method, not xPDO) to keep your data and markup separate.
  • Line 25 uses getOne() to get the related object. The relationship must be defined in the model. Instead of getOne, you could also access the relationship directly ($student->Graduation), which would be lazy loaded, or (assuming the Graduation model has a student field containing the student) you could use $modx->getObject('Graduation', ['student' => $student->get('id')]).

Take a look at the various subsections to learn more about specific aspects of xPDO.

Patterns are easy to understand

But xPDO is a bit more than a simple pattern implementation. It is also a way to abstract business objects from the actual SQL queries and prepared statements used to access the relational database structure that represents them, and a way to easily describe and provide optimized object model implementations for multiple target database platforms.

xPDO was developed using several design patterns that are well described in Martin Fowler’s book “Patterns of Enterprise Application Architecture”. These include but are not limited to:

  • Domain Model
  • Active Record
  • Data Mapper
  • Lazy Load
  • Identity Field
  • Single Table Inheritance
  • Metadata Mapping
  • Query Object

It will be very useful to be familiar with these patterns (and others from Fowler’s catalog) before programming with xPDO. Understanding these concepts will help you not only in learning xPDO, but in many other things related to programming.

Why It Was Created

xPDO was inspired by the need to quickly create a framework for a web application that could be easily extended into a full-blown object model that could be optimized as much as possible for the database platform it was deployed on, without introducing platform dependencies or maintenance nightmares. And it had to be as code-small as possible; implementing an efficient object-relational persistence framework in PHP requires this.

Rate article
MODX 3
Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.