Retrieves grid columns configuration using the modelClass.
@param Model $model
@param string $fields
@param string $relationName
@param \yii\db\ActiveQuery $relation
@return array grid columns
198493
public static function getRelationGridColumns($model, $fields, $relationName, $relation)
    {
        $columns = parent::getRelationGridColumns($model, $fields, $relationName, $relation);
        $controller = Yii::$app->crudModelsMap[$model::className()];
        $actionColumn = new \yii\grid\ActionColumn();

        if (!isset($columns[0]) || !isset($columns[0]['class']) || $columns[0]['class'] !== 'yii\grid\ActionColumn') {
            array_unshift($columns, [
                'class'         => 'yii\grid\ActionColumn',
                'headerOptions' => ['class' => 'column-action'],
                'controller'    => $controller,
                'template'      => '',
                'buttons'       => [],
            ]);
        }

        $columns[0]['template'] = '{update} {unlink}';
        $columns[0]['urlCreator'] = function ($action, $model, $key, $index) use ($controller, $relation) {
            $params = is_array($key) ? $key : ['id' => (string)$key];
            if ($action === 'update') {
                $params['hide'] = implode(',', array_keys($relation->link));
            }
            $params[0] = $controller . '/' . $action;

            return Url::toRoute($params);
        };
        $columns[0]['buttons']['update'] = function ($url, $model, $key) use ($actionColumn, $relationName) {
            /** @var \netis\crud\db\ActiveRecord $model */
            if (!Yii::$app->user->can($model::className() . '.update', ['model' => $model])) {
                return null;
            }

            $options = array_merge([
                'title'         => Yii::t('app', 'Update'),
                'aria-label'    => Yii::t('app', 'Update'),
                'data-pjax'     => '0',
                // required for editing in a modal
                'data-toggle'   => 'modal',
                'data-target'   => '#relationModal',
                'data-relation' => $relationName,
                'data-title'    => $model->getCrudLabel('update'),
                'data-pjax-url' => $url,
                'class'         => 'btn btn-default btn-xs',
            ], $actionColumn->buttonOptions);
            return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, $options);
        };

        /** @var \yii\db\ActiveQuery $relation */
        $remove = false;
        $modelClass = $relation->modelClass;
        foreach (array_keys($relation->link) as $foreignKey) {
            $remove = $remove || !$modelClass::getTableSchema()->getColumn($foreignKey)->allowNull;
        }

        $columns[0]['buttons']['unlink'] = function ($url, $model, $key) use ($actionColumn, $remove) {
            /** @var \netis\crud\db\ActiveRecord $model */
            if (!Yii::$app->user->can($model::className() . ($remove ? '.delete' : '.update'), ['model' => $model])) {
                return null;
            }

            $options = array_merge([
                'title'      => Yii::t('app', 'Unlink'),
                'aria-label' => Yii::t('app', 'Unlink'),
                //'data-confirm' => Yii::t('app', 'Are you sure you want to unlink this item?'),
                'data-pjax'  => '0',
                'class'      => 'remove btn btn-default btn-xs',
            ], $actionColumn->buttonOptions);
            return Html::a('<span class="glyphicon glyphicon-remove"></span>', $url, $options);
        };
        return $columns;
    }
628502
protected static function getModelsForRelation($model, $relationKey)
    {
        if (!method_exists($model, $relationKey)) {
            throw new Exception(
                    'Relation "' . $relationKey . '" does not exist in model',
                    static::ERROR_SCOPE | static::ERROR_UNKNOWN_ID,
                    BaseResponse::HTTP_INTERNAL_SERVER_ERROR
                );
        }
        $relationModels = $model->{$relationKey};
        if (is_null($relationModels)) {
            return null;
        }

        if (! $relationModels instanceof Collection) {
            return [ $relationModels ];
        }

        return $relationModels;
    }
1254310
protected function decideRelationColumns($relationName)
    {
        // https://stackoverflow.com/a/25472778/3113599
        $relationType = (new \ReflectionClass($this->modelObject->$relationName()))->getShortName();
        $selectColumns = [];

        switch ($relationType) {
            case 'BelongsTo':
                // Laravel 5.8 renamed getForeignKey() to getForeignKeyName()
                $methodName = method_exists($this->modelObject->$relationName(), 'getForeignKeyName') ?
                    'getForeignKeyName' :
                    'getForeignKey'
                ;

                $selectColumns[] = $this->modelObject->$relationName()->{$methodName}();
                break;
            case 'MorphTo':
                $selectColumns[] = $this->modelObject->$relationName()->getForeignKey();
                $selectColumns[] = $this->modelObject->$relationName()->getMorphType();
                break;
        }

        return $selectColumns;
    }
382274
public static function findRelations($modelOne, $modelTwo, $keys): array
    {
        $keys = (array)$keys;
        if (!$relations = self::getRelations($modelOne, $modelTwo)) {
            throw new RelationNotFoundException("Relations between model `$modelOne` and `$modelTwo` is not defined");
        }

        /* @var Table $tableOneClass name */
        $tableOneClass = self::getModelClass($modelOne);

        /* @var string $tableOneName */
        $tableOneName = $tableOneClass::getInstance()->getName();

        /* @var Table $tableTwoClass name */
        $tableTwoClass = self::getModelClass($modelTwo);

        /* @var string $tableTwoName */
        $tableTwoName = $tableTwoClass::getInstance()->getName();

        /* @var Query\Select $tableTwoSelect */
        $tableTwoSelect = $tableTwoClass::getInstance()::select();

        // check many to many relation
        if (\is_int(\array_keys($relations)[0])) {
            // many to many relation over third table
            $modelThree = $relations[0];

            // relations between target table and third table
            $relations = self::getRelations($modelTwo, $modelThree);

            /* @var Table $tableThreeClass name */
            $tableThreeClass = self::getModelClass($modelThree);

            /* @var string $tableTwoName */
            $tableThreeName = $tableThreeClass::getInstance()->getName();

            // join it to query
            $tableTwoSelect->join(
                $tableTwoName,
                $tableThreeName,
                $tableThreeName,
                $tableTwoName . '.' . $relations[$modelTwo] . '=' . $tableThreeName . '.' . $relations[$modelThree]
            );

            // relations between source table and third table
            $relations = self::getRelations($modelOne, $modelThree);

            // join it to query
            $tableTwoSelect->join(
                $tableThreeName,
                $tableOneName,
                $tableOneName,
                $tableThreeName . '.' . $relations[$modelThree] . '=' . $tableOneName . '.' . $relations[$modelOne]
            );

            // set source keys
            $tableTwoSelect->where($tableOneName . '.' . $relations[$modelOne] . ' IN (?)', $keys);
        } else {
            // set source keys
            $tableTwoSelect->where($relations[$modelTwo] . ' IN (?)', $keys);
        }
        return $tableTwoSelect->execute();
    }
54453
public function relationList($relationName, $index = 'id', $name = null)
    {
        $model = $this->owner;
        /** @var ActiveRecord $class */
        $class = $model->getRelation($relationName)->modelClass;
        $item = new $class();
        /** @var \yii\db\ActiveQuery $relation */
        $relation = method_exists($item, 'search')
            ? $item->search([])
            : $class::find();
        $name = $name ? : static::getNameAttribute($model, $relationName);
        $result = $relation->select($name)
            ->orderBy($name)
            ->indexBy($index)
            ->column();
        return $result;
    }
1190334
public function getRelationalDataAndModels($model, $desired_relation)
    {
        // $relations = $model->getRelations();
        $relation = $this->trimCommonRelationEndings($desired_relation);
        // Grab all the model relationships that don't return as a collection
        if (method_exists($model, $relation)) {
            // We have our relation, return it
            return $this->getResolvedRelationship($model, $relation);
        } elseif (method_exists($model, $relation.'s')) {
            // We have our relations, return it
            return $this->getResolvedRelationship($model, $relation.'s');
        } elseif (method_exists($model, $relation.'s')) {
            return $this->getResolvedRelationship($model, $relation.'s');
        }
        // don't return because clearly, nothing matches what we need.
    }
919139
protected function getRelatedModels(Model $model)
    {
        // Store the class name
        $modelClass = get_class($model);

        // Check if we already know the related models for this model
        if (!isset($this->relatedModels[$modelClass]))
        {
            $relatedModels = [];

            $methods = with(new \ReflectionClass($model))->getMethods();

            // Iterate all class methods
            foreach ($methods as $method)
            {
                // Check if this method returns an Eloquent relation
                if ($method->class == $modelClass &&
                    preg_match('/@return\s+\\\\Illuminate\\\\Database\\\\Eloquent\\\\Relations/', $method->getDocComment())
                )
                {
                    // Get the method name, so that we can call it on the model
                    $relationMethod = $method->name;

                    // Find the relation
                    $relation = $model->$relationMethod();

                    if ($relation instanceof Relation)
                    {
                        // Find the related model
                        $related = $relation->getRelated();

                        // Store the method to help build the inverse path
                        $this->relationClassMethods[$modelClass][get_class($related)] = $relationMethod;

                        $relatedModels[] = ['model' => $related, 'method' => $method];
                    }
                }
            }

            // Cache related models for this model
            $this->relatedModels[$modelClass] = $relatedModels;

            // Return the related models
            return $relatedModels;
        } else
        {
            // Return from cache
            return $this->relatedModels[$modelClass];
        }
    }
760097
public static function getModelColumns(string $model = '', string $connectionManager = 'default'): array
    {
        $result = $columns = [];

        if (empty($model)) {
            return $result;
        }

        // making sure that model is in table naming conventions.
        $model = Inflector::tableize($model);

        try {
            $connection = ConnectionManager::get($connectionManager);
            $schema = $connection->getSchemaCollection();
            $table = $schema->describe($model);
            $columns = $table->columns();
        } catch (MissingDatasourceConfigException $e) {
            return $result;
        } catch (Exception $e) {
            return $result;
        }

        // A table with no columns?
        if (empty($columns)) {
            return $result;
        }

        foreach ($columns as $column) {
            $result[$column] = $column;
        }

        return $result;
    }
649894
private function _getJunctionTableColumns($relationName, $model)
    {
        $junctionTableColumns = [];
        if (array_key_exists($relationName, $this->_relationsExtraColumns)) {
            if (is_callable($this->_relationsExtraColumns[$relationName])) {
                $junctionTableColumns = $this->_relationsExtraColumns[$relationName]($model);
            } elseif (is_array($this->_relationsExtraColumns[$relationName])) {
                $junctionTableColumns = $this->_relationsExtraColumns[$relationName];
            }
            if (!is_array($junctionTableColumns)) {
                throw new RuntimeException(
                    'Junction table columns definition must return an array, got ' . gettype($junctionTableColumns)
                );
            }
        }
        return $junctionTableColumns;
    }
996468
protected function getRelationMethods(array $relations, array $fields)
    {
        $methods = [];

        foreach ($fields as $field) {
            if ($field->hasForeignRelation()) {
                $relation = $field->getForeignRelation();
                $methods[$relation->name] = $this->getRelationshipMethod($relation);
            }
        }

        foreach ($relations as $relation) {
            $methods[$relation->name] = $this->getRelationshipMethod($relation);
        }

        return $methods;
    }
866813
public function relation($relationName) {
		if(!$this->dataMapper->hasRelation($this->definition, $relationName))
			throw new \Exception('Relation '.$relationName.' does not exist.');
		$relation = $this->dataMapper->relation($this->definition, $relationName);
		$reverseRelation = $relation->reverse();
		$reverseRelationName = $reverseRelation->get('name');
		$relation_entity = $relation->get('entity');

		$table = $this->getTable();
		$alias = $reverseRelationName;

		$c = clone $this;
		$c->applyScopes();

		$where = $this->processConditions($c->getWhere());
		$where = $this->updateConditions($where, $table, $alias);

		return $this->dataMapper->orm($relation_entity)
			->where($where)
			->join('innerjoin '.$reverseRelationName, $this->join);
	}
744701
protected function getNestedRelation(Model $model, $relationArray)
    {
        $relation = null;
        foreach ($relationArray as $relation) {
            /** @var Relation $relation */
            $relation = $model->newQuery()->getRelation($relation);
            if ($relation) {
                $model = $relation->getRelated();
            }
        }

        return $relation;
    }
194349
public function getRelation($modelName): ?RowInterface
    {
        $relations = $this->getRelations($modelName);
        return empty($relations) ? null : current($relations);
    }
94032
public function getRelationDictionary(string $relationName): array
    {
        return $this->models
            ->mapWithKeys(
                function (Model $model) use ($relationName) {
                    return [$this->buildKey($model->getKey()) => $model->getRelation($relationName)];
                }
            )->all();
    }
1372426
public function match(array $models, Collection $results, $relation)
    {
        // We will need the parent node placeholder so that we use it to extract related results.
        $parent = $this->query->getQuery()->modelAsNode($this->parent->getTable());

        /**
         * Looping into all the parents to match back onto their children using
         * the primary key to map them onto the correct instances, every single
         * result will be having both instances at each Collection item, held by their
         * node placeholder.
         */
        foreach ($models as $model)
        {
            $matched = $results->filter(function($result) use($parent, $model)
            {
                if ($result[$parent] instanceof Model)
                {
                    // In the case of fetching nested relations, we will get an array
                    // with the first key being the model we need, and the other being
                    // the related model so we'll just take the first model out of the array.
                    if (is_array($model)) $model = reset($model);

                    return $model->getKey() == $result[$parent]->getKey();
                }
            });

            // Now that we have the matched parents we know where to add the relations.
            // Sometimes we have more than a match so we gotta catch them all!
            foreach ($matched as $match)
            {
                // In the case of fetching nested relations, we will get an array
                // with the first key being the model we need, and the other being
                // the related model so we'll just take the first model out of the array.
                if (is_array($model)) $model = reset($model);

                $model->setRelation($relation, $match[$relation]);
            }
        }

        return $models;
    }
686597
public function initRelation(array $models, $relation)
    {
        foreach ($models as $model)
        {
            // In the case of fetching nested relations, we will get an array
            // with the first key being the model we need, and the other being
            // the related model so we'll just take the first model out of the array.
            if (is_array($model)) $model = reset($model);

            $model->setRelation($relation, null);
        }

        return $models;
    }
128272
public function getRelations($modelName): array
    {
        if (!isset($this->relations[$modelName])) {
            $this->relations[$modelName] = Relations::findRelation($this, $modelName);
        }

        return $this->relations[$modelName];
    }