Skip to content

Instantly share code, notes, and snippets.

@ChangePlaces
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChangePlaces/8231b4e1d4d76562c337 to your computer and use it in GitHub Desktop.
Save ChangePlaces/8231b4e1d4d76562c337 to your computer and use it in GitHub Desktop.
//
// 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