Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add doctrine/orm 3 support #590

Merged
merged 10 commits into from
Jul 14, 2024
4 changes: 0 additions & 4 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,3 @@ parameters:
# The class "AuditController" is deprecated and extends a class that is only supported in "symfony/http-foundation" < 5.
# NEXT_MAJOR: Remove these files
- src/Controller/AuditController.php
ignoreErrors:
# For compatibility with doctrine/orm 2 and 3
- '#.+ has invalid type Doctrine\\ORM\\Mapping\\AssociationMapping\.$#'
- '#^Property SimpleThings\\EntityAudit\\Collection\\AuditedCollection\:\:\$associationDefinition has unknown class Doctrine\\ORM\\Mapping\\AssociationMapping as its type\.$#'
7 changes: 0 additions & 7 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,4 @@
<code>Controller</code>
</UndefinedClass>
</file>
<!-- For compatibility with doctrine/orm 2 and 3 -->
<file src="src/EventListener/LogRevisionsListener.php">
<UndefinedClass>
<code><![CDATA[array<string, mixed>|AssociationMapping]]></code>
<code><![CDATA[array<string, mixed>|AssociationMapping]]></code>
</UndefinedClass>
</file>
</files>
111 changes: 61 additions & 50 deletions src/AuditReader.php

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/Collection/AuditedCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ class AuditedCollection implements Collection
protected $initialized = false;

/**
* @param string $class
* @param array<string, mixed> $associationDefinition
* @param array<string, mixed> $foreignKeys
* @param string|int $revision
* @param string $class
* @param array<string, mixed>|AssociationMapping $associationDefinition
* @param array<string, mixed> $foreignKeys
* @param string|int $revision
*
* @phpstan-param class-string<T> $class
* @phpstan-param ClassMetadata<T> $metadata
Expand Down
15 changes: 8 additions & 7 deletions src/DeferredChangedManyToManyEntityRevisionToPersist.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,24 @@
namespace SimpleThings\EntityAudit;

use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping;

/**
* @internal
*/
final class DeferredChangedManyToManyEntityRevisionToPersist
{
/**
* @param array<string, mixed> $assoc
* @param array<string, mixed> $entityData
* @param ClassMetadata<object> $class
* @param ClassMetadata<object> $targetClass
* @param array<string, mixed>|ManyToManyOwningSideMapping $assoc
* @param array<string, mixed> $entityData
* @param ClassMetadata<object> $class
* @param ClassMetadata<object> $targetClass
*/
public function __construct(
private object $entity,
private string $revType,
private array $entityData,
private array $assoc,
private array|ManyToManyOwningSideMapping $assoc,
private ClassMetadata $class,
private ClassMetadata $targetClass
) {
Expand All @@ -55,9 +56,9 @@ public function getEntityData(): array
}

/**
* @return array<string, mixed>
* @return array<string, mixed>|ManyToManyOwningSideMapping
*/
public function getAssoc(): array
public function getAssoc(): array|ManyToManyOwningSideMapping
{
return $this->assoc;
}
Expand Down
9 changes: 6 additions & 3 deletions src/EventListener/CreateSchemaListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ public function getSubscribedEvents()
];
}

/**
* @psalm-suppress TypeDoesNotContainType, NoValue
*/
public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs): void
{
$cm = $eventArgs->getClassMetadata();
Expand Down Expand Up @@ -112,10 +115,10 @@ public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs)

foreach ($cm->associationMappings as $associationMapping) {
if (self::isManyToManyOwningSideMapping($associationMapping)) {
if ($schema->hasTable(self::getJoinTableName($associationMapping))) {
$this->createRevisionJoinTableForJoinTable($schema, self::getJoinTableName($associationMapping));
if ($schema->hasTable(self::getMappingJoinTableNameValue($associationMapping))) {
$this->createRevisionJoinTableForJoinTable($schema, self::getMappingJoinTableNameValue($associationMapping));
} else {
$this->defferedJoinTablesToCreate[] = self::getJoinTableName($associationMapping);
$this->defferedJoinTablesToCreate[] = self::getMappingJoinTableNameValue($associationMapping);
}
}
}
Expand Down
81 changes: 23 additions & 58 deletions src/EventListener/LogRevisionsListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,8 @@ public function postFlush(PostFlushEventArgs $eventArgs): void
foreach ($meta->associationMappings as $mapping) {
if (isset($mapping['joinColumns'])) {
foreach ($mapping['joinColumns'] as $definition) {
if (self::getMappingValue($definition, 'name') === $column) {
/** @var class-string $targetEntity */
$targetEntity = $mapping['targetEntity'];
$targetTable = $em->getClassMetadata($targetEntity);
if (self::getMappingNameValue($definition) === $column) {
$targetTable = $em->getClassMetadata(self::getMappingTargetEntityValue($mapping));
$type = $targetTable->getTypeOfField($targetTable->getFieldForColumn(self::getMappingValue($definition, 'referencedColumnName')));
}
}
Expand All @@ -181,12 +179,10 @@ public function postFlush(PostFlushEventArgs $eventArgs): void

foreach ($meta->identifier as $idField) {
if (isset($meta->fieldMappings[$idField])) {
/** @phpstan-var literal-string $columnName */
$columnName = self::getMappingValue($meta->fieldMappings[$idField], 'columnName');
$columnName = self::getMappingColumnNameValue($meta->fieldMappings[$idField]);
$types[] = self::getMappingValue($meta->fieldMappings[$idField], 'type');
} elseif (isset($meta->associationMappings[$idField]['joinColumns'])) {
/** @phpstan-var literal-string $columnName */
$columnName = self::getMappingValue($meta->associationMappings[$idField]['joinColumns'][0], 'name');
$columnName = self::getMappingNameValue($meta->associationMappings[$idField]['joinColumns'][0]);
$types[] = $meta->associationMappings[$idField]['type'];
} else {
throw new \RuntimeException('column name not found for'.$idField);
Expand Down Expand Up @@ -400,8 +396,6 @@ private function getRevisionId(Connection $conn)
* @throws Exception
*
* @return literal-string
*
* @psalm-suppress MoreSpecificReturnType,PropertyTypeCoercion,LessSpecificReturnStatement https://github.com/vimeo/psalm/issues/10909
*/
private function getInsertRevisionSQL(EntityManagerInterface $em, ClassMetadata $class): string
{
Expand All @@ -420,8 +414,7 @@ private function getInsertRevisionSQL(EntityManagerInterface $em, ClassMetadata
}

if (self::isToOneOwningSide($assoc)) {
/** @phpstan-var literal-string $sourceCol */
foreach (self::getMappingValue($assoc, 'targetToSourceKeyColumns') as $sourceCol) {
foreach (self::getTargetToSourceKeyColumns($assoc) as $sourceCol) {
$fields[$sourceCol] = true;
$sql .= ', '.$sourceCol;
$placeholders[] = '?';
Expand Down Expand Up @@ -460,8 +453,7 @@ private function getInsertRevisionSQL(EntityManagerInterface $em, ClassMetadata
)
&& null !== $class->discriminatorColumn
) {
/** @var literal-string $discriminatorColumnName */
$discriminatorColumnName = self::getMappingValue($class->discriminatorColumn, 'name');
$discriminatorColumnName = self::getMappingNameValue($class->discriminatorColumn);
$sql .= ', '.$discriminatorColumnName;
$placeholders[] = '?';
}
Expand All @@ -480,16 +472,13 @@ private function getInsertRevisionSQL(EntityManagerInterface $em, ClassMetadata
* @param array<string, mixed>|ManyToManyOwningSideMapping $assoc
*
* @return literal-string
*
* @psalm-suppress MoreSpecificReturnType,PropertyTypeCoercion,LessSpecificReturnStatement https://github.com/vimeo/psalm/issues/10909
*/
private function getInsertJoinTableRevisionSQL(
ClassMetadata $class,
ClassMetadata $targetClass,
/* @phpstan-ignore-next-line */
array|ManyToManyOwningSideMapping $assoc
): string {
$joinTableName = self::getJoinTableName($assoc);
$joinTableName = self::getMappingJoinTableNameValue($assoc);
$cacheKey = $class->name.'.'.$targetClass->name.'.'.$joinTableName;

if (
Expand All @@ -499,27 +488,16 @@ private function getInsertJoinTableRevisionSQL(

$tableName = $this->config->getTablePrefix().$joinTableName.$this->config->getTableSuffix();

/** @psalm-trace $sql */
$sql = 'INSERT INTO '.$tableName
.' ('.$this->config->getRevisionFieldName().
', '.$this->config->getRevisionTypeFieldName();

/**
* @phpstan-var literal-string $sourceColumn
*
* @phpstan-ignore argument.type
*/
foreach (self::getMappingValue($assoc, 'relationToSourceKeyColumns') as $sourceColumn => $targetColumn) {
foreach (self::getRelationToSourceKeyColumns($assoc) as $sourceColumn => $targetColumn) {
$sql .= ', '.$sourceColumn;
$placeholders[] = '?';
}

/**
* @phpstan-var literal-string $sourceColumn
*
* @phpstan-ignore argument.type
*/
foreach (self::getMappingValue($assoc, 'relationToTargetKeyColumns') as $sourceColumn => $targetColumn) {
foreach (self::getRelationToTargetKeyColumns($assoc) as $sourceColumn => $targetColumn) {
$sql .= ', '.$sourceColumn;
$placeholders[] = '?';
}
Expand Down Expand Up @@ -551,21 +529,18 @@ private function saveRevisionEntityData(EntityManagerInterface $em, ClassMetadat
continue;
}

if ($assoc['isOwningSide']) {
if (0 !== ($assoc['type'] & ClassMetadata::TO_ONE)
&& isset($assoc['sourceToTargetKeyColumns'])) {
if (self::isOwningSide($assoc)) {
if (self::isToOneOwningSide($assoc)) {
$data = $entityData[$field] ?? null;
$relatedId = [];

if (\is_object($data) && $uow->isInIdentityMap($data)) {
$relatedId = $uow->getEntityIdentifier($data);
}

/** @var class-string $targetEntity */
$targetEntity = $assoc['targetEntity'];
$targetClass = $em->getClassMetadata($targetEntity);
$targetClass = $em->getClassMetadata(self::getMappingTargetEntityValue($assoc));

foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
foreach (self::getSourceToTargetKeyColumns($assoc) as $sourceColumn => $targetColumn) {
$fields[$sourceColumn] = true;
if (null === $data) {
$params[] = null;
Expand All @@ -575,9 +550,8 @@ private function saveRevisionEntityData(EntityManagerInterface $em, ClassMetadat
$types[] = $targetClass->getTypeOfField($targetClass->getFieldForColumn($targetColumn));
}
}
} elseif (($assoc['type'] & ClassMetadata::MANY_TO_MANY) > 0
&& isset($assoc['relationToSourceKeyColumns'], $assoc['relationToTargetKeyColumns'])) {
$targetClass = $em->getClassMetadata($assoc['targetEntity']);
} elseif (self::isManyToManyOwningSideMapping($assoc)) {
$targetClass = $em->getClassMetadata(self::getMappingTargetEntityValue($assoc));

$collection = $entityData[$assoc['fieldName']];
if (null !== $collection) {
Expand Down Expand Up @@ -622,19 +596,18 @@ private function saveRevisionEntityData(EntityManagerInterface $em, ClassMetadat
&& $class->name === $class->rootEntityName
&& null !== $class->discriminatorColumn
) {
$params[] = $entityData[self::getMappingValue($class->discriminatorColumn, 'name')];
$params[] = $entityData[self::getMappingNameValue($class->discriminatorColumn)];
$types[] = self::getMappingValue($class->discriminatorColumn, 'type');
}

if (
$class->isInheritanceTypeJoined() && $class->name !== $class->rootEntityName
&& null !== $class->discriminatorColumn
) {
$entityData[self::getMappingValue($class->discriminatorColumn, 'name')] = $class->discriminatorValue;
$entityData[self::getMappingNameValue($class->discriminatorColumn)] = $class->discriminatorValue;
$this->saveRevisionEntityData(
$em,
$em->getClassMetadata($class->rootEntityName),
/* @phpstan-ignore argument.type */
$entityData,
$revType
);
Expand All @@ -660,7 +633,6 @@ private function recordRevisionForManyToManyEntity(
EntityManagerInterface $em,
string $revType,
array $entityData,
/* @phpstan-ignore-next-line */
array|ManyToManyOwningSideMapping $assoc,
ClassMetadata $class,
ClassMetadata $targetClass
Expand All @@ -669,14 +641,12 @@ private function recordRevisionForManyToManyEntity(
$joinTableParams = [$this->getRevisionId($conn), $revType];
$joinTableTypes = [\PDO::PARAM_INT, \PDO::PARAM_STR];

/* @phpstan-ignore argument.type */
foreach (self::getMappingValue($assoc, 'relationToSourceKeyColumns') as $targetColumn) {
foreach (self::getRelationToSourceKeyColumns($assoc) as $targetColumn) {
$joinTableParams[] = $entityData[$class->fieldNames[$targetColumn]];
$joinTableTypes[] = PersisterHelper::getTypeOfColumn($targetColumn, $class, $em);
}

/* @phpstan-ignore argument.type */
foreach (self::getMappingValue($assoc, 'relationToTargetKeyColumns') as $targetColumn) {
foreach (self::getRelationToTargetKeyColumns($assoc) as $targetColumn) {
$reflField = $targetClass->reflFields[$targetClass->fieldNames[$targetColumn]];
\assert(null !== $reflField);
$joinTableParams[] = $reflField->getValue($relatedEntity);
Expand Down Expand Up @@ -740,7 +710,7 @@ private function prepareUpdateData(EntityManagerInterface $em, EntityPersister $
$newVal = $change[1];

if (!isset($classMetadata->associationMappings[$field])) {
$columnName = self::getMappingValue($classMetadata->fieldMappings[$field], 'columnName');
$columnName = self::getMappingColumnNameValue($classMetadata->fieldMappings[$field]);
$result[$persister->getOwningTable($field)][$columnName] = $newVal;

continue;
Expand All @@ -749,10 +719,7 @@ private function prepareUpdateData(EntityManagerInterface $em, EntityPersister $
$assoc = $classMetadata->associationMappings[$field];

// Only owning side of x-1 associations can have a FK column.
if (
0 === ($assoc['type'] & ClassMetadata::TO_ONE)
|| false === $assoc['isOwningSide']
|| !isset($assoc['joinColumns'])) {
if (!self::isToOneOwningSide($assoc)) {
continue;
}

Expand All @@ -772,13 +739,11 @@ private function prepareUpdateData(EntityManagerInterface $em, EntityPersister $
$newValId = $uow->getEntityIdentifier($newVal);
}

/** @var class-string $targetEntity */
$targetEntity = $assoc['targetEntity'];
$targetClass = $em->getClassMetadata($targetEntity);
$targetClass = $em->getClassMetadata(self::getMappingTargetEntityValue($assoc));
$owningTable = $persister->getOwningTable($field);

foreach ($assoc['joinColumns'] as $joinColumn) {
$sourceColumn = self::getMappingValue($joinColumn, 'name');
$sourceColumn = self::getMappingNameValue($joinColumn);
$targetColumn = self::getMappingValue($joinColumn, 'referencedColumnName');

$result[$owningTable][$sourceColumn] = null !== $newValId
Expand Down
Loading
Loading