Added vendor/ directory for Composer's installed files

This commit is contained in:
Ascendings
2015-08-30 12:33:20 -04:00
parent 45df179c49
commit b66a773ed8
1162 changed files with 112457 additions and 0 deletions

View File

@ -0,0 +1,948 @@
<?php namespace Illuminate\Database\Eloquent;
use Closure;
use Illuminate\Pagination\Paginator;
use Illuminate\Database\Query\Expression;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Builder as QueryBuilder;
class Builder {
/**
* The base query builder instance.
*
* @var \Illuminate\Database\Query\Builder
*/
protected $query;
/**
* The model being queried.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $model;
/**
* The relationships that should be eager loaded.
*
* @var array
*/
protected $eagerLoad = array();
/**
* All of the registered builder macros.
*
* @var array
*/
protected $macros = array();
/**
* A replacement for the typical delete function.
*
* @var \Closure
*/
protected $onDelete;
/**
* The methods that should be returned from query builder.
*
* @var array
*/
protected $passthru = array(
'toSql', 'lists', 'insert', 'insertGetId', 'pluck', 'count',
'min', 'max', 'avg', 'sum', 'exists', 'getBindings',
);
/**
* Create a new Eloquent query builder instance.
*
* @param \Illuminate\Database\Query\Builder $query
* @return void
*/
public function __construct(QueryBuilder $query)
{
$this->query = $query;
}
/**
* Find a model by its primary key.
*
* @param mixed $id
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|null
*/
public function find($id, $columns = array('*'))
{
if (is_array($id))
{
return $this->findMany($id, $columns);
}
$this->query->where($this->model->getQualifiedKeyName(), '=', $id);
return $this->first($columns);
}
/**
* Find a model by its primary key.
*
* @param array $ids
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findMany($ids, $columns = array('*'))
{
if (empty($ids)) return $this->model->newCollection();
$this->query->whereIn($this->model->getQualifiedKeyName(), $ids);
return $this->get($columns);
}
/**
* Find a model by its primary key or throw an exception.
*
* @param mixed $id
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function findOrFail($id, $columns = array('*'))
{
$result = $this->find($id, $columns);
if (is_array($id))
{
if (count($result) == count(array_unique($id))) return $result;
}
elseif ( ! is_null($result))
{
return $result;
}
throw (new ModelNotFoundException)->setModel(get_class($this->model));
}
/**
* Execute the query and get the first result.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model|static|null
*/
public function first($columns = array('*'))
{
return $this->take(1)->get($columns)->first();
}
/**
* Execute the query and get the first result or throw an exception.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model|static
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function firstOrFail($columns = array('*'))
{
if ( ! is_null($model = $this->first($columns))) return $model;
throw (new ModelNotFoundException)->setModel(get_class($this->model));
}
/**
* Execute the query as a "select" statement.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public function get($columns = array('*'))
{
$models = $this->getModels($columns);
// If we actually found models we will also eager load any relationships that
// have been specified as needing to be eager loaded, which will solve the
// n+1 query issue for the developers to avoid running a lot of queries.
if (count($models) > 0)
{
$models = $this->eagerLoadRelations($models);
}
return $this->model->newCollection($models);
}
/**
* Pluck a single column from the database.
*
* @param string $column
* @return mixed
*/
public function pluck($column)
{
$result = $this->first(array($column));
if ($result) return $result->{$column};
}
/**
* Chunk the results of the query.
*
* @param int $count
* @param callable $callback
* @return void
*/
public function chunk($count, callable $callback)
{
$results = $this->forPage($page = 1, $count)->get();
while (count($results) > 0)
{
// On each chunk result set, we will pass them to the callback and then let the
// developer take care of everything within the callback, which allows us to
// keep the memory low for spinning through large result sets for working.
call_user_func($callback, $results);
$page++;
$results = $this->forPage($page, $count)->get();
}
}
/**
* Get an array with the values of a given column.
*
* @param string $column
* @param string $key
* @return array
*/
public function lists($column, $key = null)
{
$results = $this->query->lists($column, $key);
// If the model has a mutator for the requested column, we will spin through
// the results and mutate the values so that the mutated version of these
// columns are returned as you would expect from these Eloquent models.
if ($this->model->hasGetMutator($column))
{
foreach ($results as $key => &$value)
{
$fill = array($column => $value);
$value = $this->model->newFromBuilder($fill)->$column;
}
}
return $results;
}
/**
* Paginate the given query.
*
* @param int $perPage
* @param array $columns
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = null, $columns = ['*'])
{
$total = $this->query->getCountForPagination();
$this->query->forPage(
$page = Paginator::resolveCurrentPage(),
$perPage = $perPage ?: $this->model->getPerPage()
);
return new LengthAwarePaginator($this->get($columns), $total, $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
]);
}
/**
* Paginate the given query into a simple paginator.
*
* @param int $perPage
* @param array $columns
* @return \Illuminate\Contracts\Pagination\Paginator
*/
public function simplePaginate($perPage = null, $columns = ['*'])
{
$page = Paginator::resolveCurrentPage();
$perPage = $perPage ?: $this->model->getPerPage();
$this->skip(($page - 1) * $perPage)->take($perPage + 1);
return new Paginator($this->get($columns), $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
]);
}
/**
* Update a record in the database.
*
* @param array $values
* @return int
*/
public function update(array $values)
{
return $this->query->update($this->addUpdatedAtColumn($values));
}
/**
* Increment a column's value by a given amount.
*
* @param string $column
* @param int $amount
* @param array $extra
* @return int
*/
public function increment($column, $amount = 1, array $extra = array())
{
$extra = $this->addUpdatedAtColumn($extra);
return $this->query->increment($column, $amount, $extra);
}
/**
* Decrement a column's value by a given amount.
*
* @param string $column
* @param int $amount
* @param array $extra
* @return int
*/
public function decrement($column, $amount = 1, array $extra = array())
{
$extra = $this->addUpdatedAtColumn($extra);
return $this->query->decrement($column, $amount, $extra);
}
/**
* Add the "updated at" column to an array of values.
*
* @param array $values
* @return array
*/
protected function addUpdatedAtColumn(array $values)
{
if ( ! $this->model->usesTimestamps()) return $values;
$column = $this->model->getUpdatedAtColumn();
return array_add($values, $column, $this->model->freshTimestampString());
}
/**
* Delete a record from the database.
*
* @return mixed
*/
public function delete()
{
if (isset($this->onDelete))
{
return call_user_func($this->onDelete, $this);
}
return $this->query->delete();
}
/**
* Run the default delete function on the builder.
*
* @return mixed
*/
public function forceDelete()
{
return $this->query->delete();
}
/**
* Register a replacement for the default delete function.
*
* @param \Closure $callback
* @return void
*/
public function onDelete(Closure $callback)
{
$this->onDelete = $callback;
}
/**
* Get the hydrated models without eager loading.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model[]
*/
public function getModels($columns = array('*'))
{
$results = $this->query->get($columns);
$connection = $this->model->getConnectionName();
return $this->model->hydrate($results, $connection)->all();
}
/**
* Eager load the relationships for the models.
*
* @param array $models
* @return array
*/
public function eagerLoadRelations(array $models)
{
foreach ($this->eagerLoad as $name => $constraints)
{
// For nested eager loads we'll skip loading them here and they will be set as an
// eager load on the query to retrieve the relation so that they will be eager
// loaded on that query, because that is where they get hydrated as models.
if (strpos($name, '.') === false)
{
$models = $this->loadRelation($models, $name, $constraints);
}
}
return $models;
}
/**
* Eagerly load the relationship on a set of models.
*
* @param array $models
* @param string $name
* @param \Closure $constraints
* @return array
*/
protected function loadRelation(array $models, $name, Closure $constraints)
{
// First we will "back up" the existing where conditions on the query so we can
// add our eager constraints. Then we will merge the wheres that were on the
// query back to it in order that any where conditions might be specified.
$relation = $this->getRelation($name);
$relation->addEagerConstraints($models);
call_user_func($constraints, $relation);
$models = $relation->initRelation($models, $name);
// Once we have the results, we just match those back up to their parent models
// using the relationship instance. Then we just return the finished arrays
// of models which have been eagerly hydrated and are readied for return.
$results = $relation->getEager();
return $relation->match($models, $results, $name);
}
/**
* Get the relation instance for the given relation name.
*
* @param string $relation
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function getRelation($relation)
{
// We want to run a relationship query without any constrains so that we will
// not have to remove these where clauses manually which gets really hacky
// and is error prone while we remove the developer's own where clauses.
$query = Relation::noConstraints(function() use ($relation)
{
return $this->getModel()->$relation();
});
$nested = $this->nestedRelations($relation);
// If there are nested relationships set on the query, we will put those onto
// the query instances so that they can be handled after this relationship
// is loaded. In this way they will all trickle down as they are loaded.
if (count($nested) > 0)
{
$query->getQuery()->with($nested);
}
return $query;
}
/**
* Get the deeply nested relations for a given top-level relation.
*
* @param string $relation
* @return array
*/
protected function nestedRelations($relation)
{
$nested = array();
// We are basically looking for any relationships that are nested deeper than
// the given top-level relationship. We will just check for any relations
// that start with the given top relations and adds them to our arrays.
foreach ($this->eagerLoad as $name => $constraints)
{
if ($this->isNested($name, $relation))
{
$nested[substr($name, strlen($relation.'.'))] = $constraints;
}
}
return $nested;
}
/**
* Determine if the relationship is nested.
*
* @param string $name
* @param string $relation
* @return bool
*/
protected function isNested($name, $relation)
{
$dots = str_contains($name, '.');
return $dots && starts_with($name, $relation.'.');
}
/**
* Add a basic where clause to the query.
*
* @param string $column
* @param string $operator
* @param mixed $value
* @param string $boolean
* @return $this
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure)
{
$query = $this->model->newQueryWithoutScopes();
call_user_func($column, $query);
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
}
else
{
call_user_func_array(array($this->query, 'where'), func_get_args());
}
return $this;
}
/**
* Add an "or where" clause to the query.
*
* @param string $column
* @param string $operator
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhere($column, $operator = null, $value = null)
{
return $this->where($column, $operator, $value, 'or');
}
/**
* Add a relationship count condition to the query.
*
* @param string $relation
* @param string $operator
* @param int $count
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
{
if (strpos($relation, '.') !== false)
{
return $this->hasNested($relation, $operator, $count, $boolean, $callback);
}
$relation = $this->getHasRelationQuery($relation);
$query = $relation->getRelationCountQuery($relation->getRelated()->newQuery(), $this);
if ($callback) call_user_func($callback, $query);
return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
}
/**
* Add nested relationship count conditions to the query.
*
* @param string $relations
* @param string $operator
* @param int $count
* @param string $boolean
* @param \Closure $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
protected function hasNested($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
{
$relations = explode('.', $relations);
// In order to nest "has", we need to add count relation constraints on the
// callback Closure. We'll do this by simply passing the Closure its own
// reference to itself so it calls itself recursively on each segment.
$closure = function ($q) use (&$closure, &$relations, $operator, $count, $boolean, $callback)
{
if (count($relations) > 1)
{
$q->whereHas(array_shift($relations), $closure);
}
else
{
$q->has(array_shift($relations), $operator, $count, 'and', $callback);
}
};
return $this->has(array_shift($relations), '>=', 1, $boolean, $closure);
}
/**
* Add a relationship count condition to the query.
*
* @param string $relation
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function doesntHave($relation, $boolean = 'and', Closure $callback = null)
{
return $this->has($relation, '<', 1, $boolean, $callback);
}
/**
* Add a relationship count condition to the query with where clauses.
*
* @param string $relation
* @param \Closure $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
{
return $this->has($relation, $operator, $count, 'and', $callback);
}
/**
* Add a relationship count condition to the query with where clauses.
*
* @param string $relation
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function whereDoesntHave($relation, Closure $callback = null)
{
return $this->doesntHave($relation, 'and', $callback);
}
/**
* Add a relationship count condition to the query with an "or".
*
* @param string $relation
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orHas($relation, $operator = '>=', $count = 1)
{
return $this->has($relation, $operator, $count, 'or');
}
/**
* Add a relationship count condition to the query with where clauses and an "or".
*
* @param string $relation
* @param \Closure $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
{
return $this->has($relation, $operator, $count, 'or', $callback);
}
/**
* Add the "has" condition where clause to the query.
*
* @param \Illuminate\Database\Eloquent\Builder $hasQuery
* @param \Illuminate\Database\Eloquent\Relations\Relation $relation
* @param string $operator
* @param int $count
* @param string $boolean
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function addHasWhere(Builder $hasQuery, Relation $relation, $operator, $count, $boolean)
{
$this->mergeWheresToHas($hasQuery, $relation);
if (is_numeric($count))
{
$count = new Expression($count);
}
return $this->where(new Expression('('.$hasQuery->toSql().')'), $operator, $count, $boolean);
}
/**
* Merge the "wheres" from a relation query to a has query.
*
* @param \Illuminate\Database\Eloquent\Builder $hasQuery
* @param \Illuminate\Database\Eloquent\Relations\Relation $relation
* @return void
*/
protected function mergeWheresToHas(Builder $hasQuery, Relation $relation)
{
// Here we have the "has" query and the original relation. We need to copy over any
// where clauses the developer may have put in the relationship function over to
// the has query, and then copy the bindings from the "has" query to the main.
$relationQuery = $relation->getBaseQuery();
$hasQuery = $hasQuery->getModel()->removeGlobalScopes($hasQuery);
$hasQuery->mergeWheres(
$relationQuery->wheres, $relationQuery->getBindings()
);
$this->query->mergeBindings($hasQuery->getQuery());
}
/**
* Get the "has relation" base query instance.
*
* @param string $relation
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function getHasRelationQuery($relation)
{
return Relation::noConstraints(function() use ($relation)
{
return $this->getModel()->$relation();
});
}
/**
* Set the relationships that should be eager loaded.
*
* @param mixed $relations
* @return $this
*/
public function with($relations)
{
if (is_string($relations)) $relations = func_get_args();
$eagers = $this->parseRelations($relations);
$this->eagerLoad = array_merge($this->eagerLoad, $eagers);
return $this;
}
/**
* Parse a list of relations into individuals.
*
* @param array $relations
* @return array
*/
protected function parseRelations(array $relations)
{
$results = array();
foreach ($relations as $name => $constraints)
{
// If the "relation" value is actually a numeric key, we can assume that no
// constraints have been specified for the eager load and we'll just put
// an empty Closure with the loader so that we can treat all the same.
if (is_numeric($name))
{
$f = function() {};
list($name, $constraints) = array($constraints, $f);
}
// We need to separate out any nested includes. Which allows the developers
// to load deep relationships using "dots" without stating each level of
// the relationship with its own key in the array of eager load names.
$results = $this->parseNested($name, $results);
$results[$name] = $constraints;
}
return $results;
}
/**
* Parse the nested relationships in a relation.
*
* @param string $name
* @param array $results
* @return array
*/
protected function parseNested($name, $results)
{
$progress = array();
// If the relation has already been set on the result array, we will not set it
// again, since that would override any constraints that were already placed
// on the relationships. We will only set the ones that are not specified.
foreach (explode('.', $name) as $segment)
{
$progress[] = $segment;
if ( ! isset($results[$last = implode('.', $progress)]))
{
$results[$last] = function() {};
}
}
return $results;
}
/**
* Call the given model scope on the underlying model.
*
* @param string $scope
* @param array $parameters
* @return \Illuminate\Database\Query\Builder
*/
protected function callScope($scope, $parameters)
{
array_unshift($parameters, $this);
return call_user_func_array(array($this->model, $scope), $parameters) ?: $this;
}
/**
* Get the underlying query builder instance.
*
* @return \Illuminate\Database\Query\Builder|static
*/
public function getQuery()
{
return $this->query;
}
/**
* Set the underlying query builder instance.
*
* @param \Illuminate\Database\Query\Builder $query
* @return $this
*/
public function setQuery($query)
{
$this->query = $query;
return $this;
}
/**
* Get the relationships being eagerly loaded.
*
* @return array
*/
public function getEagerLoads()
{
return $this->eagerLoad;
}
/**
* Set the relationships being eagerly loaded.
*
* @param array $eagerLoad
* @return $this
*/
public function setEagerLoads(array $eagerLoad)
{
$this->eagerLoad = $eagerLoad;
return $this;
}
/**
* Get the model instance being queried.
*
* @return \Illuminate\Database\Eloquent\Model
*/
public function getModel()
{
return $this->model;
}
/**
* Set a model instance for the model being queried.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return $this
*/
public function setModel(Model $model)
{
$this->model = $model;
$this->query->from($model->getTable());
return $this;
}
/**
* Extend the builder with a given callback.
*
* @param string $name
* @param \Closure $callback
* @return void
*/
public function macro($name, Closure $callback)
{
$this->macros[$name] = $callback;
}
/**
* Get the given macro by name.
*
* @param string $name
* @return \Closure
*/
public function getMacro($name)
{
return array_get($this->macros, $name);
}
/**
* Dynamically handle calls into the query instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (isset($this->macros[$method]))
{
array_unshift($parameters, $this);
return call_user_func_array($this->macros[$method], $parameters);
}
elseif (method_exists($this->model, $scope = 'scope'.ucfirst($method)))
{
return $this->callScope($scope, $parameters);
}
$result = call_user_func_array(array($this->query, $method), $parameters);
return in_array($method, $this->passthru) ? $result : $this;
}
/**
* Force a clone of the underlying query builder when cloning.
*
* @return void
*/
public function __clone()
{
$this->query = clone $this->query;
}
}

View File

@ -0,0 +1,266 @@
<?php namespace Illuminate\Database\Eloquent;
use Illuminate\Support\Collection as BaseCollection;
class Collection extends BaseCollection {
/**
* Find a model in the collection by key.
*
* @param mixed $key
* @param mixed $default
* @return \Illuminate\Database\Eloquent\Model
*/
public function find($key, $default = null)
{
if ($key instanceof Model)
{
$key = $key->getKey();
}
return array_first($this->items, function($itemKey, $model) use ($key)
{
return $model->getKey() == $key;
}, $default);
}
/**
* Load a set of relationships onto the collection.
*
* @param mixed $relations
* @return $this
*/
public function load($relations)
{
if (count($this->items) > 0)
{
if (is_string($relations)) $relations = func_get_args();
$query = $this->first()->newQuery()->with($relations);
$this->items = $query->eagerLoadRelations($this->items);
}
return $this;
}
/**
* Add an item to the collection.
*
* @param mixed $item
* @return $this
*/
public function add($item)
{
$this->items[] = $item;
return $this;
}
/**
* Determine if a key exists in the collection.
*
* @param mixed $key
* @param mixed $value
* @return bool
*/
public function contains($key, $value = null)
{
if (func_num_args() == 2) return parent::contains($key, $value);
if ( ! $this->useAsCallable($key))
{
$key = $key instanceof Model ? $key->getKey() : $key;
return parent::contains(function($k, $m) use ($key)
{
return $m->getKey() == $key;
});
}
return parent::contains($key);
}
/**
* Fetch a nested element of the collection.
*
* @param string $key
* @return static
*/
public function fetch($key)
{
return new static(array_fetch($this->toArray(), $key));
}
/**
* Get the max value of a given key.
*
* @param string $key
* @return mixed
*/
public function max($key)
{
return $this->reduce(function($result, $item) use ($key)
{
return is_null($result) || $item->{$key} > $result ? $item->{$key} : $result;
});
}
/**
* Get the min value of a given key.
*
* @param string $key
* @return mixed
*/
public function min($key)
{
return $this->reduce(function($result, $item) use ($key)
{
return is_null($result) || $item->{$key} < $result ? $item->{$key} : $result;
});
}
/**
* Get the array of primary keys.
*
* @return array
*/
public function modelKeys()
{
return array_map(function($m) { return $m->getKey(); }, $this->items);
}
/**
* Merge the collection with the given items.
*
* @param \ArrayAccess|array $items
* @return static
*/
public function merge($items)
{
$dictionary = $this->getDictionary();
foreach ($items as $item)
{
$dictionary[$item->getKey()] = $item;
}
return new static(array_values($dictionary));
}
/**
* Diff the collection with the given items.
*
* @param \ArrayAccess|array $items
* @return static
*/
public function diff($items)
{
$diff = new static;
$dictionary = $this->getDictionary($items);
foreach ($this->items as $item)
{
if ( ! isset($dictionary[$item->getKey()]))
{
$diff->add($item);
}
}
return $diff;
}
/**
* Intersect the collection with the given items.
*
* @param \ArrayAccess|array $items
* @return static
*/
public function intersect($items)
{
$intersect = new static;
$dictionary = $this->getDictionary($items);
foreach ($this->items as $item)
{
if (isset($dictionary[$item->getKey()]))
{
$intersect->add($item);
}
}
return $intersect;
}
/**
* Return only unique items from the collection.
*
* @return static
*/
public function unique()
{
$dictionary = $this->getDictionary();
return new static(array_values($dictionary));
}
/**
* Returns only the models from the collection with the specified keys.
*
* @param mixed $keys
* @return static
*/
public function only($keys)
{
$dictionary = array_only($this->getDictionary(), $keys);
return new static(array_values($dictionary));
}
/**
* Returns all models in the collection except the models with specified keys.
*
* @param mixed $keys
* @return static
*/
public function except($keys)
{
$dictionary = array_except($this->getDictionary(), $keys);
return new static(array_values($dictionary));
}
/**
* Get a dictionary keyed by primary keys.
*
* @param \ArrayAccess|array $items
* @return array
*/
public function getDictionary($items = null)
{
$items = is_null($items) ? $this->items : $items;
$dictionary = array();
foreach ($items as $value)
{
$dictionary[$value->getKey()] = $value;
}
return $dictionary;
}
/**
* Get a base Support collection instance from this collection.
*
* @return \Illuminate\Support\Collection
*/
public function toBase()
{
return new BaseCollection($this->items);
}
}

