forked from sofascore/purgatory-bundle
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAssociationResolver.php
More file actions
113 lines (95 loc) · 4.31 KB
/
AssociationResolver.php
File metadata and controls
113 lines (95 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<?php
declare(strict_types=1);
namespace Sofascore\PurgatoryBundle\Cache\PropertyResolver;
use Doctrine\ORM\Mapping\AssociationMapping;
use Doctrine\ORM\Mapping\ClassMetadata as ORMClassMetadata;
use Doctrine\ORM\Mapping\OneToOneOwningSideMapping;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Sofascore\PurgatoryBundle\Attribute\RouteParamValue\InverseValuesAwareInterface;
use Sofascore\PurgatoryBundle\Attribute\RouteParamValue\ValuesInterface;
use Sofascore\PurgatoryBundle\Cache\RouteMetadata\RouteMetadata;
use Sofascore\PurgatoryBundle\Cache\Subscription\PurgeSubscription;
use Sofascore\PurgatoryBundle\Exception\PropertyNotAccessibleException;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\PropertyInfo\PropertyReadInfo;
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
final class AssociationResolver implements SubscriptionResolverInterface
{
public function __construct(
private readonly PropertyReadInfoExtractorInterface $extractor,
) {
}
/**
* {@inheritDoc}
*/
public function resolveSubscription(
RouteMetadata $routeMetadata,
ClassMetadata $classMetadata,
array $routeParams,
string $target,
): \Generator {
if (!$classMetadata instanceof ORMClassMetadata || !$classMetadata->hasAssociation($target)) {
return false;
}
/** @var AssociationMapping|array{type: int, inversedBy?: ?string} $associationMapping */
$associationMapping = $classMetadata->getAssociationMapping($target);
$associationType = $associationMapping instanceof AssociationMapping
? $associationMapping->type()
: $associationMapping['type'];
if (ORMClassMetadata::ONE_TO_ONE === $associationType) {
if ($classMetadata->isAssociationInverseSide($target)) {
$associationTarget = $classMetadata->getAssociationMappedByTargetField($target);
} else {
/** @var ?string $associationTarget */
$associationTarget = $associationMapping instanceof OneToOneOwningSideMapping
? $associationMapping->inversedBy
: $associationMapping['inversedBy'] ?? null;
}
} elseif (ORMClassMetadata::ONE_TO_MANY === $associationType) {
$associationTarget = $classMetadata->getAssociationMappedByTargetField($target);
} else {
return false;
}
if (null === $associationTarget) {
return false;
}
$associationClass = $classMetadata->getAssociationTargetClass($target);
/** @var array<string, ValuesInterface> $inverseRouteParams */
$inverseRouteParams = [];
foreach ($routeParams as $routeParam => $values) {
$inverseRouteParams[$routeParam] = $this->getInverseValuesFor($values, $associationTarget);
}
if (null !== $if = $routeMetadata->purgeOn->if) {
$expression = (string) $if;
$getter = $this->createGetter($associationClass, $associationTarget);
$inverseIf = str_replace('obj', 'obj.'.$getter, $expression);
$if = new Expression("obj.$getter !== null && ($inverseIf)");
}
yield new PurgeSubscription(
class: $associationClass,
property: null,
routeParams: $inverseRouteParams,
routeName: $routeMetadata->routeName,
route: $routeMetadata->route,
actions: $routeMetadata->purgeOn->actions,
if: $if,
);
return true;
}
private function getInverseValuesFor(ValuesInterface $values, string $associationTarget): ValuesInterface
{
return $values instanceof InverseValuesAwareInterface ? $values->buildInverseValuesFor($associationTarget) : $values;
}
private function createGetter(string $class, string $property): string
{
if (null === $readInfo = $this->extractor->getReadInfo($class, $property)) {
throw new PropertyNotAccessibleException($class, $property);
}
/** @var PropertyReadInfo::TYPE_* $type */
$type = $readInfo->getType();
return match ($type) {
PropertyReadInfo::TYPE_METHOD => $readInfo->getName().'()',
PropertyReadInfo::TYPE_PROPERTY => $readInfo->getName(),
};
}
}