Define a many-to-many relationship.

@param mixed       $entity
@param string      $related
@param string|null $table
@param string|null $foreignKey
@param string|null $otherKey

@throws MappingException

@return \Analogue\ORM\Relationships\BelongsToMany
321670
public function belongsToMany(
        $entity,
        string $related,
        string $table = null,
        string $foreignKey = null,
        string $otherKey = null
    ): BelongsToMany {
        // Add the relation to the definition in map
        list(, $caller) = debug_backtrace(false);
        $relation = $caller['function'];
        $this->relatedClasses[$relation] = $related;

        $this->addManyRelation($relation);
        $this->addForeignRelation($relation);
        $this->addPivotRelation($relation);

        // First, we'll need to determine the foreign key and "other key" for the
        // relationship. Once we have determined the keys we'll make the query
        // instances as well as the relationship instances we need for this.
        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $relatedMapper = Manager::getInstance()->mapper($related);

        $relatedMap = $relatedMapper->getEntityMap();

        $otherKey = $otherKey ?: $relatedMap->getForeignKey();

        // If no table name was provided, we can guess it by concatenating the two
        // models using underscores in alphabetical order. The two model names
        // are transformed to snake case from their default CamelCase also.
        if (is_null($table)) {
            $table = $this->joiningTable($relatedMap);
        }

        return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
    }
567627
public function setRelatedModel($foreignKey, Model $relatedModel)
    {
        if (count($this->relatedModels) >= 2
            && !array_key_exists($foreignKey, $this->relatedModels)) {

            return;
        }

        $this->relatedModels[$foreignKey] = $relatedModel;
    }
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);
        }
    }
66373
public function belongsTo(string $alias, string $entity): Relation {
    return $this->belongs_to[$entity] = new Relation($alias, $this->full_name, $entity);
  }
242025
public function hasMany(Model $related)
    {
        $table = ucfirst($related->getTable());
        $column = "shared{$table}List";
        $this->bean->$column[] = $related->getBean();
    }
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;
    }
709037
public function belongsToThrough($related, $through, $localKey = null, $prefix = '', $foreignKeyLookup = [])
    {
        /** @var \Illuminate\Database\Eloquent\Model $relatedInstance */
        $relatedInstance = new $related;
        $throughParents = [];
        $foreignKeys = [];

        foreach ((array) $through as $model) {
            $foreignKey = null;

            if (is_array($model)) {
                $foreignKey = $model[1];

                $model = $model[0];
            }

            /** @var \Illuminate\Database\Eloquent\Model $instance */
            $instance = new $model;

            if ($foreignKey) {
                $foreignKeys[$instance->getTable()] = $foreignKey;
            }

            $throughParents[] = $instance;
        }

        foreach ($foreignKeyLookup as $model => $foreignKey) {
            $instance = new $model;

            if ($foreignKey) {
                $foreignKeys[$instance->getTable()] = $foreignKey;
            }
        }

        return $this->newBelongsToThrough($relatedInstance->newQuery(), $this, $throughParents, $localKey, $prefix, $foreignKeys);
    }
327837
private function cascadePersist($entity, array &$visited)
    {
        $class = $this->em->getClassMetadata(get_class($entity));

        if ($entity instanceof GhostObjectInterface && ! $entity->isProxyInitialized()) {
            // nothing to do - proxy is not initialized, therefore we don't do anything with it
            return;
        }

        foreach ($class->getDeclaredPropertiesIterator() as $association) {
            if (! ($association instanceof AssociationMetadata && in_array('persist', $association->getCascade(), true))) {
                continue;
            }

            /** @var AssociationMetadata $association */
            $relatedEntities = $association->getValue($entity);
            $targetEntity    = $association->getTargetEntity();

            switch (true) {
                case $relatedEntities instanceof PersistentCollection:
                    // Unwrap so that foreach() does not initialize
                    $relatedEntities = $relatedEntities->unwrap();
                    // break; is commented intentionally!

                case $relatedEntities instanceof Collection:
                case is_array($relatedEntities):
                    if (! ($association instanceof ToManyAssociationMetadata)) {
                        throw ORMInvalidArgumentException::invalidAssociation(
                            $this->em->getClassMetadata($targetEntity),
                            $association,
                            $relatedEntities
                        );
                    }

                    foreach ($relatedEntities as $relatedEntity) {
                        $this->doPersist($relatedEntity, $visited);
                    }

                    break;

                case $relatedEntities !== null:
                    if (! $relatedEntities instanceof $targetEntity) {
                        throw ORMInvalidArgumentException::invalidAssociation(
                            $this->em->getClassMetadata($targetEntity),
                            $association,
                            $relatedEntities
                        );
                    }

                    $this->doPersist($relatedEntities, $visited);
                    break;

                default:
                    // Do nothing
            }
        }
    }
1013315
protected function OneToMany($related, $model, $local, $remote)
    {
        $relationVal = $this->manyRelationValue($related, $model, $local);
        $relationColumn = $this->manyRelationColumn($related, $model, $remote);
        //
        return $this->all($related, $relationColumn, $relationVal);
    }
990841
protected function getRelation($entity, $method)
    {
        // We need to check the relationship method only at the first local entity, because all entities must be of the same class.
        if (!method_exists($entity, $method)) {
            throw new LogicException('Call to undefined relationship "' . $method . '" on model "' . get_class($entity) . '".');
        }

        // Create a Relation without any constrains.
        $relation = \Core\Models\Relation::noConstraints(function() use ($entity, $method) {
            return $entity->$method();
        });

        if (!$relation instanceof Relation) {
            throw new LogicException('Relationship method "' . $method . '" must return an object of type Core\Models\Contracts\Relation.');
        }

        return $relation;
    }
94324
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(false, 2);

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

        $specialType = $this->getCmsSpecialRelationType($relation);

        // 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)) {
            if ($specialType === self::RELATION_TYPE_CATEGORY) {
                $foreignKey = config('pxlcms.relations.categories.keys.category');
            } else {
                $foreignKey = Str::snake($relation);
            }
        }

        // If the relation name is the same (casing aside) as the foreign key attribute,
        // we must make sure that the foreign key property is at least a key in the
        // attributes array. If it is not, there will be recursive lookup problems
        // where the attribute is never resolved and 'property does not exist' exceptions occur.
        if (    snake_case($relation) == $foreignKey
            &&  ! array_key_exists($foreignKey, $this->attributes)
        ) {
            $this->attributes[ $foreignKey ] = null;
        }

        /** @var Builder $belongsTo */
        $belongsTo = parent::belongsTo($related, $foreignKey, $otherKey, $relation);

        // category relation must be filtered by module id
        if ($specialType === self::RELATION_TYPE_CATEGORY) {
            $belongsTo->where(
                config('pxlcms.relations.categories.keys.module'),
                $this->getModuleNumber()
            );
        }

        return $belongsTo;
    }
1058161
public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
    {
        // If no relationship name was passed, we will pull backtraces to get the
        // name of the calling function. We will use that function name as the
        // title of this relation since that is a great convention to apply.
        if (is_null($relation)) {
            $relation = $this->getBelongsToManyCaller();
        }

        $foreignKey = $foreignKey ?: $this->getCmsReferenceKeyForRelation($relation);
        $otherKey   = $otherKey   ?: $this->getCmsReferenceKeyForRelation($relation, true);

        if (is_null($table)) {
            $table = $this->getCmsJoiningTable();
        }

        $fieldId = $this->getCmsReferenceFieldId($relation);
        if (empty($fieldId)) {
            throw new InvalidArgumentException("No 'field' id configured for relation/reference: '{$relation}'!");
        }

        // originally, it was possible to just get the parent's BelongsToMany instance, but
        // to avoid problems when saving on the relationship, we need a specialized extension
        // of the relation class for the CMS.
        //$belongsToMany = parent::belongsToMany($related, $table, $foreignKey, $otherKey);

        /** @var Model $instance */
        $instance = new $related;

        $query = $instance->newQuery();

        $belongsToMany = new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation, $fieldId);

        // add constraints
        $belongsToMany->wherePivot(config('pxlcms.relations.references.keys.field', 'from_field_id'), $fieldId);

        return $belongsToMany;
    }
1085229
public function create($related)
	{
		if($related instanceof $this->model)
		{
			$related->{$this->getForeignKey()} = $this->parent->getPrimaryKeyValue();

			$related->save();

			return $related;
		}

		return $this->model->create([$this->getForeignKey() => $this->parent->getPrimaryKeyValue()] + $related);
	}
167001
public function belongsToMany($related, $table = null, $foreignKey = null, $relatedKey = null, $relation = null)
    {
        // If no relationship name was passed, we will pull backtraces to get the
        // name of the calling function. We will use that function name as the
        // title of this relation since that is a great convention to apply.
        if (is_null($relation)) {
            $relation = $this->guessBelongsToManyRelation();
        }

        // First, we'll need to determine the foreign key and "other key" for the
        // relationship. Once we have determined the keys we'll make the query
        // instances as well as the relationship instances we need for this.
        $instance = $this->newRelatedInstance($related);

        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $relatedKey = $relatedKey ?: $instance->getForeignKey();

        // If no table name was provided, we can guess it by concatenating the two
        // models using underscores in alphabetical order. The two model names
        // are transformed to snake case from their default CamelCase also.
        if (is_null($table)) {
            $table = $this->joiningTable($related);
        }

        return new BelongsToMany(
            $instance->newQuery(), $this, $table, $foreignKey, $relatedKey, $relation
        );
    }
268945
public function belongsToMany(string $related, ?string $primary_key = null, ?string $foreign_key = null)
    {
        $relatedModel = app()->make($related);
        $local_key = $local_key ?? $this->getKey();
        // Make the table name singular and append ID to it
        // FIXME: Use a more reliable approach
        $foreign_key = $foreign_key ?? rtrim($relatedModel->getTable(), 's').'_id';
        return new BelongsToMany($relatedModel, $this, $primary_key, $foreign_key);
    }
1270910
public function join(
        $foreignTable,
        $leftField = null,
        $joinKind = null,
        $joinAlias = null,
        $relation = null,
        $behaviour = 'cascade'
    ) {
        list($rightTable, $rightField) = explode('.', $foreignTable, 2);
        if (is_null($rightField)) {
            $rightField = 'id';
        }
        $referenceType = ($rightField === 'id') ? 'hasOne' : 'hasMany';
        if ($referenceType === 'hasMany') {
            throw $this->exception('has many join isn\'t supported');
        }
        $leftTable = $this->table;

        $joinAlias = $this->_unique($this->relations, $joinAlias);
        /** @type Field_SQL_Relation $field */
        $field = $this->add($this->defaultSQLRelationFieldClass);
        $field->setLeftTable($relation ?: $leftTable)
            ->setLeftField($leftField)
            ->setRightTable($rightTable)
            ->setRightField($rightField)
            ->setJoinKind($joinKind)
            ->setJoinAlias($joinAlias)
            ->setModel($this);

        $field->referenceType = $referenceType;
        $field->setBehaviour($behaviour);

        $this->relations[$joinAlias] = $field;

        return $field;
    }
1015439
public function getRelationManager(EntityContract $entity)
    {
        $key = $entity->{$this->getRelationKey()};

        if (!isset($this->relations[$key])) {
            return null;
        }

        $class = $this->relations[$key];

        return new $class($this->getManager()->getAgent());
    }