Get a foreign relationship from given string

@param string $rawRelation

@return null | ForeignRelationship
182047
public static function fromString($rawRelation)
    {
        //name:assets;type:hasMany;params:App\\Models\\Asset|category_id|id,
        // expected string
        //name|type|params|field
        //assets|hasMany|App\\Models\\Asset,category_id,id|title

        $parts = explode(';', $rawRelation);
        $collection = [];
        foreach ($parts as $part) {
            if (!str_contains($part, ':')) {
                continue;
            }

            list($key, $value) = explode(':', $part);

            if ($key == 'params' || str_contains($value, '|')) {
                $value = explode('|', $value);
            }

            $collection[$key] = $value;
        }

        return self::get($collection);
    }
213237
protected static function getRelations(array $properties)
    {
        $relations = [];

        foreach ($properties as $property) {
            if (!is_null($relation = ForeignRelationship::get($property))) {
                $relations[] = $relation;
            }
        }

        return $relations;
    }
920263
public static function get(array $options)
    {
        if (!array_key_exists('type', $options) || !array_key_exists('params', $options) || !array_key_exists('name', $options)) {
			
			if(count($options) >= 3) {
				$values = array_values($options);
				$field = isset($values[3]) ? $values[3] : null;
				return new ForeignRelationship(
					$values[1],
					$values[2],
					$values[0],
					$field
				);				
			}
			
            return null;
        }
		
		$field = array_key_exists('field', $options) ? $options['field'] : null;

        return new ForeignRelationship(
            $options['type'],
            $options['params'],
            $options['name'],
            $field
        );
    }
49946
protected function _getRelationshipValue($relationship)
    {
        if (!isset($this->_relatedObjects[$relationship])) {
            $rel = static::$_classRelationships[get_called_class()][$relationship];

            if ($rel['type'] == 'one-one') {
                if ($value = $this->_getFieldValue($rel['local'])) {
                    $this->_relatedObjects[$relationship] = $rel['class']::getByField($rel['foreign'], $value);
                
                    // hook relationship for invalidation
                    static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
                } else {
                    $this->_relatedObjects[$relationship] = null;
                }
            } elseif ($rel['type'] == 'one-many') {
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
                    $rel['indexField'] = false;
                }
                
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByWhere(
                    array_merge($rel['conditions'], [
                        $rel['foreign'] => $this->_getFieldValue($rel['local']),
                    ]),
                
                    [
                        'indexField' => $rel['indexField'],
                        'order' => $rel['order'],
                        'conditions' => $rel['conditions'],
                    ]
                );
                
                
                // hook relationship for invalidation
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
            } elseif ($rel['type'] == 'context-children') {
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
                    $rel['indexField'] = false;
                }

                $conditions = array_merge($rel['conditions'], [
                    'ContextClass' => $rel['contextClass'],
                    'ContextID' => $this->_getFieldValue($rel['local']),
                ]);
            
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByWhere(
                    $conditions,
            
                    [
                        'indexField' => $rel['indexField'],
                        'order' => $rel['order'],
                    ]
                );
                
                // hook relationship for invalidation
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
            } elseif ($rel['type'] == 'context-parent') {
                $className = $this->_getFieldValue($rel['classField']);
                $this->_relatedObjects[$relationship] = $className ? $className::getByID($this->_getFieldValue($rel['local'])) : null;
                
                // hook both relationships for invalidation
                static::$_classFields[get_called_class()][$rel['classField']]['relationships'][$relationship] = true;
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
            } elseif ($rel['type'] == 'many-many') {
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
                    $rel['indexField'] = false;
                }
                
                // TODO: support indexField, conditions, and order
                
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByQuery(
                    'SELECT Related.* FROM `%s` Link JOIN `%s` Related ON (Related.`%s` = Link.%s) WHERE Link.`%s` = %u AND %s',
                
                    [
                        $rel['linkClass']::$tableName,
                        $rel['class']::$tableName,
                        $rel['foreign'],
                        $rel['linkForeign'],
                        $rel['linkLocal'],
                        $this->_getFieldValue($rel['local']),
                        $rel['conditions'] ? join(' AND ', $rel['conditions']) : '1',
                    ]
                );
                
                // hook relationship for invalidation
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
            } elseif ($rel['type'] == 'history' && static::isVersioned()) {
                $this->_relatedObjects[$relationship] = $rel['class']::getRevisionsByID($this->getPrimaryKeyValue(), $rel);
            }
        }
        
        return $this->_relatedObjects[$relationship];
    }
