• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 15966359730

30 Jun 2025 07:13AM UTC coverage: 22.064% (-0.02%) from 22.082%
15966359730

push

github

soyuka
ci: distribution update on new tag only

11518 of 52203 relevant lines covered (22.06%)

22.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Doctrine/Odm/State/LinksHandlerTrait.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Doctrine\Odm\State;
15

16
use ApiPlatform\Doctrine\Common\State\LinksHandlerTrait as CommonLinksHandlerTrait;
17
use ApiPlatform\Metadata\Exception\RuntimeException;
18
use ApiPlatform\Metadata\Link;
19
use ApiPlatform\Metadata\Operation;
20
use ApiPlatform\State\Util\StateOptionsTrait;
21
use Doctrine\ODM\MongoDB\Aggregation\Builder;
22
use Doctrine\ODM\MongoDB\DocumentManager;
23
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
24
use Doctrine\Persistence\ManagerRegistry;
25

26
/**
27
 * @internal
28
 */
29
trait LinksHandlerTrait
30
{
31
    use CommonLinksHandlerTrait;
32
    use StateOptionsTrait;
33

34
    private ManagerRegistry $managerRegistry;
35

36
    private function handleLinks(Builder $aggregationBuilder, array $identifiers, array $context, string $resourceClass, ?Operation $operation = null): void
37
    {
38
        if (!$identifiers) {
×
39
            return;
×
40
        }
41

42
        $links = $this->getLinks($resourceClass, $operation, $context);
×
43

44
        if (!$links) {
×
45
            return;
×
46
        }
47

48
        foreach ($links as $i => $link) {
×
49
            if (null !== $link->getExpandedValue()) {
×
50
                unset($links[$i]);
×
51
            }
52
        }
53

54
        $executeOptions = $operation->getExtraProperties()['doctrine_mongodb']['execute_options'] ?? [];
×
55

56
        $this->buildAggregation($resourceClass, array_reverse($links), array_reverse($identifiers), $context, $executeOptions, $resourceClass, $aggregationBuilder, $operation);
×
57
    }
58

59
    /**
60
     * @throws RuntimeException
61
     */
62
    private function buildAggregation(string $toClass, array $links, array $identifiers, array $context, array $executeOptions, string $previousAggregationClass, Builder $previousAggregationBuilder, Operation $operation): Builder
63
    {
64
        if (\count($links) <= 0) {
×
65
            return $previousAggregationBuilder;
×
66
        }
67

68
        /** @var Link $link */
69
        $link = array_shift($links);
×
70

71
        $fromClass = $link->getFromClass();
×
72
        $fromProperty = $link->getFromProperty();
×
73
        $toProperty = $link->getToProperty();
×
74
        $identifierProperties = $link->getIdentifiers();
×
75
        $hasCompositeIdentifiers = 1 < \count($identifierProperties);
×
76

77
        $aggregationClass = $fromClass;
×
78
        if ($toProperty) {
×
79
            $aggregationClass = $toClass;
×
80
        }
81

82
        $lookupProperty = $toProperty ?? $fromProperty;
×
83
        $lookupPropertyAlias = $lookupProperty ? "{$lookupProperty}_lkup" : null;
×
84

85
        $manager = $this->managerRegistry->getManagerForClass($aggregationClass);
×
86
        if (!$manager instanceof DocumentManager) {
×
87
            $aggregationClass = $this->getLinkFromClass($link, $operation);
×
88
            $manager = $this->managerRegistry->getManagerForClass($aggregationClass);
×
89

90
            if (!$manager instanceof DocumentManager) {
×
91
                throw new RuntimeException(\sprintf('The manager for "%s" must be an instance of "%s".', $aggregationClass, DocumentManager::class));
×
92
            }
93
        }
94

95
        $classMetadata = $manager->getClassMetadata($aggregationClass);
×
96

97
        if (!$classMetadata instanceof ClassMetadata) {
×
98
            throw new RuntimeException(\sprintf('The class metadata for "%s" must be an instance of "%s".', $aggregationClass, ClassMetadata::class));
×
99
        }
100

101
        $aggregation = $previousAggregationBuilder;
×
102
        if ($aggregationClass !== $previousAggregationClass) {
×
103
            $aggregation = $manager->createAggregationBuilder($aggregationClass);
×
104
        }
105

106
        if ($lookupProperty && $classMetadata->hasAssociation($lookupProperty)) {
×
107
            $aggregation->lookup($lookupProperty)->alias($lookupPropertyAlias);
×
108
        }
109

110
        if ($toProperty) {
×
111
            foreach ($identifierProperties as $identifierProperty) {
×
112
                $aggregation->match()->field(\sprintf('%s.%s', $lookupPropertyAlias, 'id' === $identifierProperty ? '_id' : $identifierProperty))->equals($this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
×
113
            }
114
        } else {
115
            foreach ($identifierProperties as $identifierProperty) {
×
116
                $aggregation->match()->field($identifierProperty)->equals($this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
×
117
            }
118
        }
119

120
        // Recurse aggregations
121
        $aggregation = $this->buildAggregation($fromClass, $links, $identifiers, $context, $executeOptions, $aggregationClass, $aggregation, $operation);
×
122

123
        if (null === $fromProperty || null !== $toProperty) {
×
124
            return $aggregation;
×
125
        }
126

127
        $results = $aggregation->getAggregation($executeOptions)->getIterator()->toArray();
×
128
        $in = [];
×
129
        foreach ($results as $result) {
×
130
            foreach ($result[$lookupPropertyAlias] ?? [] as $lookupResult) {
×
131
                $in[] = $lookupResult['_id'];
×
132
            }
133
        }
134
        $previousAggregationBuilder->match()->field('_id')->in($in);
×
135

136
        return $previousAggregationBuilder;
×
137
    }
138

139
    private function getLinkFromClass(Link $link, Operation $operation): string
140
    {
141
        $documentClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class);
×
142
        $fromClass = $link->getFromClass();
×
143
        if ($fromClass === $operation->getClass() && $documentClass) {
×
144
            return $documentClass;
×
145
        }
146

147
        $operation = $this->resourceMetadataCollectionFactory->create($fromClass)->getOperation();
×
148

149
        if ($documentClass = $this->getStateOptionsClass($operation, null, Options::class)) {
×
150
            return $documentClass;
×
151
        }
152

153
        throw new \Exception('Can not found a doctrine class for this link.');
×
154
    }
155
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc