/** * Get the relation instance for the given relation name. * * @param string $relation * * @return Relation * @throws BadRequestException */ public function getRelation($relation) { $query = Relation::noConstraints(function () use($relation) { /** @var BaseModel $model */ $model = $this->getModel(); $relationType = $model->getReferencingType($relation); if (RelationSchema::HAS_MANY === $relationType) { return $model->getHasManyByRelationName($relation); } elseif (RelationSchema::MANY_MANY === $relationType) { return $model->getBelongsToManyByRelationName($relation); } elseif (RelationSchema::BELONGS_TO === $relationType) { return $model->getBelongsToByRelationName($relation); } return null; }); if (!empty($query)) { return $query; } if (!method_exists($this->getModel(), $relation)) { throw new BadRequestException('Unknown relationship: ' . $relation); } return parent::getRelation($relation); }
/** * Get the "has relation" base query instance. * * @param string $relation * @return \Illuminate\Database\Eloquent\Builder */ protected function getHasRelationQuery($relation) { $me = $this; return Relation::noConstraints(function () use($me, $relation) { return $me->getModel()->{$relation}(); }); }
/** * Makes the form object used for rendering a simple field type */ protected function makeRenderFormField() { return $this->renderFormField = RelationBase::noConstraints(function () { $field = clone $this->formField; list($model, $attribute) = $this->getModelArrayAttribute($this->relationName); $relatedObj = $model->makeRelation($attribute); $query = $model->{$attribute}()->newQuery(); if (in_array($this->relationType, ['belongsToMany', 'morphToMany', 'morphedByMany', 'hasMany'])) { $field->type = 'checkboxlist'; } elseif (in_array($this->relationType, ['belongsTo', 'hasOne'])) { $field->type = 'dropdown'; $field->placeholder = $this->emptyOption; } // It is safe to assume that if the model and related model are of // the exact same class, then it cannot be related to itself if ($model->exists && get_class($model) == get_class($relatedObj)) { $query->where($relatedObj->getKeyName(), '<>', $model->getKey()); } // Even though "no constraints" is applied, belongsToMany constrains the query // by joining its pivot table. Remove all joins from the query. $query->getQuery()->getQuery()->joins = []; $treeTraits = ['October\\Rain\\Database\\Traits\\NestedTree', 'October\\Rain\\Database\\Traits\\SimpleTree']; if (count(array_intersect($treeTraits, class_uses($relatedObj))) > 0) { $field->options = $query->listsNested($this->nameFrom, $relatedObj->getKeyName()); } else { $field->options = $query->lists($this->nameFrom, $relatedObj->getKeyName()); } return $field; }); }
/** * Add a join clause to the query automatically using the informations of the relation * @param string $relations * @param string $type * @param string $operator * @param boolean $where * @return \Illuminate\Database\Query\Builder|static */ public function scopeJoinRelation($query, $relations, $type = 'inner', $operator = '=', $where = false) { $me = $this; $relations = (array) $relations; $joined = array(); $joinRelation = function ($model, $names, $constraints) use($query, &$joinRelation, &$joined, $type, $operator, $where) { $name = array_shift($names); $relation = \Illuminate\Database\Eloquent\Relations\Relation::noConstraints(function () use($model, $name) { return $model->{$name}(); }); $related = $relation->getRelated(); if (!in_array($name, $joined)) { $joined[] = $name; if (is_a($relation, '\\Illuminate\\Database\\Eloquent\\Relations\\HasOneOrMany')) { $one = $relation->getQualifiedParentKeyName(); $two = $relation->getForeignKey(); } if (is_a($relation, '\\Illuminate\\Database\\Eloquent\\Relations\\BelongsTo')) { $one = $relation->getQualifiedForeignKey(); $two = $relation->getQualifiedOtherKeyName(); } if (is_a($relation, '\\Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany')) { $query->join($relation->getTable(), $relation->getForeignKey(), '=', $relation->getParent()->getQualifiedKeyName()); $one = $relation->getRelated()->getQualifiedKeyName(); $two = $relation->getOtherKey(); } $query->join($related->getTable(), $one, $operator, $two, $type, $where); $relationQuery = $relation->getBaseQuery(); call_user_func($constraints, $query); $query->getQuery()->mergeWheres($relationQuery->wheres, $relationQuery->getBindings()); } if ($names) { $joinRelation($related, $names, $constraints); } }; foreach ($relations as $name => $constraints) { if (is_numeric($name)) { list($name, $constraints) = array($constraints, function () { }); } $joinRelation($me, explode('.', $name), $constraints); } return $query; }
public function getRelationRecordCount($relation, $relation_count, $use_main_table = true) { $this->with($relation); // substitutes the model with a Builder instance $model = $this->model; $query = Relation::noConstraints(function () use($model, $relation, $relation_count) { return $model->getRelation($relation_count)->groupBy($this->relations[$relation]['table'] . '.' . $this->relations[$relation]['foreignKey'])->orderBy($this->relations[$relation]['count'], 'desc'); }); if ($model instanceof Builder) { $model = $model->getModel(); } $table = $query->getQuery()->getModel()->getTable(); $query->setModel($this->model->getModel()); $query->getQuery()->getQuery()->from($table); if ($use_main_table) { $query->select(['main.*']); } foreach ($this->relations[$relation]['fields'] as $field => $name) { $query->selectRaw($field . ' as ' . $name); } if ($use_main_table) { $query->join($model->getTable() . ' as main', 'main.id', '=', $this->relations[$relation]['table'] . '.' . $this->relations[$relation]['foreignKey']); } // It is necessary to return the previous state of the model after // using "$this->with()", which substitutes the model with a Builder // instance. $this->resetModel(); return $query; }
/** * Get the relation instance for the given relation name. * * @param string $relation * @return \Illuminate\Database\Eloquent\Relations\Relation */ public function getRelation($relation) { $me = $this; // 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($me, $relation) { return $me->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; }
public function getRelationRecordCount($relation, $relation_count, $use_main_table = true) { $this->with($relation); $model = $this->model; $query = Relation::noConstraints(function () use($model, $relation, $relation_count) { return $model->getRelation($relation_count)->groupBy($this->relations[$relation]['table'] . '.' . $this->relations[$relation]['foreignKey'])->orderBy($this->relations[$relation]['count'], 'desc'); }); if ($model instanceof Builder) { $model = $model->getModel(); } $table = $query->getQuery()->getModel()->getTable(); $query->setModel($this->model->getModel()); $query->getQuery()->getQuery()->from($table); if ($use_main_table) { $query->select(['main.*']); } foreach ($this->relations[$relation]['fields'] as $field => $name) { $query->selectRaw($field . ' as ' . $name); } if ($use_main_table) { $query->join($model->getTable() . ' as main', 'main.id', '=', $this->relations[$relation]['table'] . '.' . $this->relations[$relation]['foreignKey']); } return $query; }
/** * Makes the form object used for rendering a simple field type */ protected function makeRenderFormField() { return $this->renderFormField = RelationBase::noConstraints(function () { $field = clone $this->formField; $relationObject = $this->getRelationObject(); $query = $relationObject->newQuery(); list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom); $relationType = $model->getRelationType($attribute); $relationModel = $model->makeRelation($attribute); if (in_array($relationType, ['belongsToMany', 'morphToMany', 'morphedByMany', 'hasMany'])) { $field->type = 'checkboxlist'; } elseif (in_array($relationType, ['belongsTo', 'hasOne'])) { $field->type = 'dropdown'; } $field->placeholder = $this->emptyOption; // It is safe to assume that if the model and related model are of // the exact same class, then it cannot be related to itself if ($model->exists && get_class($model) == get_class($relationModel)) { $query->where($relationModel->getKeyName(), '<>', $model->getKey()); } // Even though "no constraints" is applied, belongsToMany constrains the query // by joining its pivot table. Remove all joins from the query. $query->getQuery()->getQuery()->joins = []; // Determine if the model uses a tree trait $treeTraits = ['October\\Rain\\Database\\Traits\\NestedTree', 'October\\Rain\\Database\\Traits\\SimpleTree']; $usesTree = count(array_intersect($treeTraits, class_uses($relationModel))) > 0; // The "sqlSelect" config takes precedence over "nameFrom". // A virtual column called "selection" will contain the result. // Tree models must select all columns to return parent columns, etc. if ($this->sqlSelect) { $nameFrom = 'selection'; $selectColumn = $usesTree ? '*' : $relationModel->getKeyName(); $result = $query->select($selectColumn, Db::raw($this->sqlSelect . ' AS ' . $nameFrom)); } else { $nameFrom = $this->nameFrom; $result = $query->getQuery()->get(); } $field->options = $usesTree ? $result->listsNested($nameFrom, $relationModel->getKeyName()) : $result->lists($nameFrom, $relationModel->getKeyName()); return $field; }); }