This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | /* |
||
6 | * This file is part of the Sonata Project package. |
||
7 | * |
||
8 | * (c) Thomas Rabaix <[email protected]> |
||
9 | * |
||
10 | * For the full copyright and license information, please view the LICENSE |
||
11 | * file that was distributed with this source code. |
||
12 | */ |
||
13 | |||
14 | namespace Sonata\DoctrineMongoDBAdminBundle\Model; |
||
15 | |||
16 | use Doctrine\Common\Collections\ArrayCollection; |
||
17 | use Doctrine\Common\Persistence\Mapping\ClassMetadata as CommonClassMetadata; |
||
18 | use Doctrine\ODM\MongoDB\Query\Builder; |
||
19 | use Doctrine\Persistence\Mapping\ClassMetadata; |
||
20 | use Sonata\AdminBundle\Admin\FieldDescriptionInterface; |
||
21 | use Sonata\AdminBundle\Datagrid\DatagridInterface; |
||
22 | use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; |
||
23 | use Sonata\AdminBundle\Model\ModelManagerInterface; |
||
24 | use Sonata\DoctrineMongoDBAdminBundle\Admin\FieldDescription; |
||
25 | use Sonata\DoctrineMongoDBAdminBundle\Datagrid\ProxyQuery; |
||
26 | use Sonata\Exporter\Source\DoctrineODMQuerySourceIterator; |
||
27 | use Symfony\Bridge\Doctrine\ManagerRegistry; |
||
28 | use Symfony\Component\PropertyAccess\PropertyAccess; |
||
29 | use Symfony\Component\PropertyAccess\PropertyAccessorInterface; |
||
30 | |||
31 | class ModelManager implements ModelManagerInterface |
||
32 | { |
||
33 | public const ID_SEPARATOR = '-'; |
||
34 | |||
35 | /** |
||
36 | * @var ManagerRegistry |
||
37 | */ |
||
38 | protected $registry; |
||
39 | |||
40 | /** |
||
41 | * @var PropertyAccessorInterface |
||
42 | */ |
||
43 | private $propertyAccessor; |
||
44 | |||
45 | /** |
||
46 | * NEXT_MAJOR: Make $propertyAccessor mandatory. |
||
47 | */ |
||
48 | public function __construct(ManagerRegistry $registry, ?PropertyAccessorInterface $propertyAccessor = null) |
||
49 | { |
||
50 | $this->registry = $registry; |
||
51 | |||
52 | // NEXT_MAJOR: Remove this block. |
||
53 | if (!$propertyAccessor instanceof PropertyAccessorInterface) { |
||
54 | @trigger_error(sprintf( |
||
0 ignored issues
–
show
|
|||
55 | 'Not passing an object implementing "%s" as argument 2 for "%s()" is deprecated since' |
||
56 | .' sonata-project/doctrine-mongodb-admin-bundle 3.x and will throw a %s error in 4.0.', |
||
57 | PropertyAccessorInterface::class, |
||
58 | __METHOD__, |
||
59 | \TypeError::class |
||
60 | ), E_USER_DEPRECATED); |
||
61 | |||
62 | $propertyAccessor = PropertyAccess::createPropertyAccessor(); |
||
63 | } |
||
64 | |||
65 | $this->propertyAccessor = $propertyAccessor; |
||
66 | } |
||
67 | |||
68 | public function getMetadata($class) |
||
69 | { |
||
70 | return $this->getDocumentManager($class)->getMetadataFactory()->getMetadataFor($class); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Returns the model's metadata holding the fully qualified property, and the last |
||
75 | * property name. |
||
76 | * |
||
77 | * @param string $baseClass The base class of the model holding the fully qualified property |
||
78 | * @param string $propertyFullName The name of the fully qualified property (dot ('.') separated |
||
79 | * property string) |
||
80 | * |
||
81 | * @return array( |
||
0 ignored issues
–
show
The doc-type
array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.
Loading history...
|
|||
82 | * \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $parentMetadata, |
||
83 | * string $lastPropertyName, |
||
84 | * array $parentAssociationMappings |
||
85 | * ) |
||
86 | */ |
||
87 | public function getParentMetadataForProperty($baseClass, $propertyFullName) |
||
88 | { |
||
89 | $nameElements = explode('.', $propertyFullName); |
||
90 | $lastPropertyName = array_pop($nameElements); |
||
91 | $class = $baseClass; |
||
92 | $parentAssociationMappings = []; |
||
93 | |||
94 | foreach ($nameElements as $nameElement) { |
||
95 | $metadata = $this->getMetadata($class); |
||
96 | $parentAssociationMappings[] = $metadata->associationMappings[$nameElement]; |
||
97 | $class = $metadata->getAssociationTargetClass($nameElement); |
||
98 | } |
||
99 | |||
100 | return [$this->getMetadata($class), $lastPropertyName, $parentAssociationMappings]; |
||
101 | } |
||
102 | |||
103 | public function hasMetadata($class) |
||
104 | { |
||
105 | return $this->getDocumentManager($class)->getMetadataFactory()->hasMetadataFor($class); |
||
106 | } |
||
107 | |||
108 | public function getNewFieldDescriptionInstance($class, $name, array $options = []) |
||
109 | { |
||
110 | if (!\is_string($name)) { |
||
111 | throw new \RuntimeException('The name argument must be a string'); |
||
112 | } |
||
113 | |||
114 | if (!isset($options['route']['name'])) { |
||
115 | $options['route']['name'] = 'edit'; |
||
116 | } |
||
117 | |||
118 | if (!isset($options['route']['parameters'])) { |
||
119 | $options['route']['parameters'] = []; |
||
120 | } |
||
121 | |||
122 | [$metadata, $propertyName, $parentAssociationMappings] = $this->getParentMetadataForProperty($class, $name); |
||
0 ignored issues
–
show
The variable
$propertyName does not exist. Did you forget to declare it?
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.
Loading history...
The variable
$parentAssociationMappings does not exist. Did you forget to declare it?
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.
Loading history...
|
|||
123 | |||
124 | $fieldDescription = new FieldDescription(); |
||
125 | $fieldDescription->setName($name); |
||
126 | $fieldDescription->setOptions($options); |
||
127 | $fieldDescription->setParentAssociationMappings($parentAssociationMappings); |
||
128 | |||
129 | /* @var ClassMetadata */ |
||
130 | if (isset($metadata->associationMappings[$propertyName])) { |
||
131 | $fieldDescription->setAssociationMapping($metadata->associationMappings[$propertyName]); |
||
132 | } |
||
133 | |||
134 | if (isset($metadata->fieldMappings[$propertyName])) { |
||
135 | $fieldDescription->setFieldMapping($metadata->fieldMappings[$propertyName]); |
||
136 | } |
||
137 | |||
138 | return $fieldDescription; |
||
139 | } |
||
140 | |||
141 | public function create($object): void |
||
142 | { |
||
143 | $documentManager = $this->getDocumentManager($object); |
||
144 | $documentManager->persist($object); |
||
145 | $documentManager->flush(); |
||
146 | } |
||
147 | |||
148 | public function update($object): void |
||
149 | { |
||
150 | $documentManager = $this->getDocumentManager($object); |
||
151 | $documentManager->persist($object); |
||
152 | $documentManager->flush(); |
||
153 | } |
||
154 | |||
155 | public function delete($object): void |
||
156 | { |
||
157 | $documentManager = $this->getDocumentManager($object); |
||
158 | $documentManager->remove($object); |
||
159 | $documentManager->flush(); |
||
160 | } |
||
161 | |||
162 | public function find($class, $id) |
||
163 | { |
||
164 | if (!isset($id)) { |
||
165 | return null; |
||
166 | } |
||
167 | |||
168 | $documentManager = $this->getDocumentManager($class); |
||
169 | |||
170 | if (is_numeric($id)) { |
||
171 | $value = $documentManager->getRepository($class)->find((int) $id); |
||
172 | |||
173 | if (!empty($value)) { |
||
174 | return $value; |
||
175 | } |
||
176 | } |
||
177 | |||
178 | return $documentManager->getRepository($class)->find($id); |
||
179 | } |
||
180 | |||
181 | public function findBy($class, array $criteria = []) |
||
182 | { |
||
183 | return $this->getDocumentManager($class)->getRepository($class)->findBy($criteria); |
||
184 | } |
||
185 | |||
186 | public function findOneBy($class, array $criteria = []) |
||
187 | { |
||
188 | return $this->getDocumentManager($class)->getRepository($class)->findOneBy($criteria); |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * @param object|string $class |
||
193 | * |
||
194 | * @throw \RuntimeException |
||
195 | * |
||
196 | * @return \Doctrine\ODM\MongoDB\DocumentManager |
||
197 | */ |
||
198 | public function getDocumentManager($class) |
||
199 | { |
||
200 | if (\is_object($class)) { |
||
201 | $class = \get_class($class); |
||
202 | } |
||
203 | |||
204 | $dm = $this->registry->getManagerForClass($class); |
||
205 | |||
206 | if (!$dm) { |
||
207 | throw new \RuntimeException(sprintf('No document manager defined for class %s', $class)); |
||
208 | } |
||
209 | |||
210 | return $dm; |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * NEXT_MAJOR: Remove this method. |
||
215 | * |
||
216 | * @deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x and will be removed in version 4.0 |
||
217 | */ |
||
218 | public function getParentFieldDescription($parentAssociationMapping, $class) |
||
219 | { |
||
220 | @trigger_error(sprintf( |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
221 | 'Method "%s()" is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x' |
||
222 | .' and will be removed in 4.0', |
||
223 | __METHOD__ |
||
224 | ), E_USER_DEPRECATED); |
||
225 | |||
226 | $fieldName = $parentAssociationMapping['fieldName']; |
||
227 | |||
228 | $metadata = $this->getMetadata($class); |
||
229 | |||
230 | $associatingMapping = $metadata->associationMappings[$parentAssociationMapping]; |
||
231 | |||
232 | $fieldDescription = $this->getNewFieldDescriptionInstance($class, $fieldName); |
||
233 | $fieldDescription->setName($parentAssociationMapping); |
||
234 | $fieldDescription->setAssociationMapping($associatingMapping); |
||
235 | |||
236 | return $fieldDescription; |
||
237 | } |
||
238 | |||
239 | public function createQuery($class, $alias = 'o') |
||
240 | { |
||
241 | $repository = $this->getDocumentManager($class)->getRepository($class); |
||
242 | |||
243 | return new ProxyQuery($repository->createQueryBuilder()); |
||
244 | } |
||
245 | |||
246 | public function executeQuery($query) |
||
247 | { |
||
248 | if ($query instanceof Builder) { |
||
249 | return $query->getQuery()->execute(); |
||
250 | } |
||
251 | |||
252 | return $query->execute(); |
||
253 | } |
||
254 | |||
255 | public function getModelIdentifier($class) |
||
256 | { |
||
257 | return $this->getMetadata($class)->identifier; |
||
258 | } |
||
259 | |||
260 | public function getIdentifierValues($document) |
||
261 | { |
||
262 | return [$this->getDocumentManager($document)->getUnitOfWork()->getDocumentIdentifier($document)]; |
||
263 | } |
||
264 | |||
265 | public function getIdentifierFieldNames($class) |
||
266 | { |
||
267 | return [$this->getMetadata($class)->getIdentifier()]; |
||
268 | } |
||
269 | |||
270 | public function getNormalizedIdentifier($document) |
||
271 | { |
||
272 | if (null === $document) { |
||
273 | return null; |
||
274 | } |
||
275 | |||
276 | if (!\is_object($document)) { |
||
277 | throw new \RuntimeException('Invalid argument, object or null required'); |
||
278 | } |
||
279 | |||
280 | // the document is not managed |
||
281 | if (!$this->getDocumentManager($document)->contains($document)) { |
||
282 | return null; |
||
283 | } |
||
284 | |||
285 | $values = $this->getIdentifierValues($document); |
||
286 | |||
287 | return implode(self::ID_SEPARATOR, $values); |
||
288 | } |
||
289 | |||
290 | public function getUrlSafeIdentifier($document) |
||
291 | { |
||
292 | return $this->getNormalizedIdentifier($document); |
||
293 | } |
||
294 | |||
295 | public function addIdentifiersToQuery($class, ProxyQueryInterface $queryProxy, array $idx): void |
||
296 | { |
||
297 | $queryBuilder = $queryProxy->getQueryBuilder(); |
||
298 | $queryBuilder->field('_id')->in($idx); |
||
299 | } |
||
300 | |||
301 | public function batchDelete($class, ProxyQueryInterface $queryProxy): void |
||
302 | { |
||
303 | /** @var Query $queryBuilder */ |
||
304 | $queryBuilder = $queryProxy->getQuery(); |
||
305 | |||
306 | $documentManager = $this->getDocumentManager($class); |
||
307 | |||
308 | $i = 0; |
||
309 | foreach ($queryBuilder->execute() as $object) { |
||
310 | $documentManager->remove($object); |
||
311 | |||
312 | if (0 === (++$i % 20)) { |
||
313 | $documentManager->flush(); |
||
314 | $documentManager->clear(); |
||
315 | } |
||
316 | } |
||
317 | |||
318 | $documentManager->flush(); |
||
319 | $documentManager->clear(); |
||
320 | } |
||
321 | |||
322 | public function getDataSourceIterator(DatagridInterface $datagrid, array $fields, $firstResult = null, $maxResult = null) |
||
323 | { |
||
324 | $datagrid->buildPager(); |
||
325 | $query = $datagrid->getQuery(); |
||
326 | |||
327 | $query->setFirstResult($firstResult); |
||
328 | $query->setMaxResults($maxResult); |
||
329 | |||
330 | return new DoctrineODMQuerySourceIterator($query instanceof ProxyQuery ? $query->getQuery() : $query, $fields); |
||
0 ignored issues
–
show
The method
getQuery() does not exist on Sonata\DoctrineMongoDBAd...dle\Datagrid\ProxyQuery . Did you maybe mean getQueryBuilder() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise.
Loading history...
|
|||
331 | } |
||
332 | |||
333 | public function getExportFields($class) |
||
334 | { |
||
335 | $metadata = $this->getDocumentManager($class)->getClassMetadata($class); |
||
336 | |||
337 | return $metadata->getFieldNames(); |
||
338 | } |
||
339 | |||
340 | public function getModelInstance($class) |
||
341 | { |
||
342 | if (!class_exists($class)) { |
||
343 | throw new \InvalidArgumentException(sprintf('Class "%s" not found', $class)); |
||
344 | } |
||
345 | |||
346 | $r = new \ReflectionClass($class); |
||
347 | if ($r->isAbstract()) { |
||
348 | throw new \InvalidArgumentException(sprintf('Cannot initialize abstract class: %s', $class)); |
||
349 | } |
||
350 | |||
351 | $constructor = $r->getConstructor(); |
||
352 | |||
353 | if (null !== $constructor && (!$constructor->isPublic() || $constructor->getNumberOfRequiredParameters() > 0)) { |
||
354 | return $r->newInstanceWithoutConstructor(); |
||
355 | } |
||
356 | |||
357 | return new $class(); |
||
358 | } |
||
359 | |||
360 | public function getSortParameters(FieldDescriptionInterface $fieldDescription, DatagridInterface $datagrid) |
||
361 | { |
||
362 | $values = $datagrid->getValues(); |
||
363 | |||
364 | if ($this->isFieldAlreadySorted($fieldDescription, $datagrid)) { |
||
365 | if ('ASC' === $values['_sort_order']) { |
||
366 | $values['_sort_order'] = 'DESC'; |
||
367 | } else { |
||
368 | $values['_sort_order'] = 'ASC'; |
||
369 | } |
||
370 | } else { |
||
371 | $values['_sort_order'] = 'ASC'; |
||
372 | } |
||
373 | |||
374 | $values['_sort_by'] = \is_string($fieldDescription->getOption('sortable')) ? $fieldDescription->getOption('sortable') : $fieldDescription->getName(); |
||
375 | |||
376 | return ['filter' => $values]; |
||
377 | } |
||
378 | |||
379 | public function getPaginationParameters(DatagridInterface $datagrid, $page) |
||
380 | { |
||
381 | $values = $datagrid->getValues(); |
||
382 | |||
383 | if (isset($values['_sort_by']) && $values['_sort_by'] instanceof FieldDescriptionInterface) { |
||
384 | $values['_sort_by'] = $values['_sort_by']->getName(); |
||
385 | } |
||
386 | $values['_page'] = $page; |
||
387 | |||
388 | return ['filter' => $values]; |
||
389 | } |
||
390 | |||
391 | public function getDefaultSortValues($class) |
||
392 | { |
||
393 | return [ |
||
394 | '_page' => 1, |
||
395 | '_per_page' => 25, |
||
396 | ]; |
||
397 | } |
||
398 | |||
399 | public function getDefaultPerPageOptions(string $class): array |
||
0 ignored issues
–
show
|
|||
400 | { |
||
401 | return [10, 25, 50, 100, 250]; |
||
402 | } |
||
403 | |||
404 | public function modelTransform($class, $instance) |
||
405 | { |
||
406 | return $instance; |
||
407 | } |
||
408 | |||
409 | public function modelReverseTransform($class, array $array = []) |
||
410 | { |
||
411 | $instance = $this->getModelInstance($class); |
||
412 | $metadata = $this->getMetadata($class); |
||
413 | |||
414 | foreach ($array as $name => $value) { |
||
415 | $property = $this->getFieldName($metadata, $name); |
||
416 | |||
417 | $this->propertyAccessor->setValue($instance, $property, $value); |
||
418 | } |
||
419 | |||
420 | return $instance; |
||
421 | } |
||
422 | |||
423 | public function getModelCollectionInstance($class) |
||
424 | { |
||
425 | return new ArrayCollection(); |
||
426 | } |
||
427 | |||
428 | public function collectionClear(&$collection) |
||
429 | { |
||
430 | return $collection->clear(); |
||
431 | } |
||
432 | |||
433 | public function collectionHasElement(&$collection, &$element) |
||
434 | { |
||
435 | return $collection->contains($element); |
||
436 | } |
||
437 | |||
438 | public function collectionAddElement(&$collection, &$element) |
||
439 | { |
||
440 | return $collection->add($element); |
||
441 | } |
||
442 | |||
443 | public function collectionRemoveElement(&$collection, &$element) |
||
444 | { |
||
445 | return $collection->removeElement($element); |
||
0 ignored issues
–
show
|
|||
446 | } |
||
447 | |||
448 | /** |
||
449 | * NEXT_MAJOR: Remove this method. |
||
450 | * |
||
451 | * @deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. |
||
452 | * |
||
453 | * @param string $property |
||
454 | * |
||
455 | * @return mixed |
||
456 | */ |
||
457 | protected function camelize($property) |
||
458 | { |
||
459 | @trigger_error(sprintf( |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
460 | 'Method "%s()" is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x and will be removed in version 4.0.', |
||
461 | __METHOD__ |
||
462 | ), E_USER_DEPRECATED); |
||
463 | |||
464 | return str_replace(' ', '', ucwords(str_replace('_', ' ', $property))); |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * NEXT_MAJOR: Remove CommonClassMetadata and add ClassMetadata as type hint when dropping doctrine/mongodb-odm 1.3.x. |
||
469 | * |
||
470 | * @param ClassMetadata|CommonClassMetadata $metadata |
||
471 | */ |
||
472 | private function getFieldName($metadata, string $name): string |
||
473 | { |
||
474 | if (\array_key_exists($name, $metadata->fieldMappings)) { |
||
0 ignored issues
–
show
Accessing
fieldMappings on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
475 | return $metadata->fieldMappings[$name]['fieldName']; |
||
0 ignored issues
–
show
Accessing
fieldMappings on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
476 | } |
||
477 | |||
478 | if (\array_key_exists($name, $metadata->associationMappings)) { |
||
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
479 | return $metadata->associationMappings[$name]['fieldName']; |
||
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
480 | } |
||
481 | |||
482 | return $name; |
||
483 | } |
||
484 | |||
485 | private function isFieldAlreadySorted(FieldDescriptionInterface $fieldDescription, DatagridInterface $datagrid): bool |
||
486 | { |
||
487 | $values = $datagrid->getValues(); |
||
488 | |||
489 | if (!isset($values['_sort_by']) || !$values['_sort_by'] instanceof FieldDescriptionInterface) { |
||
490 | return false; |
||
491 | } |
||
492 | |||
493 | return $values['_sort_by']->getName() === $fieldDescription->getName() |
||
494 | || $values['_sort_by']->getName() === $fieldDescription->getOption('sortable'); |
||
495 | } |
||
496 | } |
||
497 |
If you suppress an error, we recommend checking for the error condition explicitly: