Get a list of valid containable model relations.
Should also get belongsTo data for hasOne and hasMany.

@param Model $model
@param bool $extended
@return array
323819
public function getDeepRelations(Model $model, $extended = true) {
        $contain = array_keys($model->belongsTo);
        $contain = array_merge($contain, array_keys($model->hasAndBelongsToMany));

        if ($extended) {
            foreach (array($model->hasOne, $model->hasMany) as $assocs) {
                foreach ($assocs as $alias => $assoc) {
                    $contain[$alias] = array_keys($model->{$alias}->belongsTo);
                }
            }
        }

        return $contain;
    }
931450
protected function relations($model, $foreignKeyIds)
    {
        $relations = $model->getSoftCascade();

        if (empty($relations)) {
            return;
        }

        foreach ($relations as $relation) {
            extract($this->relationResolver($relation));
            $this->validateRelation($model, $relation);

            $modelRelation = $model->$relation();

            /**
             * Maintains compatibility fot get foreign key name on laravel old and new methods.
             *
             * @link https://github.com/laravel/framework/issues/20869
             */
            $fnUseGetForeignKey = array_intersect($this->fnGetForeignKey, get_class_methods($modelRelation));
            $fnUseGetForeignKey = reset($fnUseGetForeignKey);

            //Get foreign key and foreign key ids
            $foreignKeyUse = $modelRelation->{$fnUseGetForeignKey}();
            $foreignKeyIdsUse = $foreignKeyIds;

            //Many to many relations need to get related ids and related local key
            if ($modelRelation instanceof BelongsToMany) {
                extract($this->getBelongsToManyData($modelRelation, $foreignKeyUse, $foreignKeyIds));
            } elseif ($modelRelation instanceof MorphOneOrMany) {
                extract($this->getMorphManyData($modelRelation, $foreignKeyIds));
            }

            $affectedRows = $this->affectedRows($modelRelation, $foreignKeyUse, $foreignKeyIdsUse);

            if ($action === 'restrict' && $affectedRows > 0) {
                DB::rollBack(); //Rollback the transaction before throw exception
                throw (new SoftCascadeRestrictedException())->setModel(get_class($modelRelation->getModel()), $foreignKeyUse, $foreignKeyIdsUse->toArray());
            }

            $this->execute($modelRelation, $foreignKeyUse, $foreignKeyIdsUse, $affectedRows);
        }
    }
267113
public function getIndexGridColumns($model, $fields)
    {
        $id = Yii::$app->request->getQueryParam('id');
        $relationName = Yii::$app->request->getQueryParam('relation');
        $multiple = Yii::$app->request->getQueryParam('multiple', 'true') === 'true';
        foreach ($fields as $key => $field) {
            if (((is_array($field) || (!is_string($field) && is_callable($field))) && $key === $relationName)
                || $field === $relationName
            ) {
                unset($fields[$key]);
            }
        }
        return array_merge([
            [
                'class'         => 'yii\grid\CheckboxColumn',
                'multiple'      => $multiple,
                'headerOptions' => ['class' => 'column-serial'],
                'checkboxOptions' => function ($model, $key, $index, $column) use ($id, $relationName) {
                    /** @var \yii\db\ActiveRecord $model */
                    $options = [
                        'value' => is_array($key)
                            ? json_encode($key, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
                            : $key,
                    ];
                    if (empty($relationName) || $model->$relationName === null || trim($id) === '') {
                        return $options;
                    }
                    $relation = $model->getRelation($relationName);
                    if ($relation->multiple) {
                        /** @var \yii\db\ActiveRecord $relationClass */
                        $relationClass = $relation->modelClass;
                        if (Action::importKey($relationClass::primaryKey(), $id)
                            === $model->getAttributes(array_keys($relation->link))
                        ) {
                            $options['checked']  = true;
                            $options['disabled'] = true;
                        }
                    } elseif (Action::exportKey($relation->one()->getPrimaryKey()) === $id) {
                        $options['checked']  = true;
                        $options['disabled'] = true;
                    }
                    return $options;
                },
            ],
            [
                'class'         => 'yii\grid\SerialColumn',
                'headerOptions' => ['class' => 'column-serial'],
            ],
        ], self::getGridColumns($model, $fields));
    }
4892
public static function getAllOneToOneRelationshipsForModelBySourceColumnName($modelClassName)
    {
        $relationships = self::getAllRelationshipsForModel($modelClassName);
        $columnRelationships = [];

        foreach ($relationships as $relationship) {
            if ($relationship instanceof OneToOne) {
                $columnName = $relationship->getSourceColumnName();

                $columnRelationships[$columnName] = $relationship;
            }
        }

        return $columnRelationships;
    }
859542
public function getConstraints()
    {
        if ($this->relation instanceof HasMany) {
            return [
                $this->relation->getForeignKeyName() => $this->relation->getParentKey(),
            ];
        }

        return false;
    }
1051706
public function extension(Model $model, $data, array $allowed = array()) {
        return $this->_validate($model, $data, 'ext', array($allowed));
    }
1199157
public function getJointParent($model, $includeSelf = false)
    {
        if ( is_numeric($model) ) {
            $model = call_user_func("$this->ownerClassName::findOne", $model);
        }
        if ( $this->owner->{$this->parentIdField} == $model->{$this->parentIdField} ) {
            return $this->getParent();
        }
        $ownerParents = $this->owner->getParents($includeSelf)->all();
        $modelParents = $model->getParents($includeSelf)->all();
        $jointParent = false;
        foreach ( $ownerParents as $idx => $parent ) {
            if ( isset($modelParents[$idx]) && $parent->{$this->primaryKey} == $modelParents[$idx]->{$this->primaryKey} ) {
                $jointParent = $parent;
            } else {
                break;
            }
        }
        return $jointParent;
    }
681889
protected function loadRelatedModels(Model $model) {
        // get the relations to load
        $relations = $this->exposedRelationsFromRequest($model);

        foreach ($relations as $relation) {
            // if this relation is loaded via a method, then call said method
            if (in_array($relation, $model->relationsFromMethod())) {
                $model->$relation = $model->$relation();
                continue;
            }

            $model->load($relation);
        }
    }
308601
private function isRelationship($model, $conditions)
    {
        /**
         * A valid relationship must:
         * - Be a method
         * - Not appear in the 'except' list
         * - Appear in the 'only' list (if used)
         * - Extend Illuminate\Database\Eloquent\Relations\Relation
         */
        return (
            method_exists($this, $model) &&
            $this->filterRelationshipByConditions($model, $conditions) &&
            is_subclass_of($this->{Str::camel($model)}(), 'Illuminate\Database\Eloquent\Relations\Relation')
        );
    }
128272
public function getRelations($modelName): array
    {
        if (!isset($this->relations[$modelName])) {
            $this->relations[$modelName] = Relations::findRelation($this, $modelName);
        }

        return $this->relations[$modelName];
    }
1132275
private function findAffectedModels(Model $model, $excludeCurrent = false)
    {
        // Temporary array to store affected models
        $affectedModels = [];

        $paths = Config::get('larasearch.reversedPaths.' . get_class($model), []);

        foreach ((array)$paths as $path)
        {
            if (!empty($path))
            {
                $model = $model->load($path);

                // Explode the path into an array
                $path = explode('.', $path);

                // Define a little recursive function to walk the relations of the model based on the path
                // Eventually it will queue all affected searchable models for reindexing
                $walk = function ($relation, array $path) use (&$walk, &$affectedModels)
                {
                    $segment = array_shift($path);

                    $relation = $relation instanceof Collection ? $relation : new Collection([$relation]);

                    foreach ($relation as $record)
                    {
                        if ($record instanceof Model)
                        {
                            if (!empty($segment))
                            {
                                if (array_key_exists($segment, $record->getRelations()))
                                {
                                    $walk($record->getRelation($segment), $path);
                                } else
                                {
                                    // Apparently the relation doesn't exist on this model, so skip the rest of the path as well
                                    return;
                                }
                            } else
                            {
                                if (in_array('Iverberk\Larasearch\Traits\SearchableTrait', class_uses($record)))
                                {
                                    $affectedModels[] = get_class($record) . ':' . $record->getKey();
                                }
                            }
                        }
                    }
                };

                $walk($model->getRelation(array_shift($path)), $path);
            } else if (!$excludeCurrent)
            {
                if (in_array('Iverberk\Larasearch\Traits\SearchableTrait', class_uses($model)))
                {
                    $affectedModels[] = get_class($model) . ':' . $model->getKey();
                }
            }
        }

        return array_unique($affectedModels);
    }
835194
public function retrieve(Model $model, $source)
    {
        $data = [
            'longitude' => null,
            'latitude'  => null,
            'location'  => null,
        ];

        if ($latitude = $this->getAttributeLatitude()) {
            $data['latitude'] = (float) parent::retrieve($model, $latitude);
        }

        if ($longitude = $this->getAttributeLongitude()) {
            $data['longitude'] = (float) parent::retrieve($model, $longitude);
        }

        if ($text = $this->getAttributeText()) {
            $data['location'] = parent::retrieve($model, $text);
        }

        return $data;
    }
52687
protected function saveColumns(Model $model)
    {
        /** @var \T4\Orm\Model $class */
        $class = get_class($model);
        $columns = $class::getColumns();
        $relations = $class::getRelations();
        $cols = [];
        $prep = [];
        $sets = [];
        $data = [];

        foreach ($columns as $column => $def) {
            if (isset($model->{$column}) && null === $model->{$column} && isset($def['default'])) {
                $cols[] = $this->quoteName($column);
                $prep[] = ':' . $column;
                $sets[] = '' . $this->quoteName($column) . '=:' . $column;
                $data[':' . $column] = $def['default'];
            } else {
                $cols[] = $this->quoteName($column);
                $prep[] = ':' . $column;
                $sets[] = '' . $this->quoteName($column) . '=:' . $column;
                switch ($def['type']) {
                    case 'boolean':
                        $data[':' . $column] = (int)$model->{$column};
                        break;
                    default:
                        $data[':' . $column] = $model->{$column};
                        break;
                }
            }
        }

        foreach ($relations as $rel => $def) {
            switch ($def['type']) {
                case $class::BELONGS_TO:
                    $column = $class::getRelationLinkName($def);
                    if (!in_array($column, $cols)) {
                        // @todo: test this!
                        // if (isset($model->{$column}) && !is_null($model->{$column})) {
                        if (isset($model->{$column})) {
                            $cols[] = $this->quoteName($column);
                            $prep[] = ':' . $column;
                            $sets[] = '' . $this->quoteName($column) . '=:' . $column;
                            $data[':' . $column] = $model->{$column};
                        } elseif (isset($model->{$rel}) && $model->{$rel} instanceof Model) {
                            $cols[] = $this->quoteName($column);
                            $prep[] = ':' . $column;
                            $sets[] = '' . $this->quoteName($column) . '=:' . $column;
                            $data[':' . $column] = $model->{$rel}->getPk();
                        }
                    }
                    break;
            }
        }

        $connection = $class::getDbConnection();
        if ($model->isNew()) {
            $sql = '
                INSERT INTO ' . $this->quoteName($class::getTableName()) . '
                (' . implode(', ', array_unique($cols)) . ')
                VALUES
                (' . implode(', ', array_unique($prep)) . ')
                RETURNING ' . $class::PK . '
            ';
            $res = $connection->query($sql, $data);
            $model->{$class::PK} = $res->fetch()[$class::PK];
        } else {
            $sql = '
                UPDATE ' . $this->quoteName($class::getTableName()) . '
                SET ' . implode(', ', array_unique($sets)) . '
                WHERE "' . $class::PK . '"=\'' . $model->{$class::PK} . '\'
            ';
            $connection->execute($sql, $data);
        }

        return $model;

    }
667926
public function belongsTo($model, $options = array())
    {
        if (isset($this->_relationsCache[$model])) {
            return $this->_relationsCache[$model];
        }

        $options = static::getRelationInfo($model, $options);

        if (!isset($options['localKey'])) {
            $options['localKey'] = Inflector::foreignKey($model);
        }

        if (!isset($options['foreignKey'])) {
            $options['foreignKey'] = $options['model']::primaryKey();
        }

        return $this->_relationsCache[$model] = $options['model']::select()
            ->where("{$options['foreignKey']} = ?", $this->{$options['localKey']})
            ->fetch();
    }
1003968
public function children( $model )
	{
		$query = $this->model->newQuery();

		$query->where( $this->model->getQualifiedParentColumnName(), '=', $model->getKey() );

		// We must also add the scoped column values to the query to compute valid
		// left and right indexes.
		foreach ( $this->scopedAttributes( $model ) as $fld => $value )
			$query->where( $this->qualify( $fld ), '=', $value );

		$query->orderBy( $this->model->getQualifiedLeftColumnName() );
		$query->orderBy( $this->model->getQualifiedRightColumnName() );
		$query->orderBy( $this->model->getQualifiedKeyName() );

		return $query->get();
	}
929015
protected function saveColumns(Model $model)
    {
        /** @var \T4\Orm\Model $class */
        $class = get_class($model);
        $columns = $class::getColumns();
        $relations = $class::getRelations();
        $cols = [];
        $sets = [];
        $data = [];

        foreach ($columns as $column => $def) {
            if (isset($model->{$column}) && null === $model->{$column} && isset($def['default'])) {
                $sets[$column] = ':' . $column;
                $data[':'.$column] = $def['default'];
            } else {
                $cols[] = $column;
                $sets[$column] = ':' . $column;
                switch ($def['type']) {
                    case 'boolean':
                        $data[':' . $column] = (int)$model->{$column};
                        break;
                    default:
                        $data[':' . $column] = $model->{$column};
                        break;
                }
            }
        }

        foreach ($relations as $rel => $def) {
            switch ($def['type']) {
                case $class::BELONGS_TO:
                    $column = $class::getRelationLinkName($def);
                    if (!in_array($column, $cols)) {
                        // todo: test this!
                        //if (isset($model->{$column}) && !is_null($model->{$column})) {
                        if (isset($model->{$column})) {
                            $sets[$column] = ':' . $column;
                            $data[':'.$column] = $model->{$column};
                        } elseif (isset($model->{$rel}) && $model->{$rel} instanceof Model) {
                            $sets[$column] = ':' . $column;
                            $data[':'.$column] = $model->{$rel}->getPk();
                        }
                    }
                    break;
            }
        }

        $connection = $class::getDbConnection();
        if ($model->isNew()) {
            $query = new Query();
            $query->insert($class::getTableName())
                ->values($sets)
                ->params($data);
            $connection->execute($query);
            $model->{$class::PK} = $connection->lastInsertId();
        } else {
            $query = new Query();
            $query->update($class::getTableName())
                ->values($sets)
                ->where($this->quoteName($class::PK) . '='. $model->getPk())
                ->params($data);
            $connection->execute($query);
        }

        return $model;

    }
328300
protected function saveBelongsToRelations(Model $model)
    {
        foreach ($model->getRelations() as $name => $relation) {
            if ($model->{$name}() instanceof BelongsTo && ! is_null($relation)) {
                $relation->save();
                $model->{$name}()->associate($relation);
            }
        }
    }