@param EntityStructure $structure
@param \ReflectionProperty $property
@param ManyToMany|Relation $relation

@throws RelationException Class not correct, no target property found or not extending Entity.
@throws \ReflectionException Class not found
14667
private function manyToMany(&$structure, $property, $relation)
    {
        $from = $structure->name;
        $to = $relation->targetEntity;

        $reflection = null;
        try {
            $reflection = new \ReflectionClass($to);
        }catch(\Exception $e) { // @codeCoverageIgnore
            // Ignore, we will throw error on the next if. // @codeCoverageIgnore
        }
        if ($reflection === null || ! $reflection->isSubclassOf(Entity::class)) {
            throw new RelationException("The target entity of your relation on the entity '".$from."' and property '".$property->getName()."' has an unknown target Entity!"); // @codeCoverageIgnore
        }

        /** @var JoinTable $join */
        $join = $this->getJoin($property, JoinTable::class);
        $join->sourceEntityName = $from;
        $join->targetEntityName = $to;

        $relation->join = $join;

        // Register the join table
        EntityManager::getInstance()->registerJoinTable($join);

        // Add declaration to the structure
        $structure->relationProperties[] = $property->getName();
        $structure->foreignColumnNames[] = $join->column;
        $structure->relations[$property->getName()] = $relation;
    }
1080365
protected function createAssociation(ClassMetadataBuilder $builder, $relation, $entity)
    {
        return $this->builder->createOneToMany($relation, $entity);
    }
1271564
public function assignRelated($source, $relationNames=null, $meta=null)
    {
        if (!$source) { return; }

        $stack = [];
        $sourceIsArray = is_array($source) || $source instanceof \Traversable;
        if (!$sourceIsArray) {
            $source = array($source);
        }

        if ($meta !== null) {
            $meta = !$meta instanceof Meta ? $this->mapper->getMeta($meta) : $meta;
        } else {
            $meta = $this->mapper->getMeta(get_class($source[0]));
        }

        if (!$relationNames) {
            if ($meta->autoRelations) {
                $relationNames = $meta->autoRelations;
            } else {
                throw new Exception("relationNames not passed, class {$meta->class} does not define autoRelations");
            }
        }

        $relationMap = [];
        foreach ((array)$relationNames as $relationName) {
            if (!isset($meta->relations[$relationName])) {
                throw new Exception("Unknown relation $relationName on {$meta->class}");
            }
            $relationMap[$relationName] = $rel = $meta->relations[$relationName];
            if ($rel['mode'] == 'class') {
                throw new Exception("Relation $relationName is not assignable for class {$meta->class}");
            }
        }

        $done = [];
        foreach ($relationMap as $relationName=>$relation) {
            if (isset($done[$relationName])) {
                continue;
            }
            $done[$relationName] = true;

            $missing = [];
            foreach ($source as $idx=>$item) {
                // we have to assume it's missing if we see a getter as there
                // may be serious unintended side effects from calling a getter
                // that may be unpopulated. it might lazy load if it's an active
                // record, or it might throw an exception because the 'set' hasn't
                // been called.

                // check for isset as well as falsey for the situation where we're using
                // stdClasses as the response object
                if (isset($relation['getter']) || !isset($item->{$relationName}) || !$item->{$relationName}) {
                    $missing[$idx] = $item;
                }
            }

            if ($missing) {
                $result = $this->getRelated($missing, $relationName, ['stack'=>$stack], $meta);
                $relator = $this->getRelator($meta, $relationName);
                $this->populateObjectsWithRelated($missing, $result, $relation);
            }
        }
    }
1067400
public function createTranslatedProperty($property, $locale, StructureInterface $structure = null)
    {
        if ($property instanceof ItemMetadata) {
            $property = $this->createProperty($property, $structure);
        }
        $property = new TranslatedProperty(
            $property,
            $locale,
            $this->namespaceRegistry->getPrefix('content_localized')
        );

        return $property;
    }
