1
|
|
|
<?php
|
2
|
|
|
|
3
|
|
|
declare(strict_types=1);
|
4
|
|
|
|
5
|
|
|
namespace Arp\DoctrineEntityRepository\Persistence;
|
6
|
|
|
|
7
|
|
|
use Arp\DoctrineEntityRepository\Exception\EntityRepositoryException;
|
8
|
|
|
use Arp\DoctrineEntityRepository\Persistence\Exception\PersistenceException;
|
9
|
|
|
use Arp\Entity\EntityInterface;
|
10
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
11
|
|
|
|
12
|
|
|
/**
|
13
|
|
|
* @author Alex Patterson <[email protected]>
|
14
|
|
|
* @package Arp\DoctrineEntityRepository\Persistence
|
15
|
|
|
*/
|
16
|
|
|
class CascadeDeleteService extends AbstractCascadeService
|
17
|
|
|
{
|
18
|
|
|
/**
|
19
|
|
|
* @param EntityManagerInterface $entityManager
|
20
|
|
|
* @param string $entityName
|
21
|
|
|
* @param EntityInterface $entity
|
22
|
|
|
* @param array $deleteOptions
|
23
|
|
|
* @param array $deleteCollectionOptions
|
24
|
|
|
*
|
25
|
|
|
* @throws EntityRepositoryException
|
26
|
|
|
* @throws PersistenceException
|
27
|
|
|
*/
|
28
|
|
|
public function deleteAssociations(
|
29
|
|
|
EntityManagerInterface $entityManager,
|
30
|
|
|
string $entityName,
|
31
|
|
|
EntityInterface $entity,
|
32
|
|
|
array $deleteOptions = [],
|
33
|
|
|
array $deleteCollectionOptions = []
|
34
|
|
|
): void {
|
35
|
|
|
$deleteOptions = array_replace_recursive($this->options, $deleteOptions);
|
36
|
|
|
$deleteCollectionOptions = array_replace_recursive($this->collectionOptions, $deleteCollectionOptions);
|
37
|
|
|
|
38
|
|
|
$classMetadata = $this->getClassMetadata($entityManager, $entityName);
|
39
|
|
|
$mappings = $classMetadata->getAssociationMappings();
|
40
|
|
|
|
41
|
|
|
$this->logger->info(
|
42
|
|
|
sprintf('Processing cascade delete operations for for entity class \'%s\'', $entityName)
|
43
|
|
|
);
|
44
|
|
|
|
45
|
|
|
foreach ($mappings as $mapping) {
|
46
|
|
|
if (
|
47
|
|
|
!isset(
|
48
|
|
|
$mapping['targetEntity'],
|
49
|
|
|
$mapping['fieldName'],
|
50
|
|
|
$mapping['type'],
|
51
|
|
|
$mapping['isCascadeRemove']
|
52
|
|
|
)
|
53
|
|
|
|| true !== $mapping['isCascadeRemove']
|
54
|
|
|
) {
|
55
|
|
|
// We only want to save associations that are configured to cascade delete/remove
|
56
|
|
|
continue;
|
57
|
|
|
}
|
58
|
|
|
|
59
|
|
|
$this->logger->info(
|
60
|
|
|
sprintf(
|
61
|
|
|
'The entity field \'%s::%s\' is configured for cascade delete operations for target entity \'%s\'',
|
62
|
|
|
$entityName,
|
63
|
|
|
$mappings['fieldName'],
|
64
|
|
|
$mappings['targetEntity']
|
65
|
|
|
)
|
66
|
|
|
);
|
67
|
|
|
|
68
|
|
|
$targetEntityOrCollection = $this->resolveTargetEntityOrCollection(
|
69
|
|
|
$entity,
|
70
|
|
|
$mapping['fieldName'],
|
71
|
|
|
$classMetadata,
|
72
|
|
|
$this->getClassMetadata($entityManager, $mapping['targetEntity'])
|
73
|
|
|
);
|
74
|
|
|
|
75
|
|
|
if (!$this->isValidAssociation($targetEntityOrCollection, $mapping)) {
|
76
|
|
|
$errorMessage = sprintf(
|
77
|
|
|
'The entity field \'%s::%s\' value could not be resolved',
|
78
|
|
|
$entityName,
|
79
|
|
|
$mappings['fieldName']
|
80
|
|
|
);
|
81
|
|
|
$this->logger->error($errorMessage);
|
82
|
|
|
|
83
|
|
|
throw new PersistenceException($errorMessage);
|
84
|
|
|
continue;
|
|
|
|
|
85
|
|
|
}
|
86
|
|
|
|
87
|
|
|
$this->logger->info(
|
88
|
|
|
sprintf(
|
89
|
|
|
'Performing cascading delete operations for field \'%s::%s\'',
|
90
|
|
|
$entityName,
|
91
|
|
|
$mappings['fieldName']
|
92
|
|
|
)
|
93
|
|
|
);
|
94
|
|
|
|
95
|
|
|
$this->deleteAssociation(
|
96
|
|
|
$entityManager,
|
97
|
|
|
$mapping['targetEntity'],
|
98
|
|
|
$targetEntityOrCollection,
|
99
|
|
|
(is_iterable($targetEntityOrCollection) ? $deleteCollectionOptions : $deleteOptions)
|
100
|
|
|
);
|
101
|
|
|
}
|
102
|
|
|
}
|
103
|
|
|
|
104
|
|
|
/**
|
105
|
|
|
* @param EntityManagerInterface $entityManager
|
106
|
|
|
* @param string $targetEntityName
|
107
|
|
|
* @param $entityOrCollection
|
108
|
|
|
* @param array $options
|
109
|
|
|
*
|
110
|
|
|
* @throws PersistenceException
|
111
|
|
|
* @throws EntityRepositoryException
|
112
|
|
|
*/
|
113
|
|
|
public function deleteAssociation(
|
114
|
|
|
EntityManagerInterface $entityManager,
|
115
|
|
|
string $targetEntityName,
|
116
|
|
|
$entityOrCollection,
|
117
|
|
|
array $options = []
|
118
|
|
|
): void {
|
119
|
|
|
$targetRepository = $this->getTargetRepository($entityManager, $targetEntityName);
|
120
|
|
|
|
121
|
|
|
if (is_iterable($entityOrCollection)) {
|
122
|
|
|
$targetRepository->deleteCollection($entityOrCollection, $options);
|
123
|
|
|
} elseif ($entityOrCollection instanceof EntityInterface) {
|
124
|
|
|
$targetRepository->delete($entityOrCollection, $options);
|
125
|
|
|
} else {
|
126
|
|
|
$errorMessage = sprintf(
|
127
|
|
|
'Unable to cascade save target entity \'%s\': The entity or collection is of an invalid type \'%s\'',
|
128
|
|
|
$targetEntityName,
|
129
|
|
|
(is_object($entityOrCollection) ? get_class($entityOrCollection) : gettype($entityOrCollection))
|
130
|
|
|
);
|
131
|
|
|
|
132
|
|
|
$this->logger->error($errorMessage);
|
133
|
|
|
|
134
|
|
|
throw new PersistenceException($errorMessage);
|
135
|
|
|
}
|
136
|
|
|
}
|
137
|
|
|
}
|
138
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.