View File

@ -0,0 +1,5 @@
<?php namespace Illuminate\Database\Eloquent;
use RuntimeException;
class MassAssignmentException extends RuntimeException {}

3399
vendor/illuminate/database/Eloquent/Model.php vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
<?php namespace Illuminate\Database\Eloquent;
use RuntimeException;
class ModelNotFoundException extends RuntimeException {
/**
* Name of the affected Eloquent model.
*
* @var string
*/
protected $model;
/**
* Set the affected Eloquent model.
*
* @param string $model
* @return $this
*/
public function setModel($model)
{
$this->model = $model;
$this->message = "No query results for model [{$model}].";
return $this;
}
/**
* Get the affected Eloquent model.
*
* @return string
*/
public function getModel()
{
return $this->model;
}
}

View File

@ -0,0 +1,27 @@
<?php namespace Illuminate\Database\Eloquent;
use Illuminate\Contracts\Queue\EntityNotFoundException;
use Illuminate\Contracts\Queue\EntityResolver as EntityResolverContract;
class QueueEntityResolver implements EntityResolverContract {
/**
* Resolve the entity for the given ID.
*
* @param string $type
* @param mixed $id
* @return mixed
*/
public function resolve($type, $id)
{
$instance = (new $type)->find($id);
if ($instance)
{
return $instance;
}
throw new EntityNotFoundException($type, $id);
}
}

View File

@ -0,0 +1,310 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
class BelongsTo extends Relation {
/**
* The foreign key of the parent model.
*
* @var string
*/
protected $foreignKey;
/**
* The associated key on the parent model.
*
* @var string
*/
protected $otherKey;
/**
* The name of the relationship.
*
* @var string
*/
protected $relation;
/**
* Create a new belongs to relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $foreignKey
* @param string $otherKey
* @param string $relation
* @return void
*/
public function __construct(Builder $query, Model $parent, $foreignKey, $otherKey, $relation)
{
$this->otherKey = $otherKey;
$this->relation = $relation;
$this->foreignKey = $foreignKey;
parent::__construct($query, $parent);
}
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->query->first();
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
if (static::$constraints)
{
// For belongs to relationships, which are essentially the inverse of has one
// or has many relationships, we need to actually query on the primary key
// of the related models matching on the foreign key that's on a parent.
$table = $this->related->getTable();
$this->query->where($table.'.'.$this->otherKey, '=', $this->parent->{$this->foreignKey});
}
}
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
if ($parent->getQuery()->from == $query->getQuery()->from)
{
return $this->getRelationCountQueryForSelfRelation($query, $parent);
}
$query->select(new Expression('count(*)'));
$otherKey = $this->wrap($query->getModel()->getTable().'.'.$this->otherKey);
return $query->where($this->getQualifiedForeignKey(), '=', new Expression($otherKey));
}
/**
* Add the constraints for a relationship count query on the same table.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQueryForSelfRelation(Builder $query, Builder $parent)
{
$query->select(new Expression('count(*)'));
$tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
$query->from($query->getModel()->getTable().' as '.$tablePrefix.$hash = $this->getRelationCountHash());
$key = $this->wrap($this->getQualifiedForeignKey());
return $query->where($hash.'.'.$query->getModel()->getKeyName(), '=', new Expression($key));
}
/**
* Get a relationship join table hash.
*
* @return string
*/
public function getRelationCountHash()
{
return 'self_'.md5(microtime(true));
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
// We'll grab the primary key name of the related models since it could be set to
// a non-standard name and not "id". We will then construct the constraint for
// our eagerly loading query so it returns the proper models from execution.
$key = $this->related->getTable().'.'.$this->otherKey;
$this->query->whereIn($key, $this->getEagerModelKeys($models));
}
/**
* Gather the keys from an array of related models.
*
* @param array $models
* @return array
*/
protected function getEagerModelKeys(array $models)
{
$keys = array();
// First we need to gather all of the keys from the parent models so we know what
// to query for via the eager loading query. We will add them to an array then
// execute a "where in" statement to gather up all of those related records.
foreach ($models as $model)
{
if ( ! is_null($value = $model->{$this->foreignKey}))
{
$keys[] = $value;
}
}
// If there are no keys that were not null we will just return an array with 0 in
// it so the query doesn't fail, but will not return any results, which should
// be what this developer is expecting in a case where this happens to them.
if (count($keys) == 0)
{
return array(0);
}
return array_values(array_unique($keys));
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, null);
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
$foreign = $this->foreignKey;
$other = $this->otherKey;
// First we will get to build a dictionary of the child models by their primary
// key of the relationship, then we can easily match the children back onto
// the parents using that dictionary and the primary key of the children.
$dictionary = array();
foreach ($results as $result)
{
$dictionary[$result->getAttribute($other)] = $result;
}
// Once we have the dictionary constructed, we can loop through all the parents
// and match back onto their children using these keys of the dictionary and
// the primary key of the children to map them onto the correct instances.
foreach ($models as $model)
{
if (isset($dictionary[$model->$foreign]))
{
$model->setRelation($relation, $dictionary[$model->$foreign]);
}
}
return $models;
}
/**
* Associate the model instance to the given parent.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function associate(Model $model)
{
$this->parent->setAttribute($this->foreignKey, $model->getAttribute($this->otherKey));
return $this->parent->setRelation($this->relation, $model);
}
/**
* Dissociate previously associated model from the given parent.
*
* @return \Illuminate\Database\Eloquent\Model
*/
public function dissociate()
{
$this->parent->setAttribute($this->foreignKey, null);
return $this->parent->setRelation($this->relation, null);
}
/**
* Update the parent model on the relationship.
*
* @param array $attributes
* @return mixed
*/
public function update(array $attributes)
{
$instance = $this->getResults();
return $instance->fill($attributes)->save();
}
/**
* Get the foreign key of the relationship.
*
* @return string
*/
public function getForeignKey()
{
return $this->foreignKey;
}
/**
* Get the fully qualified foreign key of the relationship.
*
* @return string
*/
public function getQualifiedForeignKey()
{
return $this->parent->getTable().'.'.$this->foreignKey;
}
/**
* Get the associated key of the relationship.
*
* @return string
*/
public function getOtherKey()
{
return $this->otherKey;
}
/**
* Get the fully qualified associated key of the relationship.
*
* @return string
*/
public function getQualifiedOtherKeyName()
{
return $this->related->getTable().'.'.$this->otherKey;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Collection;
class HasMany extends HasOneOrMany {
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->query->get();
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, $this->related->newCollection());
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $this->matchMany($models, $results, $relation);
}
}

View File

@ -0,0 +1,340 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
class HasManyThrough extends Relation {
/**
* The distance parent model instance.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $farParent;
/**
* The near key on the relationship.
*
* @var string
*/
protected $firstKey;
/**
* The far key on the relationship.
*
* @var string
*/
protected $secondKey;
/**
* Create a new has many through relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $farParent
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $firstKey
* @param string $secondKey
* @return void
*/
public function __construct(Builder $query, Model $farParent, Model $parent, $firstKey, $secondKey)
{
$this->firstKey = $firstKey;
$this->secondKey = $secondKey;
$this->farParent = $farParent;
parent::__construct($query, $parent);
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
$parentTable = $this->parent->getTable();
$this->setJoin();
if (static::$constraints)
{
$this->query->where($parentTable.'.'.$this->firstKey, '=', $this->farParent->getKey());
}
}
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$parentTable = $this->parent->getTable();
$this->setJoin($query);
$query->select(new Expression('count(*)'));
$key = $this->wrap($parentTable.'.'.$this->firstKey);
return $query->where($this->getHasCompareKey(), '=', new Expression($key));
}
/**
* Set the join clause on the query.
*
* @param \Illuminate\Database\Eloquent\Builder|null $query
* @return void
*/
protected function setJoin(Builder $query = null)
{
$query = $query ?: $this->query;
$foreignKey = $this->related->getTable().'.'.$this->secondKey;
$query->join($this->parent->getTable(), $this->getQualifiedParentKeyName(), '=', $foreignKey);
if ($this->parentSoftDeletes())
{
$query->whereNull($this->parent->getQualifiedDeletedAtColumn());
}
}
/**
* Determine whether close parent of the relation uses Soft Deletes.
*
* @return bool
*/
public function parentSoftDeletes()
{
return in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses_recursive(get_class($this->parent)));
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
$table = $this->parent->getTable();
$this->query->whereIn($table.'.'.$this->firstKey, $this->getKeys($models));
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, $this->related->newCollection());
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
$dictionary = $this->buildDictionary($results);
// Once we have the dictionary we can simply spin through the parent models to
// link them up with their children using the keyed dictionary to make the
// matching very convenient and easy work. Then we'll just return them.
foreach ($models as $model)
{
$key = $model->getKey();
if (isset($dictionary[$key]))
{
$value = $this->related->newCollection($dictionary[$key]);
$model->setRelation($relation, $value);
}
}
return $models;
}
/**
* Build model dictionary keyed by the relation's foreign key.
*
* @param \Illuminate\Database\Eloquent\Collection $results
* @return array
*/
protected function buildDictionary(Collection $results)
{
$dictionary = [];
$foreign = $this->firstKey;
// First we will create a dictionary of models keyed by the foreign key of the
// relationship as this will allow us to quickly access all of the related
// models without having to do nested looping which will be quite slow.
foreach ($results as $result)
{
$dictionary[$result->{$foreign}][] = $result;
}
return $dictionary;
}
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->get();
}
/**
* Execute the query and get the first related model.
*
* @param array $columns
* @return mixed
*/
public function first($columns = ['*'])
{
$results = $this->take(1)->get($columns);
return count($results) > 0 ? $results->first() : null;
}
/**
* Find a related model by its primary key.
*
* @param mixed $id
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|null
*/
public function find($id, $columns = ['*'])
{
if (is_array($id))
{
return $this->findMany($id, $columns);
}
$this->where($this->getRelated()->getQualifiedKeyName(), '=', $id);
return $this->first($columns);
}
/**
* Find multiple related models by their primary keys.
*
* @param mixed $ids
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findMany($ids, $columns = ['*'])
{
if (empty($ids)) return $this->getRelated()->newCollection();
$this->whereIn($this->getRelated()->getQualifiedKeyName(), $ids);
return $this->get($columns);
}
/**
* Execute the query as a "select" statement.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection
*/
public function get($columns = ['*'])
{
// First we'll add the proper select columns onto the query so it is run with
// the proper columns. Then, we will get the results and hydrate out pivot
// models with the result of those columns as a separate model relation.
$columns = $this->query->getQuery()->columns ? [] : $columns;
$select = $this->getSelectColumns($columns);
$models = $this->query->addSelect($select)->getModels();
// If we actually found models we will also eager load any relationships that
// have been specified as needing to be eager loaded. This will solve the
// n + 1 query problem for the developer and also increase performance.
if (count($models) > 0)
{
$models = $this->query->eagerLoadRelations($models);
}
return $this->related->newCollection($models);
}
/**
* Set the select clause for the relation query.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
protected function getSelectColumns(array $columns = ['*'])
{
if ($columns == ['*'])
{
$columns = [$this->related->getTable().'.*'];
}
return array_merge($columns, [$this->parent->getTable().'.'.$this->firstKey]);
}
/**
* Get a paginator for the "select" statement.
*
* @param int $perPage
* @param array $columns
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = null, $columns = ['*'])
{
$this->query->addSelect($this->getSelectColumns($columns));
return $this->query->paginate($perPage, $columns);
}
/**
* Paginate the given query into a simple paginator.
*
* @param int $perPage
* @param array $columns
* @return \Illuminate\Contracts\Pagination\Paginator
*/
public function simplePaginate($perPage = null, $columns = ['*'])
{
$this->query->addSelect($this->getSelectColumns($columns));
return $this->query->simplePaginate($perPage, $columns);
}
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getHasCompareKey()
{
return $this->farParent->getQualifiedKeyName();
}
}

View File

@ -0,0 +1,47 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Collection;
class HasOne extends HasOneOrMany {
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->query->first();
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, null);
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $this->matchOne($models, $results, $relation);
}
}

View File

@ -0,0 +1,412 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
abstract class HasOneOrMany extends Relation {
/**
* The foreign key of the parent model.
*
* @var string
*/
protected $foreignKey;
/**
* The local key of the parent model.
*
* @var string
*/
protected $localKey;
/**
* Create a new has one or many relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $foreignKey
* @param string $localKey
* @return void
*/
public function __construct(Builder $query, Model $parent, $foreignKey, $localKey)
{
$this->localKey = $localKey;
$this->foreignKey = $foreignKey;
parent::__construct($query, $parent);
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
if (static::$constraints)
{
$this->query->where($this->foreignKey, '=', $this->getParentKey());
$this->query->whereNotNull($this->foreignKey);
}
}
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
if ($parent->getQuery()->from == $query->getQuery()->from)
{
return $this->getRelationCountQueryForSelfRelation($query, $parent);
}
return parent::getRelationCountQuery($query, $parent);
}
/**
* Add the constraints for a relationship count query on the same table.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQueryForSelfRelation(Builder $query, Builder $parent)
{
$query->select(new Expression('count(*)'));
$tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
$query->from($query->getModel()->getTable().' as '.$tablePrefix.$hash = $this->getRelationCountHash());
$key = $this->wrap($this->getQualifiedParentKeyName());
return $query->where($hash.'.'.$this->getPlainForeignKey(), '=', new Expression($key));
}
/**
* Get a relationship join table hash.
*
* @return string
*/
public function getRelationCountHash()
{
return 'self_'.md5(microtime(true));
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
$this->query->whereIn($this->foreignKey, $this->getKeys($models, $this->localKey));
}
/**
* Match the eagerly loaded results to their single parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function matchOne(array $models, Collection $results, $relation)
{
return $this->matchOneOrMany($models, $results, $relation, 'one');
}
/**
* Match the eagerly loaded results to their many parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function matchMany(array $models, Collection $results, $relation)
{
return $this->matchOneOrMany($models, $results, $relation, 'many');
}
/**
* Match the eagerly loaded results to their many parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @param string $type
* @return array
*/
protected function matchOneOrMany(array $models, Collection $results, $relation, $type)
{
$dictionary = $this->buildDictionary($results);
// Once we have the dictionary we can simply spin through the parent models to
// link them up with their children using the keyed dictionary to make the
// matching very convenient and easy work. Then we'll just return them.
foreach ($models as $model)
{
$key = $model->getAttribute($this->localKey);
if (isset($dictionary[$key]))
{
$value = $this->getRelationValue($dictionary, $key, $type);
$model->setRelation($relation, $value);
}
}
return $models;
}
/**
* Get the value of a relationship by one or many type.
*
* @param array $dictionary
* @param string $key
* @param string $type
* @return mixed
*/
protected function getRelationValue(array $dictionary, $key, $type)
{
$value = $dictionary[$key];
return $type == 'one' ? reset($value) : $this->related->newCollection($value);
}
/**
* Build model dictionary keyed by the relation's foreign key.
*
* @param \Illuminate\Database\Eloquent\Collection $results
* @return array
*/
protected function buildDictionary(Collection $results)
{
$dictionary = array();
$foreign = $this->getPlainForeignKey();
// First we will create a dictionary of models keyed by the foreign key of the
// relationship as this will allow us to quickly access all of the related
// models without having to do nested looping which will be quite slow.
foreach ($results as $result)
{
$dictionary[$result->{$foreign}][] = $result;
}
return $dictionary;
}
/**
* Attach a model instance to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model)
{
$model->setAttribute($this->getPlainForeignKey(), $this->getParentKey());
return $model->save() ? $model : false;
}
/**
* Attach an array of models to the parent instance.
*
* @param array $models
* @return array
*/
public function saveMany(array $models)
{
array_walk($models, array($this, 'save'));
return $models;
}
/**
* Find a model by its primary key or return new instance of the related model.
*
* @param mixed $id
* @param array $columns
* @return \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model
*/
public function findOrNew($id, $columns = ['*'])
{
if (is_null($instance = $this->find($id, $columns)))
{
$instance = $this->related->newInstance();
$instance->setAttribute($this->getPlainForeignKey(), $this->getParentKey());
}
return $instance;
}
/**
* Get the first related model record matching the attributes or instantiate it.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function firstOrNew(array $attributes)
{
if (is_null($instance = $this->where($attributes)->first()))
{
$instance = $this->related->newInstance($attributes);
$instance->setAttribute($this->getPlainForeignKey(), $this->getParentKey());
}
return $instance;
}
/**
* Get the first related record matching the attributes or create it.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function firstOrCreate(array $attributes)
{
if (is_null($instance = $this->where($attributes)->first()))
{
$instance = $this->create($attributes);
}
return $instance;
}
/**
* Create or update a related record matching the attributes, and fill it with values.
*
* @param array $attributes
* @param array $values
* @return \Illuminate\Database\Eloquent\Model
*/
public function updateOrCreate(array $attributes, array $values = [])
{
$instance = $this->firstOrNew($attributes);
$instance->fill($values);
$instance->save();
return $instance;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function create(array $attributes)
{
// Here we will set the raw attributes to avoid hitting the "fill" method so
// that we do not have to worry about a mass accessor rules blocking sets
// on the models. Otherwise, some of these attributes will not get set.
$instance = $this->related->newInstance($attributes);
$instance->setAttribute($this->getPlainForeignKey(), $this->getParentKey());
$instance->save();
return $instance;
}
/**
* Create an array of new instances of the related model.
*
* @param array $records
* @return array
*/
public function createMany(array $records)
{
$instances = array();
foreach ($records as $record)
{
$instances[] = $this->create($record);
}
return $instances;
}
/**
* Perform an update on all the related models.
*
* @param array $attributes
* @return int
*/
public function update(array $attributes)
{
if ($this->related->usesTimestamps())
{
$attributes[$this->relatedUpdatedAt()] = $this->related->freshTimestampString();
}
return $this->query->update($attributes);
}
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getHasCompareKey()
{
return $this->getForeignKey();
}
/**
* Get the foreign key for the relationship.
*
* @return string
*/
public function getForeignKey()
{
return $this->foreignKey;
}
/**
* Get the plain foreign key.
*
* @return string
*/
public function getPlainForeignKey()
{
$segments = explode('.', $this->getForeignKey());
return $segments[count($segments) - 1];
}
/**
* Get the key value of the parent's local key.
*
* @return mixed
*/
public function getParentKey()
{
return $this->parent->getAttribute($this->localKey);
}
/**
* Get the fully qualified parent key name.
*
* @return string
*/
public function getQualifiedParentKeyName()
{
return $this->parent->getTable().'.'.$this->localKey;
}
}

View File

@ -0,0 +1,47 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Collection;
class MorphMany extends MorphOneOrMany {
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->query->get();
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, $this->related->newCollection());
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $this->matchMany($models, $results, $relation);
}
}

View File

@ -0,0 +1,47 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Collection;
class MorphOne extends MorphOneOrMany {
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->query->first();
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, null);
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $this->matchOne($models, $results, $relation);
}
}

View File

@ -0,0 +1,236 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
abstract class MorphOneOrMany extends HasOneOrMany {
/**
* The foreign key type for the relationship.
*
* @var string
*/
protected $morphType;
/**
* The class name of the parent model.
*
* @var string
*/
protected $morphClass;
/**
* Create a new morph one or many relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $type
* @param string $id
* @param string $localKey
* @return void
*/
public function __construct(Builder $query, Model $parent, $type, $id, $localKey)
{
$this->morphType = $type;
$this->morphClass = $parent->getMorphClass();
parent::__construct($query, $parent, $id, $localKey);
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
if (static::$constraints)
{
parent::addConstraints();
$this->query->where($this->morphType, $this->morphClass);
}
}
/**
* Get the relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$query = parent::getRelationCountQuery($query, $parent);
return $query->where($this->morphType, $this->morphClass);
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
parent::addEagerConstraints($models);
$this->query->where($this->morphType, $this->morphClass);
}
/**
* Attach a model instance to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model)
{
$model->setAttribute($this->getPlainMorphType(), $this->morphClass);
return parent::save($model);
}
/**
* Find a related model by its primary key or return new instance of the related model.
*
* @param mixed $id
* @param array $columns
* @return \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model
*/
public function findOrNew($id, $columns = ['*'])
{
if (is_null($instance = $this->find($id, $columns)))
{
$instance = $this->related->newInstance();
// When saving a polymorphic relationship, we need to set not only the foreign
// key, but also the foreign key type, which is typically the class name of
// the parent model. This makes the polymorphic item unique in the table.
$this->setForeignAttributesForCreate($instance);
}
return $instance;
}
/**
* Get the first related model record matching the attributes or instantiate it.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function firstOrNew(array $attributes)
{
if (is_null($instance = $this->where($attributes)->first()))
{
$instance = $this->related->newInstance();
// When saving a polymorphic relationship, we need to set not only the foreign
// key, but also the foreign key type, which is typically the class name of
// the parent model. This makes the polymorphic item unique in the table.
$this->setForeignAttributesForCreate($instance);
}
return $instance;
}
/**
* Get the first related record matching the attributes or create it.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function firstOrCreate(array $attributes)
{
if (is_null($instance = $this->where($attributes)->first()))
{
$instance = $this->create($attributes);
}
return $instance;
}
/**
* Create or update a related record matching the attributes, and fill it with values.
*
* @param array $attributes
* @param array $values
* @return \Illuminate\Database\Eloquent\Model
*/
public function updateOrCreate(array $attributes, array $values = [])
{
$instance = $this->firstOrNew($attributes);
$instance->fill($values);
$instance->save();
return $instance;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function create(array $attributes)
{
$instance = $this->related->newInstance($attributes);
// When saving a polymorphic relationship, we need to set not only the foreign
// key, but also the foreign key type, which is typically the class name of
// the parent model. This makes the polymorphic item unique in the table.
$this->setForeignAttributesForCreate($instance);
$instance->save();
return $instance;
}
/**
* Set the foreign ID and type for creating a related model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
protected function setForeignAttributesForCreate(Model $model)
{
$model->{$this->getPlainForeignKey()} = $this->getParentKey();
$model->{last(explode('.', $this->morphType))} = $this->morphClass;
}
/**
* Get the foreign key "type" name.
*
* @return string
*/
public function getMorphType()
{
return $this->morphType;
}
/**
* Get the plain morph type name without the table.
*
* @return string
*/
public function getPlainMorphType()
{
return last(explode('.', $this->morphType));
}
/**
* Get the class name of the parent model.
*
* @return string
*/
public function getMorphClass()
{
return $this->morphClass;
}
}

View File

@ -0,0 +1,78 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Builder;
class MorphPivot extends Pivot {
/**
* The type of the polymorphic relation.
*
* Explicitly define this so it's not included in saved attributes.
*
* @var string
*/
protected $morphType;
/**
* The value of the polymorphic relation.
*
* Explicitly define this so it's not included in saved attributes.
*
* @var string
*/
protected $morphClass;
/**
* Set the keys for a save update query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function setKeysForSaveQuery(Builder $query)
{
$query->where($this->morphType, $this->morphClass);
return parent::setKeysForSaveQuery($query);
}
/**
* Delete the pivot model record from the database.
*
* @return int
*/
public function delete()
{
$query = $this->getDeleteQuery();
$query->where($this->morphType, $this->morphClass);
return $query->delete();
}
/**
* Set the morph type for the pivot.
*
* @param string $morphType
* @return $this
*/
public function setMorphType($morphType)
{
$this->morphType = $morphType;
return $this;
}
/**
* Set the morph class for the pivot.
*
* @param string $morphClass
* @return \Illuminate\Database\Eloquent\Relations\MorphPivot
*/
public function setMorphClass($morphClass)
{
$this->morphClass = $morphClass;
return $this;
}
}

View File

@ -0,0 +1,247 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as BaseCollection;
class MorphTo extends BelongsTo {
/**
* The type of the polymorphic relation.
*
* @var string
*/
protected $morphType;
/**
* The models whose relations are being eager loaded.
*
* @var \Illuminate\Database\Eloquent\Collection
*/
protected $models;
/**
* All of the models keyed by ID.
*
* @var array
*/
protected $dictionary = array();
/*
* Indicates if soft-deleted model instances should be fetched.
*
* @var bool
*/
protected $withTrashed = false;
/**
* Create a new morph to relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $foreignKey
* @param string $otherKey
* @param string $type
* @param string $relation
* @return void
*/
public function __construct(Builder $query, Model $parent, $foreignKey, $otherKey, $type, $relation)
{
$this->morphType = $type;
parent::__construct($query, $parent, $foreignKey, $otherKey, $relation);
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
$this->buildDictionary($this->models = Collection::make($models));
}
/**
* Build a dictionary with the models.
*
* @param \Illuminate\Database\Eloquent\Collection $models
* @return void
*/
protected function buildDictionary(Collection $models)
{
foreach ($models as $model)
{
if ($model->{$this->morphType})
{
$this->dictionary[$model->{$this->morphType}][$model->{$this->foreignKey}][] = $model;
}
}
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $models;
}
/**
* Associate the model instance to the given parent.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function associate(Model $model)
{
$this->parent->setAttribute($this->foreignKey, $model->getKey());
$this->parent->setAttribute($this->morphType, $model->getMorphClass());
return $this->parent->setRelation($this->relation, $model);
}
/**
* Get the results of the relationship.
*
* Called via eager load method of Eloquent query builder.
*
* @return mixed
*/
public function getEager()
{
foreach (array_keys($this->dictionary) as $type)
{
$this->matchToMorphParents($type, $this->getResultsByType($type));
}
return $this->models;
}
/**
* Match the results for a given type to their parents.
*
* @param string $type
* @param \Illuminate\Database\Eloquent\Collection $results
* @return void
*/
protected function matchToMorphParents($type, Collection $results)
{
foreach ($results as $result)
{
if (isset($this->dictionary[$type][$result->getKey()]))
{
foreach ($this->dictionary[$type][$result->getKey()] as $model)
{
$model->setRelation($this->relation, $result);
}
}
}
}
/**
* Get all of the relation results for a type.
*
* @param string $type
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function getResultsByType($type)
{
$instance = $this->createModelByType($type);
$key = $instance->getKeyName();
$query = $instance->newQuery();
$query = $this->useWithTrashed($query);
return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
}
/**
* Gather all of the foreign keys for a given type.
*
* @param string $type
* @return array
*/
protected function gatherKeysByType($type)
{
$foreign = $this->foreignKey;
return BaseCollection::make($this->dictionary[$type])->map(function($models) use ($foreign)
{
return head($models)->{$foreign};
})->unique();
}
/**
* Create a new model instance by type.
*
* @param string $type
* @return \Illuminate\Database\Eloquent\Model
*/
public function createModelByType($type)
{
return new $type;
}
/**
* Get the foreign key "type" name.
*
* @return string
*/
public function getMorphType()
{
return $this->morphType;
}
/**
* Get the dictionary used by the relationship.
*
* @return array
*/
public function getDictionary()
{
return $this->dictionary;
}
/**
* Fetch soft-deleted model instances with query.
*
* @return $this
*/
public function withTrashed()
{
$this->withTrashed = true;
$this->query = $this->useWithTrashed($this->query);
return $this;
}
/**
* Return trashed models with query if told so.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function useWithTrashed(Builder $query)
{
if ($this->withTrashed && $query->getMacro('withTrashed') !== null)
{
return $query->withTrashed();
}
return $query;
}
}

View File

@ -0,0 +1,158 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class MorphToMany extends BelongsToMany {
/**
* The type of the polymorphic relation.
*
* @var string
*/
protected $morphType;
/**
* The class name of the morph type constraint.
*
* @var string
*/
protected $morphClass;
/**
* Indicates if we are connecting the inverse of the relation.
*
* This primarily affects the morphClass constraint.
*
* @var bool
*/
protected $inverse;
/**
* Create a new morph to many relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $name
* @param string $table
* @param string $foreignKey
* @param string $otherKey
* @param string $relationName
* @param bool $inverse
* @return void
*/
public function __construct(Builder $query, Model $parent, $name, $table, $foreignKey, $otherKey, $relationName = null, $inverse = false)
{
$this->inverse = $inverse;
$this->morphType = $name.'_type';
$this->morphClass = $inverse ? $query->getModel()->getMorphClass() : $parent->getMorphClass();
parent::__construct($query, $parent, $table, $foreignKey, $otherKey, $relationName);
}
/**
* Set the where clause for the relation query.
*
* @return $this
*/
protected function setWhere()
{
parent::setWhere();
$this->query->where($this->table.'.'.$this->morphType, $this->morphClass);
return $this;
}
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$query = parent::getRelationCountQuery($query, $parent);
return $query->where($this->table.'.'.$this->morphType, $this->morphClass);
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
parent::addEagerConstraints($models);
$this->query->where($this->table.'.'.$this->morphType, $this->morphClass);
}
/**
* Create a new pivot attachment record.
*
* @param int $id
* @param bool $timed
* @return array
*/
protected function createAttachRecord($id, $timed)
{
$record = parent::createAttachRecord($id, $timed);
return array_add($record, $this->morphType, $this->morphClass);
}
/**
* Create a new query builder for the pivot table.
*
* @return \Illuminate\Database\Query\Builder
*/
protected function newPivotQuery()
{
$query = parent::newPivotQuery();
return $query->where($this->morphType, $this->morphClass);
}
/**
* Create a new pivot model instance.
*
* @param array $attributes
* @param bool $exists
* @return \Illuminate\Database\Eloquent\Relations\Pivot
*/
public function newPivot(array $attributes = array(), $exists = false)
{
$pivot = new MorphPivot($this->parent, $attributes, $this->table, $exists);
$pivot->setPivotKeys($this->foreignKey, $this->otherKey)
->setMorphType($this->morphType)
->setMorphClass($this->morphClass);
return $pivot;
}
/**
* Get the foreign key "type" name.
*
* @return string
*/
public function getMorphType()
{
return $this->morphType;
}
/**
* Get the class name of the parent model.
*
* @return string
*/
public function getMorphClass()
{
return $this->morphClass;
}
}

