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( |
||
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); |
||
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); |
||
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); |
||
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)) { |
||
475 | return $metadata->fieldMappings[$name]['fieldName']; |
||
476 | } |
||
477 | |||
478 | if (\array_key_exists($name, $metadata->associationMappings)) { |
||
479 | return $metadata->associationMappings[$name]['fieldName']; |
||
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: