%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/main/lib/orm/fields/relations/ |
| Current File : //home/bitrix/www/bitrix/modules/main/lib/orm/fields/relations/manytomany.php |
<?php
/**
* Bitrix Framework
* @package bitrix
* @subpackage main
* @copyright 2001-2018 Bitrix
*/
namespace Bitrix\Main\ORM\Fields\Relations;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\ORM\Entity;
use Bitrix\Main\ORM\Fields\FieldTypeMask;
use Bitrix\Main\ORM\Query\Query;
use Bitrix\Main\ORM\Fields\Relations\Reference;
use Bitrix\Main\SystemException;
/**
* Performs many to many relation through mediator entity
* @package bitrix
* @subpackage main
*/
class ManyToMany extends Relation
{
/** @var string */
protected $mediatorEntityName;
/** @var Entity */
protected $mediatorEntity;
/** @var string Used when mediator is a virtual entity */
protected $mediatorTableName;
/** @var string[] Stores owner entity primary => mediator primary */
protected $localPrimaryNames;
/** @var string Name of reference from mediator to owner entity */
protected $localReferenceName;
/** @var string[] Stores target entity primary => mediator primary */
protected $remotePrimaryNames;
/** @var string Name of reference from mediator to target entity */
protected $remoteReferenceName;
/**
* @param string $name
* @param string|Entity $referenceEntity
*
* @throws SystemException
*/
public function __construct($name, $referenceEntity)
{
if ($referenceEntity instanceof Entity)
{
$this->refEntity = $referenceEntity;
$this->refEntityName = $referenceEntity->getFullName();
}
else
{
// this one could be without leading backslash and/or with Table-postfix
$this->refEntityName = Entity::normalizeName($referenceEntity);
}
parent::__construct($name);
}
public function getTypeMask()
{
return FieldTypeMask::MANY_TO_MANY;
}
/**
* Explicit mediator entity. By default will be generated automatically.
*
* @param string|Entity $entity
*
* @return $this
*/
public function configureMediatorEntity($entity)
{
if ($entity instanceof Entity)
{
$this->mediatorEntity = $entity;
$this->mediatorEntityName = $entity->getFullName();
}
else
{
// this one could be without leading backslash and/or with Table-postfix
$this->mediatorEntityName = Entity::normalizeName($entity);
}
return $this;
}
/**
* In case of auto-generated mediator, sets the custom table name.
*
* @param $name
*
* @return $this
*/
public function configureMediatorTableName($name)
{
$this->mediatorTableName = $name;
return $this;
}
/**
* Short alias for configureMediatorTableName()
*
* @param $name
*
* @return $this
*/
public function configureTableName($name)
{
return $this->configureMediatorTableName($name);
}
/**
* In case of auto-generated mediator, sets the custom ID field name that stores owner entity ID.
*
* @param $fieldName
* @param $mediatorFieldName
*
* @return $this
*/
public function configureLocalPrimary($fieldName, $mediatorFieldName)
{
$this->localPrimaryNames[$fieldName] = $mediatorFieldName;
return $this;
}
/**
* In case of auto-generated mediator, sets the custom reference field name that points to owner entity.
*
* @param $name
*
* @return $this
*/
public function configureLocalReference($name)
{
$this->localReferenceName = $name;
return $this;
}
/**
* In case of auto-generated mediator, sets the custom ID field name that stores target entity ID.
*
* @param $fieldName
* @param $mediatorFieldName
*
* @return $this
*/
public function configureRemotePrimary($fieldName, $mediatorFieldName)
{
$this->remotePrimaryNames[$fieldName] = $mediatorFieldName;
return $this;
}
/**
* In case of auto-generated mediator, sets the custom reference field name that points to target entity.
*
* @param $name
*
* @return $this
*/
public function configureRemoteReference($name)
{
$this->remoteReferenceName = $name;
return $this;
}
/**
* @return Entity
* @throws ArgumentException
* @throws SystemException
*/
public function getRemoteEntity()
{
return $this->getRefEntity();
}
/**
* @return Entity
* @throws ArgumentException
* @throws SystemException
*/
public function getMediatorEntity()
{
if ($this->mediatorEntity === null)
{
if (!empty($this->mediatorEntityName) && class_exists($this->mediatorEntityName))
{
$this->mediatorEntity = Entity::getInstance($this->mediatorEntityName);
}
else
{
// there is no described mediator entity
// check table_name first, entity can not exist without it
if (empty($this->mediatorTableName))
{
throw new ArgumentException(sprintf(
'Table Name for mediator entity of relation `%s` between %s and %s was not found',
$this->name, $this->getEntity()->getObjectClass(), $this->getRefEntity()->getObjectClass()
));
}
// generate mediator entity runtime
if (empty($this->mediatorEntityName))
{
$localEntityName = $this->getEntity()->getName();
$remoteEntityName = $this->getRefEntity()->getName();
$fieldToClassName = Entity::snake2camel($this->name);
// each field has its own entity in case of ManyToMany definitions will be different
$this->mediatorEntityName = "MediatorFrom{$localEntityName}To{$remoteEntityName}Via{$fieldToClassName}Table";
}
// fields of mediator entity
$fields = [];
// local entity primary
$localReferenceConditions = Query::filter();
foreach ($this->getEntity()->getPrimaryArray() as $primaryName)
{
if (isset($this->localPrimaryNames[$primaryName]))
{
$mediatorPrimaryName = $this->localPrimaryNames[$primaryName];
}
else
{
$mediatorPrimaryName = $this->getLocalReferenceName().'_'.$primaryName;
}
$fieldType = get_class($this->getEntity()->getField($primaryName));
/** @var \Bitrix\Main\ORM\Fields\ScalarField $mediatorPrimary */
$mediatorPrimary = new $fieldType($mediatorPrimaryName);
$mediatorPrimary->configurePrimary(true);
$fields[] = $mediatorPrimary;
// save join condition for reference
$localReferenceConditions->whereColumn('this.'.$mediatorPrimaryName, 'ref.'.$primaryName);
}
// local reference
$localReference = (new Reference($this->getLocalReferenceName(), $this->getEntity(), $localReferenceConditions))
->configureJoinType('inner');
$fields[] = $localReference;
// remote entity primary
$remoteReferenceConditions = Query::filter();
foreach ($this->getRefEntity()->getPrimaryArray() as $primaryName)
{
if (isset($this->remotePrimaryNames[$primaryName]))
{
$mediatorPrimaryName = $this->remotePrimaryNames[$primaryName];
}
else
{
$mediatorPrimaryName = $this->getRemoteReferenceName().'_'.$primaryName;
}
$fieldType = get_class($this->getRefEntity()->getField($primaryName));
/** @var \Bitrix\Main\ORM\Fields\ScalarField $mediatorPrimary */
$mediatorPrimary = new $fieldType($mediatorPrimaryName);
$mediatorPrimary->configurePrimary(true);
$fields[] = $mediatorPrimary;
// save join condition for reference
$remoteReferenceConditions->whereColumn('this.'.$mediatorPrimaryName, 'ref.'.$primaryName);
}
// remote reference
$remoteReference = (new Reference($this->getRemoteReferenceName(), $this->getRefEntity(), $remoteReferenceConditions))
->configureJoinType('inner');
$fields[] = $remoteReference;
// set table name
$parameters = ['table_name' => $this->mediatorTableName];
// finalize
$this->mediatorEntity = Entity::compileEntity($this->mediatorEntityName, $fields, $parameters);
}
}
return $this->mediatorEntity;
}
/**
* @return string
*/
public function getLocalReferenceName()
{
if (empty($this->localReferenceName))
{
$this->localReferenceName = strtoupper(Entity::camel2snake($this->getEntity()->getName()));
}
return $this->localReferenceName;
}
/**
* Returns reference from mediator to owner entity
*
* @return \Bitrix\Main\ORM\Fields\Relations\Reference|\Bitrix\Main\ORM\Fields\Field
* @throws ArgumentException
* @throws SystemException
*/
public function getLocalReference()
{
return $this->getMediatorEntity()->getField($this->getLocalReferenceName());
}
/**
* @return string
* @throws ArgumentException
* @throws SystemException
*/
public function getRemoteReferenceName()
{
if (empty($this->remoteReferenceName))
{
$this->remoteReferenceName = strtoupper(Entity::camel2snake($this->getRefEntity()->getName()));
}
return $this->remoteReferenceName;
}
/**
* Returns reference from mediator to target entity
*
* @return \Bitrix\Main\ORM\Fields\Relations\Reference|\Bitrix\Main\ORM\Fields\Field
* @throws ArgumentException
* @throws SystemException
*/
public function getRemoteReference()
{
return $this->getMediatorEntity()->getField($this->getRemoteReferenceName());
}
}