Files
spa-api/system/db/eloquent/hydrator.php

262 lines
8.0 KiB
PHP
Raw Normal View History

2011-06-08 23:45:08 -05:00
<?php namespace System\DB\Eloquent;
2011-06-17 14:50:07 -07:00
use System\DB\Eloquent;
2011-06-14 17:27:11 -05:00
class Hydrator {
2011-06-08 23:45:08 -05:00
/**
* Load the array of hydrated models.
*
* @param object $eloquent
* @return array
*/
2011-06-14 17:27:11 -05:00
public static function hydrate($eloquent)
2011-06-08 23:45:08 -05:00
{
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Load the base / parent models from the query results.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
$results = static::base(get_class($eloquent), $eloquent->query->get());
// -----------------------------------------------------
// Load all of the eager relationships.
// -----------------------------------------------------
if (count($results) > 0)
{
foreach ($eloquent->includes as $include)
{
if ( ! method_exists($eloquent, $include))
{
throw new \Exception("Attempting to eager load [$include], but the relationship is not defined.");
}
2011-06-17 14:50:07 -07:00
static::eagerly($eloquent, $results, $include);
2011-06-08 23:45:08 -05:00
}
}
return $results;
}
/**
* Hydrate the base models for a query.
*
* @param string $class
2011-06-14 17:27:11 -05:00
* @param array $results
2011-06-08 23:45:08 -05:00
* @return array
*/
2011-06-14 17:27:11 -05:00
private static function base($class, $results)
2011-06-08 23:45:08 -05:00
{
2011-06-14 17:27:11 -05:00
$models = array();
2011-06-08 23:45:08 -05:00
2011-06-14 17:27:11 -05:00
foreach ($results as $result)
2011-06-08 23:45:08 -05:00
{
$model = new $class;
2011-06-08 23:45:08 -05:00
$model->attributes = (array) $result;
2011-06-14 17:27:11 -05:00
$model->exists = true;
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// The results are keyed by the ID on the record. This
// will allow us to conveniently match them to child
// models during eager loading.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-14 17:27:11 -05:00
$models[$model->id] = $model;
2011-06-08 23:45:08 -05:00
}
2011-06-14 17:27:11 -05:00
return $models;
2011-06-08 23:45:08 -05:00
}
/**
* Eagerly load a relationship.
*
* @param object $eloquent
2011-06-17 14:50:07 -07:00
* @param array $parents
2011-06-08 23:45:08 -05:00
* @param string $include
* @return void
*/
2011-06-17 14:50:07 -07:00
private static function eagerly($eloquent, &$parents, $include)
2011-06-08 23:45:08 -05:00
{
// -----------------------------------------------------
// Get the relationship Eloquent model.
//
2011-06-17 14:50:07 -07:00
// We temporarily spoof the "belongs_to" key to allow
// the query to be fetched without any problems.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
$eloquent->attributes[$spoof = $include.'_id'] = 0;
2011-06-17 14:50:07 -07:00
$relationship = $eloquent->$include();
2011-06-08 23:45:08 -05:00
unset($eloquent->attributes[$spoof]);
// -----------------------------------------------------
// Reset the WHERE clause and bindings on the query.
2011-06-17 14:50:07 -07:00
// We'll add our own WHERE clause soon.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
$relationship->query->where = 'WHERE 1 = 1';
$relationship->query->bindings = array();
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Initialize the relationship attribute on the parents.
// As expected, "many" relationships are initialized to
// an array and "one" relationships to null.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
foreach ($parents as &$parent)
2011-06-08 23:45:08 -05:00
{
2011-06-17 14:50:07 -07:00
$parent->ignore[$include] = (strpos($eloquent->relating, 'has_many') === 0) ? array() : null;
2011-06-08 23:45:08 -05:00
}
2011-06-14 17:27:11 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Eagerly load the relationships. Phew, almost there!
2011-06-14 17:27:11 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
if ($eloquent->relating == 'has_one')
2011-06-08 23:45:08 -05:00
{
2011-06-17 14:50:07 -07:00
static::eagerly_load_one($relationship, $parents, $eloquent->relating_key, $include);
}
elseif ($eloquent->relating == 'has_many')
{
static::eagerly_load_many($relationship, $parents, $eloquent->relating_key, $include);
2011-06-08 23:45:08 -05:00
}
elseif ($eloquent->relating == 'belongs_to')
{
2011-06-17 14:50:07 -07:00
static::eagerly_load_belonging($relationship, $parents, $eloquent->relating_key, $include);
2011-06-08 23:45:08 -05:00
}
else
{
2011-06-17 14:50:07 -07:00
static::eagerly_load_many_to_many($relationship, $parents, $eloquent->relating_key, $eloquent->relating_table, $include);
2011-06-08 23:45:08 -05:00
}
}
/**
2011-06-17 14:50:07 -07:00
* Eagerly load a 1:1 relationship.
2011-06-08 23:45:08 -05:00
*
2011-06-17 14:50:07 -07:00
* @param object $relationship
* @param array $parents
2011-06-08 23:45:08 -05:00
* @param string $relating_key
* @param string $relating
* @param string $include
* @return void
*/
2011-06-17 14:50:07 -07:00
private static function eagerly_load_one($relationship, &$parents, $relating_key, $include)
2011-06-08 23:45:08 -05:00
{
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Get the all of the related models by the parent IDs.
//
// Remember, the parent results are keyed by ID. So, we
// can simply pass the keys of the array into the query.
//
// After getting the models, we'll match by ID.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
{
$parents[$child->$relating_key]->ignore[$include] = $child;
}
}
2011-06-08 23:45:08 -05:00
2011-06-17 14:50:07 -07:00
/**
* Eagerly load a 1:* relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating
* @param string $include
* @return void
*/
private static function eagerly_load_many($relationship, &$parents, $relating_key, $include)
{
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
2011-06-08 23:45:08 -05:00
{
2011-06-17 14:50:07 -07:00
$parents[$child->$relating_key]->ignore[$include][$child->id] = $child;
2011-06-08 23:45:08 -05:00
}
}
/**
* Eagerly load a 1:1 belonging relationship.
*
2011-06-17 14:50:07 -07:00
* @param object $relationship
* @param array $parents
2011-06-08 23:45:08 -05:00
* @param string $relating_key
* @param string $include
* @return void
*/
2011-06-17 14:50:07 -07:00
private static function eagerly_load_belonging($relationship, &$parents, $relating_key, $include)
2011-06-08 23:45:08 -05:00
{
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Gather the keys from the parent models. Since the
// foreign key is on the parent model for this type of
// relationship, we have to gather them individually.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
$keys = array();
2011-06-17 14:50:07 -07:00
foreach ($parents as &$parent)
2011-06-08 23:45:08 -05:00
{
2011-06-17 14:50:07 -07:00
$keys[] = $parent->$relating_key;
2011-06-08 23:45:08 -05:00
}
// -----------------------------------------------------
// Get the related models.
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
$children = $relationship->where_in('id', array_unique($keys))->get();
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Match the child models with their parent by ID.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
foreach ($parents as &$parent)
2011-06-08 23:45:08 -05:00
{
if (array_key_exists($parent->$relating_key, $children))
{
$parent->ignore[$include] = $children[$parent->$relating_key];
}
2011-06-08 23:45:08 -05:00
}
}
/**
* Eagerly load a many-to-many relationship.
*
2011-06-17 14:50:07 -07:00
* @param object $relationship
* @param array $parents
2011-06-08 23:45:08 -05:00
* @param string $relating_key
* @param string $relating_table
* @param string $include
2011-06-17 14:50:07 -07:00
*
2011-06-08 23:45:08 -05:00
* @return void
*/
2011-06-17 14:50:07 -07:00
private static function eagerly_load_many_to_many($relationship, &$parents, $relating_key, $relating_table, $include)
2011-06-08 23:45:08 -05:00
{
2011-06-17 14:50:07 -07:00
$relationship->query->select = null;
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
// Retrieve the raw results as stdClasses.
//
// We also add the foreign key to the select which will allow us
// to match the models back to their parents.
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
$children = $relationship->query
2011-06-17 14:53:41 -07:00
->where_in($relating_table.'.'.$relating_key, array_keys($parents))
->get(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key);
2011-06-08 23:45:08 -05:00
2011-06-17 14:50:07 -07:00
$class = get_class($relationship);
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
// Create the related models.
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
foreach ($children as $child)
2011-06-08 23:45:08 -05:00
{
$related = new $class;
2011-06-08 23:45:08 -05:00
$related->attributes = (array) $child;
2011-06-08 23:45:08 -05:00
$related->exists = true;
// -----------------------------------------------------
// Remove the foreign key from the attributes since it
2011-06-17 14:50:07 -07:00
// was added to the query to help us match the models.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
unset($related->attributes[$relating_key]);
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
// Match the child model its parent by ID.
2011-06-08 23:45:08 -05:00
// -----------------------------------------------------
2011-06-17 14:50:07 -07:00
$parents[$child->$relating_key]->ignore[$include][$child->id] = $related;
2011-06-08 23:45:08 -05:00
}
}
}