summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
committerpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
commitbf6655a534a6775d30cafa67bd801276bda1d98d (patch)
treec6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php
parent94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff)
downloadAppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php')
-rw-r--r--vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php281
1 files changed, 281 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php b/vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php
new file mode 100644
index 0000000..f28f3a9
--- /dev/null
+++ b/vendor/doctrine/orm/src/Query/ResultSetMappingBuilder.php
@@ -0,0 +1,281 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Query;
6
7use Doctrine\DBAL\Types\Type;
8use Doctrine\ORM\EntityManagerInterface;
9use Doctrine\ORM\Internal\SQLResultCasing;
10use Doctrine\ORM\Mapping\ClassMetadata;
11use Doctrine\ORM\Utility\PersisterHelper;
12use InvalidArgumentException;
13use Stringable;
14
15use function in_array;
16use function sprintf;
17
18/**
19 * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields.
20 */
21class ResultSetMappingBuilder extends ResultSetMapping implements Stringable
22{
23 use SQLResultCasing;
24
25 /**
26 * Picking this rename mode will register entity columns as is,
27 * as they are in the database. This can cause clashes when multiple
28 * entities are fetched that have columns with the same name.
29 */
30 public const COLUMN_RENAMING_NONE = 1;
31
32 /**
33 * Picking custom renaming allows the user to define the renaming
34 * of specific columns with a rename array that contains column names as
35 * keys and result alias as values.
36 */
37 public const COLUMN_RENAMING_CUSTOM = 2;
38
39 /**
40 * Incremental renaming uses a result set mapping internal counter to add a
41 * number to each column result, leading to uniqueness. This only works if
42 * you use {@see generateSelectClause()} to generate the SELECT clause for
43 * you.
44 */
45 public const COLUMN_RENAMING_INCREMENT = 3;
46
47 private int $sqlCounter = 0;
48
49 /** @psalm-param self::COLUMN_RENAMING_* $defaultRenameMode */
50 public function __construct(
51 private readonly EntityManagerInterface $em,
52 private readonly int $defaultRenameMode = self::COLUMN_RENAMING_NONE,
53 ) {
54 }
55
56 /**
57 * Adds a root entity and all of its fields to the result set.
58 *
59 * @param string $class The class name of the root entity.
60 * @param string $alias The unique alias to use for the root entity.
61 * @param string[] $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
62 * @psalm-param class-string $class
63 * @psalm-param array<string, string> $renamedColumns
64 * @psalm-param self::COLUMN_RENAMING_*|null $renameMode
65 */
66 public function addRootEntityFromClassMetadata(
67 string $class,
68 string $alias,
69 array $renamedColumns = [],
70 int|null $renameMode = null,
71 ): void {
72 $renameMode = $renameMode ?: $this->defaultRenameMode;
73 $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns);
74
75 $this->addEntityResult($class, $alias);
76 $this->addAllClassFields($class, $alias, $columnAliasMap);
77 }
78
79 /**
80 * Adds a joined entity and all of its fields to the result set.
81 *
82 * @param string $class The class name of the joined entity.
83 * @param string $alias The unique alias to use for the joined entity.
84 * @param string $parentAlias The alias of the entity result that is the parent of this joined result.
85 * @param string $relation The association field that connects the parent entity result
86 * with the joined entity result.
87 * @param string[] $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
88 * @psalm-param class-string $class
89 * @psalm-param array<string, string> $renamedColumns
90 * @psalm-param self::COLUMN_RENAMING_*|null $renameMode
91 */
92 public function addJoinedEntityFromClassMetadata(
93 string $class,
94 string $alias,
95 string $parentAlias,
96 string $relation,
97 array $renamedColumns = [],
98 int|null $renameMode = null,
99 ): void {
100 $renameMode = $renameMode ?: $this->defaultRenameMode;
101 $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns);
102
103 $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
104 $this->addAllClassFields($class, $alias, $columnAliasMap);
105 }
106
107 /**
108 * Adds all fields of the given class to the result set mapping (columns and meta fields).
109 *
110 * @param string[] $columnAliasMap
111 * @psalm-param array<string, string> $columnAliasMap
112 *
113 * @throws InvalidArgumentException
114 */
115 protected function addAllClassFields(string $class, string $alias, array $columnAliasMap = []): void
116 {
117 $classMetadata = $this->em->getClassMetadata($class);
118 $platform = $this->em->getConnection()->getDatabasePlatform();
119
120 if (! $this->isInheritanceSupported($classMetadata)) {
121 throw new InvalidArgumentException('ResultSetMapping builder does not currently support your inheritance scheme.');
122 }
123
124 foreach ($classMetadata->getColumnNames() as $columnName) {
125 $propertyName = $classMetadata->getFieldName($columnName);
126 $columnAlias = $this->getSQLResultCasing($platform, $columnAliasMap[$columnName]);
127
128 if (isset($this->fieldMappings[$columnAlias])) {
129 throw new InvalidArgumentException(sprintf(
130 "The column '%s' conflicts with another column in the mapper.",
131 $columnName,
132 ));
133 }
134
135 $this->addFieldResult($alias, $columnAlias, $propertyName);
136
137 $enumType = $classMetadata->getFieldMapping($propertyName)->enumType ?? null;
138 if (! empty($enumType)) {
139 $this->addEnumResult($columnAlias, $enumType);
140 }
141 }
142
143 foreach ($classMetadata->associationMappings as $associationMapping) {
144 if ($associationMapping->isToOneOwningSide()) {
145 $targetClass = $this->em->getClassMetadata($associationMapping->targetEntity);
146 $isIdentifier = isset($associationMapping->id) && $associationMapping->id === true;
147
148 foreach ($associationMapping->joinColumns as $joinColumn) {
149 $columnName = $joinColumn->name;
150 $columnAlias = $this->getSQLResultCasing($platform, $columnAliasMap[$columnName]);
151 $columnType = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em);
152
153 if (isset($this->metaMappings[$columnAlias])) {
154 throw new InvalidArgumentException(sprintf(
155 "The column '%s' conflicts with another column in the mapper.",
156 $columnAlias,
157 ));
158 }
159
160 $this->addMetaResult($alias, $columnAlias, $columnName, $isIdentifier, $columnType);
161 }
162 }
163 }
164 }
165
166 private function isInheritanceSupported(ClassMetadata $classMetadata): bool
167 {
168 if (
169 $classMetadata->isInheritanceTypeSingleTable()
170 && in_array($classMetadata->name, $classMetadata->discriminatorMap, true)
171 ) {
172 return true;
173 }
174
175 return ! ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined());
176 }
177
178 /**
179 * Gets column alias for a given column.
180 *
181 * @psalm-param array<string, string> $customRenameColumns
182 *
183 * @psalm-assert self::COLUMN_RENAMING_* $mode
184 */
185 private function getColumnAlias(string $columnName, int $mode, array $customRenameColumns): string
186 {
187 return match ($mode) {
188 self::COLUMN_RENAMING_INCREMENT => $columnName . $this->sqlCounter++,
189 self::COLUMN_RENAMING_CUSTOM => $customRenameColumns[$columnName] ?? $columnName,
190 self::COLUMN_RENAMING_NONE => $columnName,
191 default => throw new InvalidArgumentException(sprintf('%d is not a valid value for $mode', $mode)),
192 };
193 }
194
195 /**
196 * Retrieves a class columns and join columns aliases that are used in the SELECT clause.
197 *
198 * This depends on the renaming mode selected by the user.
199 *
200 * @psalm-param class-string $className
201 * @psalm-param self::COLUMN_RENAMING_* $mode
202 * @psalm-param array<string, string> $customRenameColumns
203 *
204 * @return string[]
205 * @psalm-return array<array-key, string>
206 */
207 private function getColumnAliasMap(
208 string $className,
209 int $mode,
210 array $customRenameColumns,
211 ): array {
212 if ($customRenameColumns) { // for BC with 2.2-2.3 API
213 $mode = self::COLUMN_RENAMING_CUSTOM;
214 }
215
216 $columnAlias = [];
217 $class = $this->em->getClassMetadata($className);
218
219 foreach ($class->getColumnNames() as $columnName) {
220 $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns);
221 }
222
223 foreach ($class->associationMappings as $associationMapping) {
224 if ($associationMapping->isToOneOwningSide()) {
225 foreach ($associationMapping->joinColumns as $joinColumn) {
226 $columnName = $joinColumn->name;
227 $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns);
228 }
229 }
230 }
231
232 return $columnAlias;
233 }
234
235 /**
236 * Generates the Select clause from this ResultSetMappingBuilder.
237 *
238 * Works only for all the entity results. The select parts for scalar
239 * expressions have to be written manually.
240 *
241 * @param string[] $tableAliases
242 * @psalm-param array<string, string> $tableAliases
243 */
244 public function generateSelectClause(array $tableAliases = []): string
245 {
246 $sql = '';
247
248 foreach ($this->columnOwnerMap as $columnName => $dqlAlias) {
249 $tableAlias = $tableAliases[$dqlAlias] ?? $dqlAlias;
250
251 if ($sql !== '') {
252 $sql .= ', ';
253 }
254
255 if (isset($this->fieldMappings[$columnName])) {
256 $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]);
257 $fieldName = $this->fieldMappings[$columnName];
258 $classFieldMapping = $class->fieldMappings[$fieldName];
259 $columnSql = $tableAlias . '.' . $classFieldMapping->columnName;
260
261 $type = Type::getType($classFieldMapping->type);
262 $columnSql = $type->convertToPHPValueSQL($columnSql, $this->em->getConnection()->getDatabasePlatform());
263
264 $sql .= $columnSql;
265 } elseif (isset($this->metaMappings[$columnName])) {
266 $sql .= $tableAlias . '.' . $this->metaMappings[$columnName];
267 } elseif (isset($this->discriminatorColumns[$dqlAlias])) {
268 $sql .= $tableAlias . '.' . $this->discriminatorColumns[$dqlAlias];
269 }
270
271 $sql .= ' AS ' . $columnName;
272 }
273
274 return $sql;
275 }
276
277 public function __toString(): string
278 {
279 return $this->generateSelectClause([]);
280 }
281}