View File

@ -0,0 +1,171 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class Pivot extends Model {
/**
* The parent model of the relationship.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $parent;
/**
* The name of the foreign key column.
*
* @var string
*/
protected $foreignKey;
/**
* The name of the "other key" column.
*
* @var string
*/
protected $otherKey;
/**
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = array();
/**
* Create a new pivot model instance.
*
* @param \Illuminate\Database\Eloquent\Model $parent
* @param array $attributes
* @param string $table
* @param bool $exists
* @return void
*/
public function __construct(Model $parent, $attributes, $table, $exists = false)
{
parent::__construct();
// The pivot model is a "dynamic" model since we will set the tables dynamically
// for the instance. This allows it work for any intermediate tables for the
// many to many relationship that are defined by this developer's classes.
$this->setRawAttributes($attributes, true);
$this->setTable($table);
$this->setConnection($parent->getConnectionName());
// We store off the parent instance so we will access the timestamp column names
// for the model, since the pivot model timestamps aren't easily configurable
// from the developer's point of view. We can use the parents to get these.
$this->parent = $parent;
$this->exists = $exists;
$this->timestamps = $this->hasTimestampAttributes();
}
/**
* Set the keys for a save update query.
*
* @param \Illuminate\Database\Eloquent\Builder
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function setKeysForSaveQuery(Builder $query)
{
$query->where($this->foreignKey, $this->getAttribute($this->foreignKey));
return $query->where($this->otherKey, $this->getAttribute($this->otherKey));
}
/**
* Delete the pivot model record from the database.
*
* @return int
*/
public function delete()
{
return $this->getDeleteQuery()->delete();
}
/**
* Get the query builder for a delete operation on the pivot.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function getDeleteQuery()
{
$foreign = $this->getAttribute($this->foreignKey);
$query = $this->newQuery()->where($this->foreignKey, $foreign);
return $query->where($this->otherKey, $this->getAttribute($this->otherKey));
}
/**
* Get the foreign key column name.
*
* @return string
*/
public function getForeignKey()
{
return $this->foreignKey;
}
/**
* Get the "other key" column name.
*
* @return string
*/
public function getOtherKey()
{
return $this->otherKey;
}
/**
* Set the key names for the pivot model instance.
*
* @param string $foreignKey
* @param string $otherKey
* @return $this
*/
public function setPivotKeys($foreignKey, $otherKey)
{
$this->foreignKey = $foreignKey;
$this->otherKey = $otherKey;
return $this;
}
/**
* Determine if the pivot model has timestamp attributes.
*
* @return bool
*/
public function hasTimestampAttributes()
{
return array_key_exists($this->getCreatedAtColumn(), $this->attributes);
}
/**
* Get the name of the "created at" column.
*
* @return string
*/
public function getCreatedAtColumn()
{
return $this->parent->getCreatedAtColumn();
}
/**
* Get the name of the "updated at" column.
*
* @return string
*/
public function getUpdatedAtColumn()
{
return $this->parent->getUpdatedAtColumn();
}
}

View File

@ -0,0 +1,290 @@
<?php namespace Illuminate\Database\Eloquent\Relations;
use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
abstract class Relation {
/**
* The Eloquent query builder instance.
*
* @var \Illuminate\Database\Eloquent\Builder
*/
protected $query;
/**
* The parent model instance.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $parent;
/**
* The related model instance.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $related;
/**
* Indicates if the relation is adding constraints.
*
* @var bool
*/
protected static $constraints = true;
/**
* Create a new relation instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @return void
*/
public function __construct(Builder $query, Model $parent)
{
$this->query = $query;
$this->parent = $parent;
$this->related = $query->getModel();
$this->addConstraints();
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
abstract public function addConstraints();
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
abstract public function addEagerConstraints(array $models);
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return array
*/
abstract public function initRelation(array $models, $relation);
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
abstract public function match(array $models, Collection $results, $relation);
/**
* Get the results of the relationship.
*
* @return mixed
*/
abstract public function getResults();
/**
* Get the relationship for eager loading.
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function getEager()
{
return $this->get();
}
/**
* Touch all of the related models for the relationship.
*
* @return void
*/
public function touch()
{
$column = $this->getRelated()->getUpdatedAtColumn();
$this->rawUpdate(array($column => $this->getRelated()->freshTimestampString()));
}
/**
* Run a raw update against the base query.
*
* @param array $attributes
* @return int
*/
public function rawUpdate(array $attributes = array())
{
return $this->query->update($attributes);
}
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$query->select(new Expression('count(*)'));
$key = $this->wrap($this->getQualifiedParentKeyName());
return $query->where($this->getHasCompareKey(), '=', new Expression($key));
}
/**
* Run a callback with constraints disabled on the relation.
*
* @param \Closure $callback
* @return mixed
*/
public static function noConstraints(Closure $callback)
{
$previous = static::$constraints;
static::$constraints = false;
// When resetting the relation where clause, we want to shift the first element
// off of the bindings, leaving only the constraints that the developers put
// as "extra" on the relationships, and not original relation constraints.
$results = call_user_func($callback);
static::$constraints = $previous;
return $results;
}
/**
* Get all of the primary keys for an array of models.
*
* @param array $models
* @param string $key
* @return array
*/
protected function getKeys(array $models, $key = null)
{
return array_unique(array_values(array_map(function($value) use ($key)
{
return $key ? $value->getAttribute($key) : $value->getKey();
}, $models)));
}
/**
* Get the underlying query for the relation.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getQuery()
{
return $this->query;
}
/**
* Get the base query builder driving the Eloquent builder.
*
* @return \Illuminate\Database\Query\Builder
*/
public function getBaseQuery()
{
return $this->query->getQuery();
}
/**
* Get the parent model of the relation.
*
* @return \Illuminate\Database\Eloquent\Model
*/
public function getParent()
{
return $this->parent;
}
/**
* Get the fully qualified parent key name.
*
* @return string
*/
public function getQualifiedParentKeyName()
{
return $this->parent->getQualifiedKeyName();
}
/**
* Get the related model of the relation.
*
* @return \Illuminate\Database\Eloquent\Model
*/
public function getRelated()
{
return $this->related;
}
/**
* Get the name of the "created at" column.
*
* @return string
*/
public function createdAt()
{
return $this->parent->getCreatedAtColumn();
}
/**
* Get the name of the "updated at" column.
*
* @return string
*/
public function updatedAt()
{
return $this->parent->getUpdatedAtColumn();
}
/**
* Get the name of the related model's "updated at" column.
*
* @return string
*/
public function relatedUpdatedAt()
{
return $this->related->getUpdatedAtColumn();
}
/**
* Wrap the given value with the parent query's grammar.
*
* @param string $value
* @return string
*/
public function wrap($value)
{
return $this->parent->newQueryWithoutScopes()->getQuery()->getGrammar()->wrap($value);
}
/**
* Handle dynamic method calls to the relationship.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
$result = call_user_func_array(array($this->query, $method), $parameters);
if ($result === $this->query) return $this;
return $result;
}
}

View File

@ -0,0 +1,24 @@
<?php namespace Illuminate\Database\Eloquent;
interface ScopeInterface {
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model);
/**
* Remove the scope from the given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return void
*/
public function remove(Builder $builder, Model $model);
}

View File

@ -0,0 +1,170 @@
<?php namespace Illuminate\Database\Eloquent;
trait SoftDeletes {
/**
* Indicates if the model is currently force deleting.
*
* @var bool
*/
protected $forceDeleting = false;
/**
* Boot the soft deleting trait for a model.
*
* @return void
*/
public static function bootSoftDeletes()
{
static::addGlobalScope(new SoftDeletingScope);
}
/**
* Force a hard delete on a soft deleted model.
*
* @return void
*/
public function forceDelete()
{
$this->forceDeleting = true;
$this->delete();
$this->forceDeleting = false;
}
/**
* Perform the actual delete query on this model instance.
*
* @return void
*/
protected function performDeleteOnModel()
{
if ($this->forceDeleting)
{
return $this->withTrashed()->where($this->getKeyName(), $this->getKey())->forceDelete();
}
return $this->runSoftDelete();
}
/**
* Perform the actual delete query on this model instance.
*
* @return void
*/
protected function runSoftDelete()
{
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
$this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();
$query->update(array($this->getDeletedAtColumn() => $this->fromDateTime($time)));
}
/**
* Restore a soft-deleted model instance.
*
* @return bool|null
*/
public function restore()
{
// If the restoring event does not return false, we will proceed with this
// restore operation. Otherwise, we bail out so the developer will stop
// the restore totally. We will clear the deleted timestamp and save.
if ($this->fireModelEvent('restoring') === false)
{
return false;
}
$this->{$this->getDeletedAtColumn()} = null;
// Once we have saved the model, we will fire the "restored" event so this
// developer will do anything they need to after a restore operation is
// totally finished. Then we will return the result of the save call.
$this->exists = true;
$result = $this->save();
$this->fireModelEvent('restored', false);
return $result;
}
/**
* Determine if the model instance has been soft-deleted.
*
* @return bool
*/
public function trashed()
{
return ! is_null($this->{$this->getDeletedAtColumn()});
}
/**
* Get a new query builder that includes soft deletes.
*
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public static function withTrashed()
{
return (new static)->newQueryWithoutScope(new SoftDeletingScope);
}
/**
* Get a new query builder that only includes soft deletes.
*
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public static function onlyTrashed()
{
$instance = new static;
$column = $instance->getQualifiedDeletedAtColumn();
return $instance->newQueryWithoutScope(new SoftDeletingScope)->whereNotNull($column);
}
/**
* Register a restoring model event with the dispatcher.
*
* @param \Closure|string $callback
* @return void
*/
public static function restoring($callback)
{
static::registerModelEvent('restoring', $callback);
}
/**
* Register a restored model event with the dispatcher.
*
* @param \Closure|string $callback
* @return void
*/
public static function restored($callback)
{
static::registerModelEvent('restored', $callback);
}
/**
* Get the name of the "deleted at" column.
*
* @return string
*/
public function getDeletedAtColumn()
{
return defined('static::DELETED_AT') ? static::DELETED_AT : 'deleted_at';
}
/**
* Get the fully qualified "deleted at" column.
*
* @return string
*/
public function getQualifiedDeletedAtColumn()
{
return $this->getTable().'.'.$this->getDeletedAtColumn();
}
}

View File

@ -0,0 +1,164 @@
<?php namespace Illuminate\Database\Eloquent;
class SoftDeletingScope implements ScopeInterface {
/**
* All of the extensions to be added to the builder.
*
* @var array
*/
protected $extensions = ['ForceDelete', 'Restore', 'WithTrashed', 'OnlyTrashed'];
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->whereNull($model->getQualifiedDeletedAtColumn());
$this->extend($builder);
}
/**
* Remove the scope from the given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function remove(Builder $builder, Model $model)
{
$column = $model->getQualifiedDeletedAtColumn();
$query = $builder->getQuery();
$query->wheres = collect($query->wheres)->reject(function($where) use ($column)
{
return $this->isSoftDeleteConstraint($where, $column);
})->values()->all();
}
/**
* Extend the query builder with the needed functions.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function extend(Builder $builder)
{
foreach ($this->extensions as $extension)
{
$this->{"add{$extension}"}($builder);
}
$builder->onDelete(function(Builder $builder)
{
$column = $this->getDeletedAtColumn($builder);
return $builder->update(array(
$column => $builder->getModel()->freshTimestampString(),
));
});
}
/**
* Get the "deleted at" column for the builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return string
*/
protected function getDeletedAtColumn(Builder $builder)
{
if (count($builder->getQuery()->joins) > 0)
{
return $builder->getModel()->getQualifiedDeletedAtColumn();
}
else
{
return $builder->getModel()->getDeletedAtColumn();
}
}
/**
* Add the force delete extension to the builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
protected function addForceDelete(Builder $builder)
{
$builder->macro('forceDelete', function(Builder $builder)
{
return $builder->getQuery()->delete();
});
}
/**
* Add the restore extension to the builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
protected function addRestore(Builder $builder)
{
$builder->macro('restore', function(Builder $builder)
{
$builder->withTrashed();
return $builder->update(array($builder->getModel()->getDeletedAtColumn() => null));
});
}
/**
* Add the with-trashed extension to the builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
protected function addWithTrashed(Builder $builder)
{
$builder->macro('withTrashed', function(Builder $builder)
{
$this->remove($builder, $builder->getModel());
return $builder;
});
}
/**
* Add the only-trashed extension to the builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
protected function addOnlyTrashed(Builder $builder)
{
$builder->macro('onlyTrashed', function(Builder $builder)
{
$model = $builder->getModel();
$this->remove($builder, $model);
$builder->getQuery()->whereNotNull($model->getQualifiedDeletedAtColumn());
return $builder;
});
}
/**
* Determine if the given where clause is a soft delete constraint.
*
* @param array $where
* @param string $column
* @return bool
*/
protected function isSoftDeleteConstraint(array $where, $column)
{
return $where['type'] == 'Null' && $where['column'] == $column;
}
}