Lithium PHP model -> model relations

1.0.0 2012-11-16 16:27 UTC


Now, don't get too extatic, this is in its early stages and only supports READ opperations & hasMany or hasOne. Hope to add support for full CRUD in the future.

Simply, this is a plugin designed to add support to li3 for relations on a Model to Model basis seperate from connections. This allows not only relationship support for non supportted DBs, such as MongoDB, but also allows relationships to be cross connection/data sources. Such as Solr backed model to a Mongo backed Model. If you wish any given relation to use lithiums default relation bindings, just pass in default => false as an option in the relation (detailed below).

It is likely that this type of Model -> Model support will never exist inside of lithium directly. However, with the powerful data sources lithium provides, this plugin becomes essential.


Use Composer

Modify your projects composer.json file

    "require": {
        "brandonwestcott/li3_relations": "master"

Run ./composer.phar install which will install this librarie into your app/libraries

Alternately, just clone, download or submodule

  1. Clone/Download/submodule the plugin into your app's libraries directory.
  2. Tell your app to load the plugin by adding the following to your app's config/bootstrap/libraries.php:


Add the plugin in your config/bootstrap/libraries.php file:


Then in any model, extend \li3_relations\extensions\data\Model.php

namespace app\models;

class AppModel extends \li3_relations\extensions\data\Model {

Defining Relations

Relations are defined in the lithium specified way as described here

In hasMany

class Team extends  \li3_relations\extensions\data\Model.php {

	public $hasMany = array(
		'Players' => array(
			'to'        => 'Players',
			'key'       => array('player_ids' => '_id'),
			'fieldName' => 'players',
			'with' 		=> array(
		'Coaches' => array(
			'to'     => 'Coaches',
			'key'       => array('coach_ids' => '_id'),
			'fieldName' => 'coaches',

	public $hasOne = array(
		'HeadCoach' => array(
			'to'     => 'Coaches',
			'key'       => array('coach_ids' => '_id'),
			'fieldName' => 'head_coach',
			'conditions' => array(
				'coach_level' => 'head'

Key specified is the name used to refernce the relation on a find query.

Options are: to - specifieds target model
fieldName - field to create relation on - defaults to camelCased Name
fields - fields to pass to query
order - order to pass to query
conditions - conditions to pass to query
with - relations for the relation
default - set to true to use default lithium relations (aka for two MySql models), defaults to false

Calling Relations

Relations are called in the lithium specified way as described here

Team::find('first', array(
	'_id' => 1,
	'with' => array(

In the case of using MongoDB, this would return a Document of a Team. On the team would exists 3 additional properties, players, coaches, and head_coaches. (Debating to make the reference live in a magic method vs a property - any input is welcome, aka ->players())

hasOnes will be set as type Entity as specified by their source (MongoDB creates Document, SQL creates Record).

hasManies will be set as type Set as specified by their source (MongoDB creates DocumentSet, SQL creates RecordSet).

However, when no data is returned, the behavior is slightly different. An empty hasOne will return null, as is the behavior of calling Model::find('first'). An empty hasMany will continue to return an empty Set, as is the behavior of calling Model::find('all').

Notice here that each of the Players on the Team would also include and Agent on that Document since the Players relation was specified with a "with". The Agent relationship would have been specified inside the Players model.

Modifying Relations

In some cases you may wish to modify relations on a per use basis. In these cases you can call updateRelation, find, then resetRelations.

Team::updateRelation('hasMany', array(
	'Players' => array(
		'conditions' => array('age' => 30),

$team = Team::find('first', array(
	'with' => array(


In this case, $team->players would only be populated with players whose age is 30.

Some Notes

  1. Beta Beta Beta - Currently, this plugin is being used heavily in a read MongoDB & Solr production environment. However, writes will likely majorly screw up your db. Use with caution.
  2. For the SQL lovers, I've attempted to have the plugin check to see if the current model and related model both are of the same source and support relations. (aka two MySQL backed models will continue to do a single query with a Left Join). I believe the bug exists in the binding the filter multiple times onto a connection. This bug was fixed in master and I hope to fix this soon in the default-relations branch.

Plans for the future

Need to get full CRUD on these relationships. Eventually I hope the li3 community sees this as a necessity and the features will be added into the core.


Please fork and contribute!