1038260
public function save( & $structure )
    {
        $id = $this->getSaveProperty( $structure, 'id' );

        if ( empty( $id ) )
        {
            $selector = $this->getSaveProperty( $structure, 'selector', '' );
            $media    = $this->getSaveProperty( $structure, 'media', '' );
            $rootId   = $this->getSaveProperty( $structure, 'rootParagraphId' );
            $sql      = $this->sql();
            $select   = $sql->select()
                            ->columns( array( 'id' ) )
                            ->where( array(
                                'selector'        => $selector,
                                'media'           => $media,
                                'rootParagraphId' => $rootId,
                            ) )
                            ->limit( 1 );

            $result = $sql->prepareStatementForSqlObject( $select )
                          ->execute();

            if ( $result->getAffectedRows() )
            {
                foreach ( $result as $row )
                {
                    $this->setSaveProperty( $structure, 'id', $row['id'] );
                }
            }
        }

        $result = parent::save( $structure );
        $id     = $this->getSaveProperty( $structure, 'id' );

        if ( $result > 0 && ! empty( $id ) )
        {
            switch ( true )
            {
                case $structure instanceof Structure:
                    $properties = $structure->getRawProperties();
                    break;

                case is_array( $structure ):
                    $properties = isset( $structure['properties'] )
                        ? $structure['properties']
                        : array();
                    break;

                case is_object( $structure ):
                    $properties = isset( $structure->properties )
                        ? $structure->properties
                        : array();
                    break;

                default:
                    return $result;
            }

            $sql = $this->sql( $this->getTableInSchema(
                static::$propertyTableName
            ) );

            if ( empty( $properties ) )
            {
                $delete = $sql->delete()
                              ->where( array(
                                  'ruleId' => $id,
                              ) );

                $result += $sql->prepareStatementForSqlObject( $delete )
                               ->execute()
                               ->getAffectedRows();
            }
            else
            {
                $propNames = array();

                foreach ( $properties as $name => $spec )
                {
                    $propNames[] = $propName = empty( $spec['name'] )
                        ? $name : $spec['name'];

                    $result += $this->saveProperty(
                        $id,
                        $propName,
                        $spec['value'],
                        $spec['priority']
                    );
                }

                $delete = $sql->delete()
                              ->where( array(
                                  'ruleId' => $id,
                                  new NotIn( 'name', $propNames ),
                              ) );

                $result += $sql->prepareStatementForSqlObject( $delete )
                               ->execute()
                               ->getAffectedRows();
            }
        }

        return $result;
    }
179697
private function convertReflectionPropertyToManyToOneAssociationMetadata(
        ReflectionProperty $reflectionProperty,
        array $propertyAnnotations,
        Mapping\ClassMetadata $metadata
    ) {
        $className      = $metadata->getClassName();
        $fieldName      = $reflectionProperty->getName();
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
        $assocMetadata  = new Mapping\ManyToOneAssociationMetadata($fieldName);
        $targetEntity   = $manyToOneAnnot->targetEntity;

        $assocMetadata->setTargetEntity($targetEntity);
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));

        if (! empty($manyToOneAnnot->inversedBy)) {
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
        }

        // Check for Id
        if (isset($propertyAnnotations[Annotation\Id::class])) {
            $assocMetadata->setPrimaryKey(true);
        }

        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);

        // Check for JoinColumn/JoinColumns annotations
        switch (true) {
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];

                $assocMetadata->addJoinColumn(
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
                );

                break;

            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];

                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
                    $assocMetadata->addJoinColumn(
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
                    );
                }

                break;
        }

        return $assocMetadata;
    }
85200
public function process(EnforceModelRelationshipEvent $event)
    {
        $environment = $event->getEnvironment();
        $mode        = $environment->getDataDefinition()->getBasicDefinition()->getMode();

        if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) {
            return;
        }

        $input         = $environment->getInputProvider();
        $model         = $event->getModel();
        $collector     = new ModelCollector($environment);
        $relationships = new RelationshipManager(
            $environment->getDataDefinition()->getModelRelationshipDefinition(),
            $mode
        );

        if ($input->hasParameter('into')) {
            $this->handleInto(
                ModelId::fromSerialized($input->getParameter('into')),
                $relationships,
                $collector,
                $model
            );
        } elseif ($input->hasParameter('after')) {
            $this->handleAfter(
                ModelId::fromSerialized($input->getParameter('after')),
                $relationships,
                $collector,
                $model
            );
        }

        // Also enforce the parent condition of the parent provider (if any).
        if ($input->hasParameter('pid')) {
            $parent = $collector->getModel($input->getParameter('pid'));
            $relationships->setParent($model, $parent);
        }
    }
147378
protected function updateBlocksStructure(
        array &$structureArray,
        BlockMetadata $property,
        $localeSource,
        $localeDestination
    ) {
        if (!array_key_exists($property->getName(), $structureArray) || !$structureArray[$property->getName()]) {
            return;
        }

        foreach ($structureArray[$property->getName()] as &$structure) {
            /** @var ItemMetadata $component */
            $component = $property->getComponentByName($structure['type']);
            /** @var PropertyMetadata $child */
            foreach ($component->getChildren() as $child) {
                if ($structure[$child->getName()]) {
                    $this->processContentType(
                        $child,
                        $structure,
                        $localeSource,
                        $localeDestination
                    );
                }
            }
        }
    }
885032
public function buildClassMethods(StructureInterface $structure, TypeInterface $source_type, TypeInterface $target_type, array &$result)
    {
        $namespace = $structure->getNamespace();

        if ($namespace) {
            $namespace = '\\' . ltrim($namespace, '\\');
        }

        $target_instance_class = $namespace . '\\' . Inflector::classify(Inflector::singularize($target_type->getName()));

        $classified_association_name = Inflector::classify($this->getName());

        $getter_name = "get{$classified_association_name}";
        $setter_name = "set{$classified_association_name}";
        $fk_getter_name = "get{$classified_association_name}Id";
        $fk_setter_name = "set{$classified_association_name}Id";

        $result[] = '';
        $result[] = '    /**';
        $result[] = '     * Return ' . Inflector::singularize($source_type->getName()) . ' ' . $this->getName() . '.';
        $result[] = '     *';
        $result[] = '     * @return ' . $target_instance_class;
        $result[] = '     */';
        $result[] = '    public function ' . $getter_name . '()';
        $result[] = '    {';
        $result[] = '        return $this->pool->getById(' . var_export($target_instance_class, true) . ', $this->' . $fk_getter_name . '());';
        $result[] = '    }';

        $setter_access_level = $this->getProtectSetter() ? 'protected' : 'public';

        $result[] = '';
        $result[] = '    /**';
        $result[] = '     * Set ' . Inflector::singularize($source_type->getName()) . ' ' . $this->getName() . '.';
        $result[] = '     *';
        $result[] = '     * @param  ' . $target_instance_class  . ' $value';
        $result[] = '     * @return $this';
        $result[] = '     */';
        $result[] = '    ' . $setter_access_level . ' function &' . $setter_name . '(' . $target_instance_class . ' $value' . ($this->isRequired() ? '' : ' = null') . ')';
        $result[] = '    {';

        if ($this->isRequired()) {
            $result[] = '        if (empty($value) || !$value->isLoaded()) {';
            $result[] = '            throw new \\InvalidArgumentException(\'Valid related instance is required\');';
            $result[] = '        }';
            $result[] = '';
            $result[] = '        $this->' . $fk_setter_name . '($value->getId());';
            $result[] = '';
            $result[] = '        return $this;';
        } else {
            $result[] = '        if (empty($value)) {';
            $result[] = '            $this->' . $fk_setter_name . '(0);';
            $result[] = '        } else {';
            $result[] = '            $this->' . $fk_setter_name . '($value->getId());';
            $result[] = '        }';
            $result[] = '';
            $result[] = '        return $this;';
        }

        $result[] = '    }';
    }
961221
public static function initByManyToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        if(class_exists($annotation->model))
        {
            $modelClass = $annotation->model;
        }
        else
        {
            $modelClass = Imi::getClassNamespace($className) . '\\' . $annotation->model;
        }

        $struct = new ManyToMany($className, $propertyName, $annotation);
        $leftField = $struct->getLeftField();
        $rightField = $struct->getRightField();
        $middleTable = ModelManager::getTable($struct->getMiddleModel());
        $rightTable = ModelManager::getTable($struct->getRightModel());

        static::parseManyToManyQueryFields($struct->getMiddleModel(), $struct->getRightModel(), $middleFields, $rightFields);
        $fields = static::mergeManyToManyFields($middleTable, $middleFields, $rightTable, $rightFields);
        
        $model->$propertyName = new ArrayList($struct->getMiddleModel());
        $model->{$annotation->rightMany} = new ArrayList($struct->getRightModel());
        
        if(null !== $model->$leftField)
        {
            $list = Db::query(ModelManager::getDbPoolName($className))
                        ->table($rightTable)
                        ->field(...$fields)
                        ->join($middleTable, $middleTable . '.' . $struct->getMiddleRightField(), '=', $rightTable . '.' . $rightField)
                        ->where($middleTable . '.' . $struct->getMiddleLeftField(), '=', $model->$leftField)
                        ->select()
                        ->getArray();
            if(null !== $list)
            {
                // 关联数据
                static::appendMany($model->$propertyName, $list, $middleFields, $struct->getMiddleModel());

                // 右侧表数据
                static::appendMany($model->{$annotation->rightMany}, $list, $rightFields, $struct->getRightModel());

            }
        }
    }
1017043
private function resolveRelations($entity, $meta)
    {
        $reflection = new \ReflectionClass($entity);
        $entityId   = $this->repositoryFactory->getIdAccessorRegistry()->getEntityId($entity);

        $this->resolveBelongsTo($meta->relations['belongsTo'], $entity, $reflection);
        $this->resolveBelongsToMany($meta->relations['belongsToMany'], $entity, $reflection);
        $this->resolveHasOne($meta->relations['hasOne'], $entity, $entityId);
        $this->resolveHasMany($meta->relations['hasMany'], $entity, $entityId);
        $this->resolveHasManyThrough($meta->relations['hasManyThrough'], $entity, $entityId);
    }
782657
private function buildRelationshipMetadata(
        \SimpleXMLElement $relationshipNode,
        $className,
        \ReflectionClass $reflection
    ) {
        if (
            !isset($relationshipNode['name'])
            || !isset($relationshipNode['type'])
            || !isset($relationshipNode['direction'])
            || !isset($relationshipNode['target-entity'])
        ) {
            throw new MappingException(sprintf(
                'Class "%s" OGM XML property relationship configuration is missing mandatory attributes',
                $className
            ));
        }
        $relationship = new Relationship();

        $relationship->type = (string) $relationshipNode['type'];
        $relationship->direction = (string) $relationshipNode['direction'];
        $relationship->targetEntity = (string) $relationshipNode['target-entity'];

        $relationship->relationshipEntity = isset($relationshipNode['relationship-entity'])
            ? (string) $relationshipNode['relationship-entity']
            : null
        ;
        $relationship->mappedBy = isset($relationshipNode['mapped-by'])
            ? (string) $relationshipNode['mapped-by']
            : null
        ;
        if (isset($relationshipNode['collection'])) {
            if ((string) $relationshipNode['collection'] === 'true') {
                $relationship->collection = true;
            }
            if ((string) $relationshipNode['collection'] === 'false') {
                $relationship->collection = false;
            }
        }

        $orderBy = null;
        if (isset($relationshipNode->{'order-by'})) {
            $orderNode = $relationshipNode->{'order-by'};
            if (!isset($orderNode['property']) || !isset($orderNode['order'])) {
                throw new MappingException(sprintf(
                    'Class "%s" OGM XML property relationship order configuration is missing mandatory attributes',
                    $className
                ));
            }
            $orderBy = new OrderBy();
            $orderBy->order = (string) $orderNode['order'];
            $orderBy->property = (string) $orderNode['property'];
        }

        return new RelationshipMetadata(
            $className,
            $reflection->getProperty((string) $relationshipNode['name']),
            $relationship,
            isset($relationshipNode->lazy),
            $orderBy
        );
    }
663737
public function unassignChild ($relationIdentifier, $id, $childId)
		{
			$relation = $this->getRelation($relationIdentifier);

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

			if ($relation instanceof OneToMany) {
				$this->database->createQuery()
					->update()
					->table($relation->getRelatedTable())
					->set(
						array(
							$relation->getRelatedKey() => null,
						)
					)
					->whereEqual($relation->getRelatedKey(), $id)
					->whereEqual($relation->getKey(), $childId)
					->execute();
			} else if ($relation instanceof ManyToMany) {
				$this->database->createQuery()
					->delete()
					->from($relation->getRelationalTable())
					->whereEqual($relation->getKey(), $id)
					->whereEqual($relation->getRelatedKey(), $childId)
					->execute();
			} else {
				throw new RelationException('Invalid relation type `' . gettype($relation) . '`');
			}
		}
998652
protected function cascadeRelation(Relation $relation, array $input, Model $parent = null)
	{
		// Make a child repository for containing the cascaded relationship through saves
		$target_model_class = get_class($relation->getQuery()->getModel());
		$relation_repository = (new self)->setModelClass($target_model_class);

		switch (get_class($relation)) {
			case BelongsTo::class:
				/**
				 * @var \Illuminate\Database\Eloquent\Relations\BelongsTo $relation
				 */
				// For BelongsTo, simply associate by foreign key.
				// (We don't have to assume the parent model exists to do this.)
				$related = $relation_repository->setInput($input)->save();
				$relation->associate($related);
				break;
			case HasMany::class:
				/**
				 * @var \Illuminate\Database\Eloquent\Relations\HasMany $relation
				 */
				// The parent model "owns" child models; any not specified here should be deleted.
				$current_ids = $relation->pluck($this->getKeyName())->toArray();
				$new_ids = array_filter(array_column($input, $this->getKeyName()));
				$removed_ids = array_diff($current_ids, $new_ids);
				if ( !empty($removed_ids)) {
					$relation->whereIn($this->getKeyName(), $removed_ids)->delete();
				}

				// Set foreign keys on the children from the parent, and save.
				foreach ($input as $sub_input) {
					$sub_input[$this->getRelationsForeignKeyName($relation)] = $parent->{$this->getKeyName()};
					$relation_repository->setInput($sub_input)->save();
				}
				break;
			case HasOne::class:
				/**
				 * @var \Illuminate\Database\Eloquent\Relations\HasOne $relation
				 */
				// The parent model "owns" the child model; if we have a new and/or different
				// existing child model, delete the old one.
				$current = $relation->getResults();
				if ( !is_null($current)
					&& ( !isset($input[$this->getKeyName()]) || $current->{$this->getKeyName()} !== intval($input[$this->getKeyName()]))
				) {
					$relation->delete();
				}

				// Set foreign key on the child from the parent, and save.
				$input[$this->getRelationsForeignKeyName($relation)] = $parent->{$this->getKeyName()};
				$relation_repository->setInput($input)->save();
				break;
			case BelongsToMany::class:
				/**
				 * @var \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation
				 */
				// Find all the IDs to sync.
				$ids = [];

				foreach ($input as $sub_input) {
					$id = $relation_repository->setInput($sub_input)->save()->{$this->getKeyName()};

					// If we were passed pivot data, pass it through accordingly.
					if (isset($sub_input['pivot'])) {
						$ids[$id] = (array)$sub_input['pivot'];
					} else {
						$ids[] = $id;
					}
				}

				// Sync to save pivot table and optional extra data.
				$relation->sync($ids);
				break;
		}
	}
478216
public function build(Entity $entity): void
    {
        $attributes = $entity->getEntityAttributes();

        // We will manually add a relationship for every attribute registered
        // of this entity. Once we know the relation method we have to use,
        // we will just add it to the entityAttributeRelations property.
        foreach ($attributes as $attribute) {
            $relation = $this->getRelationClosure($entity, $attribute);

            $entity->setEntityAttributeRelation($attribute->getAttribute('slug'), $relation);
        }
    }
551142
public function setStructureNodeRelatedByStructureNodeId(ChildStructureNode $v = null)
    {
        if ($v === null) {
            $this->setStructureNodeId(NULL);
        } else {
            $this->setStructureNodeId($v->getId());
        }

        $this->aStructureNodeRelatedByStructureNodeId = $v;

        // Add binding for other direction of this n:n relationship.
        // If this object has already been added to the ChildStructureNode object, it will not be re-added.
        if ($v !== null) {
            $v->addStructureNodeParentRelatedByStructureNodeId($this);
        }


        return $this;
    }
1224701
public function removeStructureNodeRelatedByParentId(ChildStructureNode $structureNodeRelatedByParentId)
    {
        if ($this->getStructureNodesRelatedByParentId()->contains($structureNodeRelatedByParentId)) { $structureNodeParent = new ChildStructureNodeParent();

            $structureNodeParent->setStructureNodeRelatedByParentId($structureNodeRelatedByParentId);
            if ($structureNodeRelatedByParentId->isStructureNodeRelatedByStructureNodeIdsLoaded()) {
                //remove the back reference if available
                $structureNodeRelatedByParentId->getStructureNodeRelatedByStructureNodeIds()->removeObject($this);
            }

            $structureNodeParent->setStructureNodeRelatedByStructureNodeId($this);
            $this->removeStructureNodeParentRelatedByStructureNodeId(clone $structureNodeParent);
            $structureNodeParent->clear();

            $this->collStructureNodesRelatedByParentId->remove($this->collStructureNodesRelatedByParentId->search($structureNodeRelatedByParentId));

            if (null === $this->structureNodesRelatedByParentIdScheduledForDeletion) {
                $this->structureNodesRelatedByParentIdScheduledForDeletion = clone $this->collStructureNodesRelatedByParentId;
                $this->structureNodesRelatedByParentIdScheduledForDeletion->clear();
            }

            $this->structureNodesRelatedByParentIdScheduledForDeletion->push($structureNodeRelatedByParentId);
        }


        return $this;
    }