180 lines
5.6 KiB
PHP
180 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Doctrine\ORM\Persisters\Entity;
|
|
|
|
use Doctrine\Common\Collections\Criteria;
|
|
use Doctrine\ORM\Internal\SQLResultCasing;
|
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
|
use Doctrine\ORM\Utility\PersisterHelper;
|
|
|
|
use function array_flip;
|
|
use function array_intersect;
|
|
use function array_map;
|
|
use function array_unshift;
|
|
use function implode;
|
|
|
|
/**
|
|
* Persister for entities that participate in a hierarchy mapped with the
|
|
* SINGLE_TABLE strategy.
|
|
*
|
|
* @link https://martinfowler.com/eaaCatalog/singleTableInheritance.html
|
|
*/
|
|
class SingleTablePersister extends AbstractEntityInheritancePersister
|
|
{
|
|
use SQLResultCasing;
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function getDiscriminatorColumnTableName()
|
|
{
|
|
return $this->class->getTableName();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function getSelectColumnsSQL()
|
|
{
|
|
if ($this->currentPersisterContext->selectColumnListSql !== null) {
|
|
return $this->currentPersisterContext->selectColumnListSql;
|
|
}
|
|
|
|
$columnList[] = parent::getSelectColumnsSQL();
|
|
|
|
$rootClass = $this->em->getClassMetadata($this->class->rootEntityName);
|
|
$tableAlias = $this->getSQLTableAlias($rootClass->name);
|
|
|
|
// Append discriminator column
|
|
$discrColumn = $this->class->getDiscriminatorColumn();
|
|
$discrColumnName = $discrColumn['name'];
|
|
$discrColumnType = $discrColumn['type'];
|
|
|
|
$columnList[] = $tableAlias . '.' . $discrColumnName;
|
|
|
|
$resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName);
|
|
|
|
$this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName);
|
|
$this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType);
|
|
|
|
// Append subclass columns
|
|
foreach ($this->class->subClasses as $subClassName) {
|
|
$subClass = $this->em->getClassMetadata($subClassName);
|
|
|
|
// Regular columns
|
|
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
|
if (isset($mapping['inherited'])) {
|
|
continue;
|
|
}
|
|
|
|
$columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);
|
|
}
|
|
|
|
// Foreign key columns
|
|
foreach ($subClass->associationMappings as $assoc) {
|
|
if (! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE) || isset($assoc['inherited'])) {
|
|
continue;
|
|
}
|
|
|
|
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
|
|
|
foreach ($assoc['joinColumns'] as $joinColumn) {
|
|
$columnList[] = $this->getSelectJoinColumnSQL(
|
|
$tableAlias,
|
|
$joinColumn['name'],
|
|
$this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform),
|
|
PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
|
|
|
|
return $this->currentPersisterContext->selectColumnListSql;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function getInsertColumnList()
|
|
{
|
|
$columns = parent::getInsertColumnList();
|
|
|
|
// Add discriminator column to the INSERT SQL
|
|
$columns[] = $this->class->getDiscriminatorColumn()['name'];
|
|
|
|
return $columns;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function getSQLTableAlias($className, $assocName = '')
|
|
{
|
|
return parent::getSQLTableAlias($this->class->rootEntityName, $assocName);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function getSelectConditionSQL(array $criteria, $assoc = null)
|
|
{
|
|
$conditionSql = parent::getSelectConditionSQL($criteria, $assoc);
|
|
|
|
if ($conditionSql) {
|
|
$conditionSql .= ' AND ';
|
|
}
|
|
|
|
return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function getSelectConditionCriteriaSQL(Criteria $criteria)
|
|
{
|
|
$conditionSql = parent::getSelectConditionCriteriaSQL($criteria);
|
|
|
|
if ($conditionSql) {
|
|
$conditionSql .= ' AND ';
|
|
}
|
|
|
|
return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL();
|
|
}
|
|
|
|
/** @return string */
|
|
protected function getSelectConditionDiscriminatorValueSQL()
|
|
{
|
|
$values = array_map(
|
|
[$this->conn, 'quote'],
|
|
array_flip(array_intersect($this->class->discriminatorMap, $this->class->subClasses))
|
|
);
|
|
|
|
if ($this->class->discriminatorValue !== null) { // discriminators can be 0
|
|
array_unshift($values, $this->conn->quote($this->class->discriminatorValue));
|
|
}
|
|
|
|
$discColumnName = $this->class->getDiscriminatorColumn()['name'];
|
|
|
|
$values = implode(', ', $values);
|
|
$tableAlias = $this->getSQLTableAlias($this->class->name);
|
|
|
|
return $tableAlias . '.' . $discColumnName . ' IN (' . $values . ')';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
|
|
{
|
|
// Ensure that the filters are applied to the root entity of the inheritance tree
|
|
$targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);
|
|
// we don't care about the $targetTableAlias, in a STI there is only one table.
|
|
|
|
return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias);
|
|
}
|
|
}
|