394055
protected function getValueFromRelationQuery($query)
    {
        // Prevent looking up connected model when foreign keys are NULL to prevent query errors.

        /** @var MorphTo $query */
        $typeName = $query->getMorphType();
        $keyName  = $query->getForeignKey();

        if ( ! $this->model->{$typeName} || ! $this->model->{$keyName}) {
            return null;
        }

        $query = $this->prepareRelationQuery($query);

        return $this->getValueFromModel(
            $query->first()
        );
    }
1262115
protected function getRelationAccessor(ForeignRelationship $collection)
    {
        return sprintf(
            '$%s = %s::pluck(\'%s\',\'%s\')->all();',
            $collection->getCollectionName(),
            $collection->getForeignModel(),
            $collection->getField(),
            $collection->getPrimaryKeyForForeignModel()
        );
    }
785617
public function getParent ($relationIdentifier, $id, $inputQuery = null)
		{
			$relation = $this->getRelation($relationIdentifier);

			if (! $relation) {
				throw new RelationException('Invalid relation `' . $relationIdentifier);
			}

			if ($relation instanceof ManyToOne && $relation instanceof OneToOne) {
				throw new RelationException('Invalid relation type `' . $relation['type'] . '`');
			}

			$row = $this->getRow($id);

			$query = $this->database->createQuery()
				->whereEqual($relation->getKey(), $row[$relation->getRelatedKey()])
				->composeWith($inputQuery);

			return $relation->getRelatedTableObject()->getRows($query)->fetchRow();
		}
435468
public function getRelation($name)
    {
        // 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 error prone. We don't want constraints because we add eager ones.
        $relation = Relation::noConstraints(function () use ($name) {
            $instance = $this->getModel()->newInstance();
            if (method_exists($instance, $name)) {
                return $instance->$name();
            }

            if ($relation = $instance->getRelationshipFromConfig($name)) {
                return $relation;
            }

            throw new RuntimeException("Call to undefined relationship [{$name}] on resource [{$this->getModel()->getTable()}].");
        });

        $nested = $this->relationsNestedUnder($name);

        // 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) {
            $relation->getQuery()->with($nested);
        }

        return $relation;
    }
40353
public function belongsTo( $related, $foreignKey = null, $otherKey = null, $relation = null )
	{
		// If no relation name was given, we will use this debug backtrace to extract
		// the calling method's name and use that as the relationship name as most
		// of the time this will be what we desire to use for the relationships.
		if ( is_null( $relation ) )
		{
			list( $current, $caller ) = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );

			$relation = $caller['function'];
		}

		// If no foreign key was supplied, we can use a backtrace to guess the proper
		// foreign key name by using the name of the relationship function, which
		// when combined with an "_id" should conventionally match the columns.
		if ( is_null( $foreignKey ) )
		{
			$foreignKey = Str::snake( $relation ) . '_id';
		}

		$instance = new $related;

		// Once we have the foreign key names, we'll just create a new Eloquent query
		// for the related models and returns the relationship instance which will
		// actually be responsible for retrieving and hydrating every relations.
		$query = $instance->newQuery();

		$otherKey = $otherKey ?: $instance->getKeyName();

		return new BelongsTo( $query, $this, $foreignKey, $otherKey, $relation );
	}
775793
protected static function getHasManyRelationField($model, $relation, $activeRelation)
    {
        $widgetOptions = self::getRelationWidgetOptions($model, $relation, $activeRelation, true);
        return static::getRelationWidget($model, $relation, $activeRelation, $widgetOptions);
    }
680371
final public function getRelated($relatedProperty) {
		
		$cacheName = $relatedProperty . 'RelatedObject';
		
		// object exists in cache, return it
		if ($this->issetCache($cacheName)) {
			return $this->getCache($cacheName);
		}
		
		// get table foreign-keys
		$foreignKeys = $this->db->getForeignKeys(static::TABLE_NAME);
		
		// get field name by mapped property
		$relatedField = $this->getMappedField($relatedProperty);
		
		// the table referenced by fk
		$referencedTable = NULL;
			
		// search the fk-table
		foreach ($foreignKeys as $fk) {
			if ($fk->COLUMN_NAME == $relatedField) {
				$referencedTable  = $fk->REFERENCED_TABLE_NAME;
				$referencedColumn = $fk->REFERENCED_COLUMN_NAME;
				break;
			}
		}
		
		// if not table is referenced, raise an error
		if (!$referencedTable) {
			$this->addError('Property ' . $relatedProperty . ' has not a foreign-key mapped into DB');
			return NULL;
		}
		
		// class that maps the referenced table
		$relatedClass = NULL;
		$loadedClasses = \get_declared_classes();
		
		// search in loaded classes
		foreach ($loadedClasses as $c) {
			if (is_subclass_of($c, 'Pair\ActiveRecord') and property_exists($c, 'TABLE_NAME') and $c::TABLE_NAME == $referencedTable) {
				$relatedClass = $c;
				break;
			}
		}
			
		// class cannot be found
		if (!$relatedClass) {
			
			// if not found, search in the whole application (FIXME encapsulation violated here...)
			$classes = Utilities::getActiveRecordClasses();
			
			// search for required one
			foreach ($classes as $class => $opts) {
				if ($opts['tableName'] == $referencedTable) {
					include_once($opts['folder'] . '/' . $opts['file']);
					$relatedClass = $class;
					break;
				}
			}
			
		}
		
		// class cannot be found
		if (!$relatedClass) {
			$this->addError('Table ' . $referencedTable . ' has not any Pair-class mapping');
			return NULL;
		}
		
		// create the new wanted Pair object
		$obj = new $relatedClass($this->$relatedProperty);
		
		// if loaded, return it otherwise NULL
		$ret = ($obj->isLoaded() ? $obj : NULL);
		
		// related object is being registered in cache of this object
		$this->setCache($cacheName, $ret);
		
		return $ret;
		
	}
442732
public function getRelationship($key)
    {
        if (isset($this->relationships[$key])) {
            return $this->relationships[$key];
        }
        return null;
    }
1150761
public function getRelation($subRelation = '', $closure = null)
    {
        if ($closure) {
            $closure($this->query);
        }

        $this->baseQuery();

        $relationModel = $this->query->relation($subRelation)->find();

        if ($relationModel) {
            $relationModel->setParent(clone $this->parent);
        }

        return $relationModel;
    }
1126817
public function getRelation()
    {
        if (!$this->relatedTableName) {
            return null;
        }
        foreach ($this->getTable()->getRelations() as $name => $relation) {
            if ($relation->getType() == RelationMap::MANY_TO_ONE) {
                if ($relation->getForeignTable()->getName() == $this->getRelatedTableName() && array_key_exists($this->getFullyQualifiedName(), $relation->getColumnMappings())) {
                    return $relation;
                }
            }
        }
    }
627704
static public function fetchReverseRelatedObjectsID( $objectID, $attributeID, $allRelations )
    {
        if ( !is_array( $allRelations ) || $allRelations === array() )
        {
            $allRelations = array( 'common', 'xml_embed', 'attribute' );
            if ( eZContentObject::isObjectRelationTyped() )
            {
                $allRelations[] = 'xml_link';
            }
        }

        $relatedObjectsTyped = array();
        foreach ( $allRelations as $relationType )
        {
            $relatedObjectsTyped[$relationType] =
                eZContentFunctionCollection::fetchReverseRelatedObjects( $objectID, $attributeID, array( $relationType ), false, array(), null );
        }

        $relatedObjectsTypedIDArray = array();
        foreach ( $relatedObjectsTyped as $relationTypeName => $relatedObjectsByType )
        {
            $relatedObjectsTypedIDArray[$relationTypeName] = array();
            foreach ( $relatedObjectsByType['result'] as $relatedObjectByType )
            {
                $relatedObjectsTypedIDArray[$relationTypeName][] = $relatedObjectByType->ID;
            }
        }

        return array( 'result' =>$relatedObjectsTypedIDArray );
    }
123252
public function get($rawId)
    {
        $row = $this->getCart()->get($rawId);

        return is_null($row) ? null : new Item($row);
    }
261827
public function getRelation($subRelation = '', $closure = null)
    {
        $localKey = $this->localKey;

        if ($closure) {
            $closure($this->query);
        }

        // 判断关联类型执行查询
        $relationModel = $this->query
            ->removeWhereField($this->foreignKey)
            ->where($this->foreignKey, $this->parent->$localKey)
            ->relation($subRelation)
            ->find();

        if ($relationModel) {
            $relationModel->setParent(clone $this->parent);
        }

        return $relationModel;
    }