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

api-platform / core / 15063570239

16 May 2025 07:56AM UTC coverage: 27.494% (-0.001%) from 27.495%
15063570239

push

github

web-flow
fix: error formats (#7148)

0 of 2 new or added lines in 2 files covered. (0.0%)

11139 existing lines in 371 files now uncovered.

13476 of 49015 relevant lines covered (27.49%)

74.77 hits per line

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

94.19
/src/Doctrine/Orm/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\Orm\State;
15

16
use ApiPlatform\Doctrine\Common\State\LinksHandlerTrait as CommonLinksHandlerTrait;
17
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
18
use ApiPlatform\Metadata\Link;
19
use ApiPlatform\Metadata\Operation;
20
use ApiPlatform\State\Util\StateOptionsTrait;
21
use Doctrine\ORM\Mapping\ClassMetadata;
22
use Doctrine\ORM\QueryBuilder;
23
use Doctrine\Persistence\ManagerRegistry;
24

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

33
    private ManagerRegistry $managerRegistry;
34

35
    private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, QueryNameGeneratorInterface $queryNameGenerator, array $context, string $entityClass, Operation $operation): void
36
    {
UNCOV
37
        if (!$identifiers) {
772✔
UNCOV
38
            return;
471✔
39
        }
40

UNCOV
41
        $manager = $this->managerRegistry->getManagerForClass($entityClass);
331✔
UNCOV
42
        $doctrineClassMetadata = $manager->getClassMetadata($entityClass);
331✔
UNCOV
43
        $alias = $queryBuilder->getRootAliases()[0];
331✔
44

UNCOV
45
        $links = $this->getLinks($entityClass, $operation, $context);
331✔
46

UNCOV
47
        if (!$links) {
331✔
48
            return;
×
49
        }
50

UNCOV
51
        $previousAlias = $alias;
331✔
UNCOV
52
        $previousJoinProperties = $doctrineClassMetadata->getIdentifierFieldNames();
331✔
UNCOV
53
        $expressions = [];
331✔
UNCOV
54
        $identifiers = array_reverse($identifiers);
331✔
55

UNCOV
56
        foreach (array_reverse($links) as $link) {
331✔
UNCOV
57
            if (null !== $link->getExpandedValue() || !$link->getFromClass()) {
331✔
58
                continue;
2✔
59
            }
60

UNCOV
61
            $fromClass = $link->getFromClass();
331✔
UNCOV
62
            if (!$this->managerRegistry->getManagerForClass($fromClass)) {
331✔
63
                $fromClass = $this->getLinkFromClass($link, $operation);
4✔
64
            }
65

UNCOV
66
            $fromClassMetadata = $manager->getClassMetadata($fromClass);
331✔
UNCOV
67
            $identifierProperties = $link->getIdentifiers();
331✔
UNCOV
68
            $hasCompositeIdentifiers = 1 < \count($identifierProperties);
331✔
69

UNCOV
70
            if (!$link->getFromProperty() && !$link->getToProperty()) {
331✔
UNCOV
71
                $currentAlias = $fromClass === $entityClass ? $alias : $queryNameGenerator->generateJoinAlias($alias);
296✔
72

UNCOV
73
                foreach ($identifierProperties as $identifierProperty) {
296✔
UNCOV
74
                    $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
296✔
UNCOV
75
                    $queryBuilder->andWhere("$currentAlias.$identifierProperty = :$placeholder");
296✔
UNCOV
76
                    $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
296✔
77
                }
78

UNCOV
79
                $previousAlias = $currentAlias;
296✔
UNCOV
80
                $previousJoinProperties = $fromClassMetadata->getIdentifierFieldNames();
296✔
UNCOV
81
                continue;
296✔
82
            }
83

84
            $joinProperties = $doctrineClassMetadata->getIdentifierFieldNames();
60✔
85

86
            if ($link->getFromProperty() && !$link->getToProperty()) {
60✔
87
                $joinAlias = $queryNameGenerator->generateJoinAlias('m');
30✔
88
                $associationMapping = $fromClassMetadata->getAssociationMapping($link->getFromProperty()); // @phpstan-ignore-line
30✔
89
                $relationType = $associationMapping['type'];
30✔
90

91
                if ($relationType & ClassMetadata::TO_MANY) {
30✔
92
                    $nextAlias = $queryNameGenerator->generateJoinAlias($alias);
21✔
93
                    $whereClause = [];
21✔
94
                    foreach ($identifierProperties as $identifierProperty) {
21✔
95
                        $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
21✔
96
                        $whereClause[] = "$nextAlias.{$identifierProperty} = :$placeholder";
21✔
97
                        $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
21✔
98
                    }
99

100
                    $property = $associationMapping['mappedBy'] ?? $joinProperties[0];
21✔
101
                    $select = isset($associationMapping['mappedBy']) ? "IDENTITY($joinAlias.$property)" : "$joinAlias.$property";
21✔
102
                    $expressions["$previousAlias.{$property}"] = "SELECT $select FROM {$fromClass} $nextAlias INNER JOIN $nextAlias.{$associationMapping['fieldName']} $joinAlias WHERE ".implode(' AND ', $whereClause);
21✔
103
                    $previousAlias = $nextAlias;
21✔
104
                    continue;
21✔
105
                }
106

107
                // A single-valued association path expression to an inverse side is not supported in DQL queries.
108
                if ($relationType & ClassMetadata::TO_ONE && !($associationMapping['isOwningSide'] ?? true)) {
11✔
109
                    $queryBuilder->innerJoin("$previousAlias.".$associationMapping['mappedBy'], $joinAlias);
1✔
110
                } else {
111
                    $queryBuilder->join(
10✔
112
                        $fromClass,
10✔
113
                        $joinAlias,
10✔
114
                        'WITH',
10✔
115
                        "$previousAlias.{$previousJoinProperties[0]} = $joinAlias.{$associationMapping['fieldName']}"
10✔
116
                    );
10✔
117
                }
118

119
                foreach ($identifierProperties as $identifierProperty) {
11✔
120
                    $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
11✔
121
                    $queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
11✔
122
                    $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
11✔
123
                }
124

125
                $previousAlias = $joinAlias;
11✔
126
                $previousJoinProperties = $joinProperties;
11✔
127
                continue;
11✔
128
            }
129

130
            $joinAlias = $queryNameGenerator->generateJoinAlias($alias);
33✔
131
            $queryBuilder->join("{$previousAlias}.{$link->getToProperty()}", $joinAlias);
33✔
132

133
            foreach ($identifierProperties as $identifierProperty) {
33✔
134
                $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
33✔
135
                $queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
33✔
136
                $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
33✔
137
            }
138

139
            $previousAlias = $joinAlias;
33✔
140
            $previousJoinProperties = $joinProperties;
33✔
141
        }
142

UNCOV
143
        if ($expressions) {
331✔
144
            $i = 0;
21✔
145
            $clause = '';
21✔
146
            foreach ($expressions as $alias => $expression) {
21✔
147
                if (0 === $i) {
21✔
148
                    $clause .= "$alias IN (".$expression;
21✔
149
                    ++$i;
21✔
150
                    continue;
21✔
151
                }
152

153
                $clause .= " AND $alias IN (".$expression;
×
154
                ++$i;
×
155
            }
156

157
            $queryBuilder->andWhere($clause.str_repeat(')', $i));
21✔
158
        }
159
    }
160

161
    private function getLinkFromClass(Link $link, Operation $operation): string
162
    {
163
        $fromClass = $link->getFromClass();
4✔
164
        if ($fromClass === $operation->getClass() && $entityClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class)) {
4✔
165
            return $entityClass;
4✔
166
        }
167

168
        $operation = $this->resourceMetadataCollectionFactory->create($fromClass)->getOperation();
×
169

170
        return $this->getStateOptionsClass($operation, $operation->getClass(), Options::class);
×
171
    }
172
}
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