Commit d6d667af authored by Taylor Otwell's avatar Taylor Otwell

Added Eloquent 2.

Signed-off-by: 's avatarTaylor Otwell <taylorotwell@gmail.com>
parent 3de0d1af
......@@ -126,6 +126,7 @@ return array(
'Cookie' => 'Laravel\\Cookie',
'Crypter' => 'Laravel\\Crypter',
'DB' => 'Laravel\\Database',
'Eloquent' => 'Laravel\\Database\\Eloquent\\Model',
'Event' => 'Laravel\\Event',
'File' => 'Laravel\\File',
'Filter' => 'Laravel\\Routing\\Filter',
......
This diff is collapsed.
<?php namespace Laravel\Database\Eloquent; use Laravel\Database;
class Query {
/**
* The model instance being queried.
*
* @var Model
*/
public $model;
/**
* The fluent query builder for the query instance.
*
* @var Query
*/
public $table;
/**
* The relationships that should be eagerly loaded by the query.
*
* @var array
*/
public $includes = array();
/**
* The methods that should be returned from the fluent query builder.
*
* @var array
*/
public $passthru = array(
'lists', 'only', 'insert', 'update', 'increment', 'decrement',
'count', 'min', 'max', 'avg', 'sum'
);
/**
* Creat a new query instance for a model.
*
* @param Model $model
* @return void
*/
public function __construct($model)
{
$this->model = ($model instanceof Model) ? $model : new $model;
$this->table = $this->query();
}
/**
* Get the first model result for the query.
*
* @param array $columns
* @return mixed
*/
public function first($columns = array('*'))
{
$results = $this->hydrate($this->model, $this->table->take(1)->get($columns, false));
return (is_array($results)) ? head($results) : $results;
}
/**
* Get all of the model results for the query.
*
* @param array $columns
* @param bool $include
* @return array
*/
public function get($columns = array('*'), $include = true)
{
$results = $this->hydrate($this->model, $this->table->get($columns));
if ($include)
{
foreach ($this->model_includes() as $relationship => $constraints)
{
// If the relationship is nested, we will skip laoding it here and let
// the load method parse and set the nested eager loads on the right
// relationship when it is getting ready to eager laod it.
if (str_contains($relationship, '.'))
{
continue;
}
$this->load($results, $relationship, $constraints);
}
}
return $results;
}
/**
* Hydrate an array of models from the given results.
*
* @param Model $model
* @param array $results
* @return array
*/
public function hydrate($model, $results)
{
$class = get_class($model);
$models = array();
// We'll spin through the array of database results and hydrate a model
// for each one of the records. We will also set the "exists" flag to
// "true" so that the model will be updated when it is saved.
foreach ((array) $results as $result)
{
$result = (array) $result;
$models[$result[$this->model->key()]] = new $class($result, true);
}
return $models;
}
/**
* Hydrate an eagerly loaded relationship on the model results.
*
* @param array $results
* @param string $relationship
* @param array|null $constraints
* @return void
*/
protected function load(&$results, $relationship, $constraints)
{
$query = $this->model->$relationship();
$query->model->includes = $this->nested_includes($relationship);
// We'll remove any of the where clauses from the relationship to give
// the relationship the opportunity to set the constraints for an
// eager relationship using a separate, specific method.
$query->table->reset_where();
$query->eagerly_constrain($results);
// Constraints may be specified in-line for the eager load by passing
// a Closure as the value portion of the eager load. We can use the
// query builder's nested query support to add the constraints.
if ( ! is_null($constraints))
{
$query->table->where_nested($constraints);
}
// Before matching the models, we will initialize the relationship
// to either null for single-value relationships or an array for
// the multi-value relationships as their baseline value.
$query->initialize($results, $relationship);
$query->match($relationship, $results, $query->get());
}
/**
* Gather the nested includes for a given relationship.
*
* @param string $relationship
* @return array
*/
protected function nested_includes($relationship)
{
$nested = array();
foreach ($this->model_includes() as $include => $constraints)
{
// To get the nested includes, we want to find any includes that begin
// the relationship and a dot, then we will strip off the leading
// nesting indicator and set the include in the array.
if (starts_with($include, $relationship.'.'))
{
$nested[substr($include, strlen($relationship.'.'))] = $constraints;
}
}
return $nested;
}
/**
* Get the eagerly loaded relationships for the model.
*
* @return array
*/
protected function model_includes()
{
$includes = array();
foreach ($this->model->includes as $relationship => $constraints)
{
// When eager loading relationships, constraints may be set on the eager
// load definition; however, is none are set, we need to swap the key
// and the value of the array since there are no constraints.
if (is_numeric($relationship))
{
list($relationship, $constraints) = array($constraints, null);
}
$includes[$relationship] = $constraints;
}
return $includes;
}
/**
* Get a fluent query builder for the model.
*
* @return Query
*/
protected function query()
{
return $this->connection()->table($this->model->table());
}
/**
* Get the database connection for the model.
*
* @return Connection
*/
protected function connection()
{
return Database::connection($this->model->connection());
}
/**
* Handle dynamic method calls to the query.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
$result = call_user_func_array(array($this->table, $method), $parameters);
// Some methods may get their results straight from the fluent query
// builder, such as the aggregate methods. If the called method is
// one of these, we will return the result straight away.
if (in_array($method, $this->passthru))
{
return $result;
}
return $this;
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent\Relationships;
class Belongs_To extends Relationship {
/**
* Get the properly hydrated results for the relationship.
*
* @return Model
*/
public function results()
{
return parent::first();
}
/**
* Set the proper constraints on the relationship table.
*
* @return void
*/
protected function constrain()
{
$foreign = $this->base->get_attribute($this->foreign);
$this->table->where($this->base->key(), '=', $foreign);
}
/**
* Initialize a relationship on an array of parent models.
*
* @param array $parents
* @param string $relationship
* @return void
*/
public function initialize(&$parents, $relationship)
{
foreach ($parents as &$parent)
{
$parent->relationships[$relationship] = null;
}
}
/**
* Set the proper constraints on the relationship table for an eager load.
*
* @param array $results
* @return void
*/
public function eagerly_constrain($results)
{
$keys = array();
// Inverse one-to-many relationships require us to gather the keys from the
// parent models and use those keys when setting the constraint since we
// are looking for the parent of a child model in this relationship.
foreach ($results as $result)
{
$keys[] = $result->{$this->foreign_key()};
}
$this->table->where_in($this->model->key(), array_unique($keys));
}
/**
* Match eagerly loaded child models to their parent models.
*
* @param array $children
* @param array $parents
* @return void
*/
public function match($relationship, &$children, $parents)
{
$foreign = $this->foreign_key();
foreach ($children as &$child)
{
if (array_key_exists($child->$foreign, $parents))
{
$child->relationships[$relationship] = $parents[$child->$foreign];
}
}
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent\Relationships;
class Has_Many extends Has_One_Or_Many {
/**
* Get the properly hydrated results for the relationship.
*
* @return array
*/
public function results()
{
return parent::get();
}
/**
* Initialize a relationship on an array of parent models.
*
* @param array $parents
* @param string $relationship
* @return void
*/
public function initialize(&$parents, $relationship)
{
foreach ($parents as &$parent)
{
$parent->relationships[$relationship] = array();
}
}
/**
* Match eagerly loaded child models to their parent models.
*
* @param array $parents
* @param array $children
* @return void
*/
public function match($relationship, &$parents, $children)
{
$foreign = $this->foreign_key();
foreach ($children as $key => $child)
{
$parents[$child->$foreign]->relationships[$relationship][$child->get_key()] = $child;
}
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent\Relationships;
class Has_Many_And_Belongs_To extends Relationship {
/**
* The name of the intermediate, joining table.
*
* @var string
*/
protected $joining;
/**
* The other or "associated" key. This is the foreign key of the related model.
*
* @var string
*/
protected $other;
/**
* Create a new many to many relationship instance.
*
* @param Model $model
* @param string $associated
* @param string $table
* @param string $foreign
* @param string $other
* @return void
*/
public function __construct($model, $associated, $table, $foreign, $other)
{
$this->other = $other;
$this->joining = $table;
parent::__construct($model, $associated, $foreign);
}
/**
* Get the properly hydrated results for the relationship.
*
* @return array
*/
public function results()
{
return parent::get();
}
/**
* Insert a new record into the joining table of the association.
*
* @param int $id
* @return bool
*/
public function add($id)
{
return $this->insert_joining($this->join_record($id));
}
/**
* Insert a new record for the association.
*
* @param array $attributes
* @return bool
*/
public function insert($attributes)
{
$id = $this->table->insert_get_id($attributes, $this->model->sequence());
$result = $this->insert_joining($this->join_record($id));
return is_numeric($id) and $result;
}
/**
* Delete all of the records from the joining table for the model.
*
* @return int
*/
public function delete()
{
return $this->joining_table()->where($this->foreign_key(), '=', $this->base->get_key())->delete();
}
/**
* Create an array representing a new joining record for the association.
*
* @param int $id
* @return array
*/
protected function join_record($id)
{
return array($this->foreign_key() => $this->base->get_key(), $this->other_key() => $id);
}
/**
* Insert a new record into the joining table of the association.
*
* @param array $attributes
* @return void
*/
protected function insert_joining($attributes)
{
return $this->joining_table()->insert($attributes);
}
/**
* Get a fluent query for the joining table of the relationship.
*
* @return Query
*/
protected function joining_table()
{
return $this->connection()->table($this->joining);
}
/**
* Set the proper constraints on the relationship table.
*
* @return void
*/
protected function constrain()
{
$foreign = $this->foreign_key();
$this->set_select($foreign)->set_join($this->other_key())->set_where($foreign);
}
/**
* Set the SELECT clause on the query builder for the relationship.
*
* @param string $foreign
* @return void
*/
protected function set_select($foreign)
{
$foreign = $this->joining.'.'.$foreign.' as eloquent_foreign_key';
$this->table->select(array($this->model->table().'.*', $foreign));
return $this;
}
/**
* Set the JOIN clause on the query builder for the relationship.
*
* @param string $other
* @return void
*/
protected function set_join($other)
{
$this->table->join($this->joining, $this->associated_key(), '=', $this->joining.'.'.$other);
return $this;
}
/**
* Set the WHERE clause on the query builder for the relationship.
*
* @param string $foreign
* @return void
*/
protected function set_where($foreign)
{
$this->table->where($this->joining.'.'.$foreign, '=', $this->base->get_key());
return $this;
}
/**
* Initialize a relationship on an array of parent models.
*
* @param array $parents
* @param string $relationship
* @return void
*/
public function initialize(&$parents, $relationship)
{
foreach ($parents as &$parent)
{
$parent->relationships[$relationship] = array();
}
}
/**
* Set the proper constraints on the relationship table for an eager load.
*
* @param array $results
* @return void
*/
public function eagerly_constrain($results)
{
$this->table->where_in($this->joining.'.'.$this->foreign_key(), array_keys($results));
}
/**
* Match eagerly loaded child models to their parent models.
*
* @param array $parents
* @param array $children
* @return void
*/
public function match($relationship, &$parents, $children)
{
$foreign = 'eloquent_foreign_key';
foreach ($children as $key => $child)
{
$parents[$child->$foreign]->relationships[$relationship][$child->{$child->key()}] = $child;
// After matching the child model with its parent, we can remove the foreign key
// from the model, as it was only necessary to allow us to know which parent
// the child belongs to for eager loading and isn't necessary otherwise.
unset($child->attributes[$foreign]);
unset($child->original[$foreign]);
}
}
/**
* Get the other or associated key for the relationship.
*
* @return string
*/
protected function other_key()
{
return Relationship::foreign($this->model, $this->other);
}
/**
* Get the fully qualified associated table's primary key.
*
* @return string
*/
protected function associated_key()
{
return $this->model->table().'.'.$this->model->key();
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent\Relationships;
class Has_One extends Has_One_Or_Many {
/**
* Get the properly hydrated results for the relationship.
*
* @return Model
*/
public function results()
{
return parent::first();
}
/**
* Initialize a relationship on an array of parent models.
*
* @param array $parents
* @param string $relationship
* @return void
*/
public function initialize(&$parents, $relationship)
{
foreach ($parents as &$parent)
{
$parent->relationships[$relationship] = null;
}
}
/**
* Match eagerly loaded child models to their parent models.
*
* @param array $parents
* @param array $children
* @return void
*/
public function match($relationship, &$parents, $children)
{
$foreign = $this->foreign_key();
foreach ($children as $key => $child)
{
$parents[$child->$foreign]->relationships[$relationship] = $child;
}
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent\Relationships; use Eloquent\Model;
class Has_One_Or_Many extends Relationship {
/**
* Insert a new record for the association.
*
* @param array $attributes
* @return bool
*/
public function insert($attributes)
{
$attributes[$this->foreign_key()] = $this->base->get_key();
return parent::insert($attributes);
}
/**
* Set the proper constraints on the relationship table.
*
* @return void
*/
protected function constrain()
{
$this->table->where($this->foreign_key(), '=', $this->base->get_key());
}
/**
* Set the proper constraints on the relationship table for an eager load.
*
* @param array $results
* @return void
*/
public function eagerly_constrain($results)
{
$this->table->where_in($this->foreign_key(), array_keys($results));
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent\Relationships; use Eloquent\Model, Eloquent\Query;
abstract class Relationship extends Query {
/**
* The base model for the relationship.
*
* @var Model
*/
protected $base;
/**
* Create a new has one or many association instance.
*
* @param Model $model
* @param string $associated
* @param string $foreign
* @return void
*/
public function __construct($model, $associated, $foreign)
{
$this->foreign = $foreign;
// We will go ahead and set the model and associated instances on the relationship
// to match the relationship targets passed in from the model. These will allow
// us to gather more inforamtion on the relationship.
$this->model = ($associated instanceof Model) ? $associated : new $associated;
if ($model instanceof Model)
{
$this->base = $model;
}
else
{
$this->base = new $model;
}
// Next we'll set the fluent query builder for the relationship and constrain
// the query such that it only returns the models that are appropriate for
// the relationship, typically by setting the foreign key.
$this->table = $this->query();
$this->constrain();
}
/**
* Get the foreign key name for the given model.
*
* @param string $model
* @param string $foreign
* @return string
*/
public static function foreign($model, $foreign = null)
{
if ( ! is_null($foreign)) return $foreign;
// If the model is an object, we will simply get the class of the object and
// then take the basename, which is simply the object name minus the
// namespace, and we'll append "_id" to the name.
if (is_object($model))
{
$model = get_class($model);
}
return strtolower(basename($model).'_id');
}
/**
* Get the foreign key for the relationship.
*
* @return string
*/
protected function foreign_key()
{
return Relationship::foreign($this->base, $this->foreign);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment