Last active
August 29, 2015 14:22
-
-
Save ChangePlaces/8231b4e1d4d76562c337 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// method for a base Model class | |
// | |
/** | |
* Define a has-many-through-pivot relationship. | |
* | |
* @param $related related desired model | |
* @param $through middle model | |
* @param $firstRelation the many-to-many relation we use on the through model | |
* @param $secondRelation the second many-to-many relation on the through table which references farparent | |
* @param $firstKey key which references related model in first relation table | |
* @param $secondKey key which references through model in both pivot tables (must be same) | |
* @param $thirdKey key which references farParent in second relation pivot table | |
* @param bool $distinct whether to distinc the results | |
* @return \App\HasManyThroughPivot | |
*/ | |
public function hasManyThroughPivot($related, $through, $firstRelation, $secondRelation, $firstKey, $secondKey, $thirdKey, $distinct) | |
{ | |
$through = new $through; | |
$related = new $related; | |
return new \App\HasManyThroughPivot($this, $related, $through, $firstRelation, $secondRelation, $firstKey, $secondKey, $thirdKey, $distinct); | |
} | |
// ----------------------------------------------- | |
// | |
// HasManyThroughPivot class file | |
// | |
namespace App; | |
//namespace Illuminate\Database\Eloquent\Relations; | |
use 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 HasManyThroughPivot extends Relations\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; | |
protected $parent; // aka $through | |
protected $relatedTable; // table of the desired model | |
// protected $through; | |
protected $pivotThroughRelationTable; // pivot table of first relation | |
protected $pivotToSelfTable; // pivot table of second table which links to current $farParent model | |
protected $distinct = true; // prevents duplicates | |
protected $firstKey; // first key to use for first relation to determine $related id | |
protected $secondKey; // second key to use for second relation to find id of through model | |
protected $thirdKey; // key to self in second relation | |
/** | |
* @param Model $farParent home model ($this from calling model) | |
* @param Model $related desired model | |
* @param Model $through the middle model | |
* @param $firstRelation the many-to-many relation we use on the through model | |
* @param $secondRelation the second many-to-many relation on the through table which references farparent | |
* @param $firstKey key which references related model in first relation table | |
* @param $secondKey key which references through model in both pivot tables (must be same) | |
* @param $thirdKey key which references farParent in second relation pivot table | |
* @param bool $distinct | |
*/ | |
public function __construct(Model $farParent, Model $related, Model $through, $firstRelation, $secondRelation, $firstKey, $secondKey, $thirdKey, $distinct = true ) | |
{ | |
$query = $related->newQuery(); | |
$this->firstKey = $firstKey; | |
$this->secondKey = $secondKey; | |
$this->thirdKey = $thirdKey; | |
$this->parent = $through; | |
$this->farParent = $farParent; | |
$this->relatedTable = $related->getTable(); | |
$this->pivotThroughRelationTable = $through->$firstRelation()->getTable(); | |
$this->pivotToSelfTable = $through->$secondRelation()->getTable(); | |
$this->distinct = $distinct; | |
parent::__construct($query, $through); | |
} | |
/** | |
* Set the base constraints on the relation query. | |
* | |
* @return void | |
*/ | |
public function addConstraints() | |
{ | |
$this->setJoin(); | |
if (static::$constraints) | |
{ | |
$this->query->where($this->pivotToSelfTable.'.'.$this->thirdKey, '=', $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) | |
{ | |
die ("todo 2"); | |
$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; | |
if ($this->distinct) | |
{ | |
$query->distinct(); | |
} | |
$query | |
->join($this->pivotThroughRelationTable, $this->pivotThroughRelationTable . '.' . $this->firstKey, '=', $this->relatedTable . '.id') | |
->join($this->pivotToSelfTable, $this->pivotToSelfTable. '.' . $this->secondKey, '=', $this->pivotThroughRelationTable . '.' . $this->secondKey); | |
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) | |
{ | |
die ("todo 5"); | |
$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) | |
{ | |
die ("todo 5.5"); | |
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) | |
{ | |
die ("todo 6"); | |
$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) | |
{ | |
die ("todo 7"); | |
$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 = ['*']) | |
{ | |
die ("todo 10"); | |
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 = ['*']) | |
{ | |
die ("todo 11"); | |
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 = ['*']) | |
{ | |
// die ("todo 12"); | |
// 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 array | |
*/ | |
protected function getSelectColumns(array $columns = ['*']) | |
{ | |
if ($columns == ['*']) | |
{ | |
$columns = [$this->related->getTable().'.*']; | |
} | |
return $columns; | |
// 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 = ['*']) | |
{ | |
die ("todo 14"); | |
$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 = ['*']) | |
{ | |
die ("todo 15"); | |
$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() | |
{ | |
die ("todo 16"); | |
return $this->farParent->getQualifiedKeyName(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment