These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the Sonata Project package. |
||
5 | * |
||
6 | * (c) Thomas Rabaix <[email protected]> |
||
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 | namespace Sonata\DoctrinePHPCRAdminBundle\Model; |
||
13 | |||
14 | use Doctrine\Common\Collections\ArrayCollection; |
||
15 | use Doctrine\Common\Util\ClassUtils; |
||
16 | use Doctrine\ODM\PHPCR\DocumentManager; |
||
17 | use Doctrine\ODM\PHPCR\Mapping\ClassMetadata; |
||
18 | use Sonata\AdminBundle\Admin\FieldDescriptionInterface; |
||
19 | use Sonata\AdminBundle\Datagrid\DatagridInterface; |
||
20 | use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; |
||
21 | use Sonata\AdminBundle\Exception\ModelManagerException; |
||
22 | use Sonata\AdminBundle\Model\ModelManagerInterface; |
||
23 | use Sonata\DoctrinePHPCRAdminBundle\Admin\FieldDescription; |
||
24 | use Sonata\DoctrinePHPCRAdminBundle\Datagrid\ProxyQuery; |
||
25 | use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; |
||
26 | |||
27 | class ModelManager implements ModelManagerInterface |
||
28 | { |
||
29 | /** |
||
30 | * @var DocumentManager |
||
31 | */ |
||
32 | protected $dm; |
||
33 | |||
34 | /** |
||
35 | * @param DocumentManager $dm |
||
36 | */ |
||
37 | public function __construct(DocumentManager $dm) |
||
38 | { |
||
39 | $this->dm = $dm; |
||
40 | } |
||
41 | |||
42 | /** |
||
43 | * Returns the related model's metadata. |
||
44 | * |
||
45 | * @param string $class |
||
46 | * |
||
47 | * @return ClassMetadata |
||
48 | */ |
||
49 | public function getMetadata($class) |
||
50 | { |
||
51 | return $this->dm->getMetadataFactory()->getMetadataFor($class); |
||
52 | } |
||
53 | |||
54 | /** |
||
55 | * Returns true is the model has some metadata. |
||
56 | * |
||
57 | * @param string $class |
||
58 | * |
||
59 | * @return bool |
||
60 | */ |
||
61 | public function hasMetadata($class) |
||
62 | { |
||
63 | return $this->dm->getMetadataFactory()->hasMetadataFor($class); |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * {@inheritdoc} |
||
68 | * |
||
69 | * @throws ModelManagerException if the document manager throws any exception |
||
70 | */ |
||
71 | public function create($object) |
||
72 | { |
||
73 | try { |
||
74 | $this->dm->persist($object); |
||
75 | $this->dm->flush(); |
||
76 | } catch (\Exception $e) { |
||
77 | throw new ModelManagerException('', 0, $e); |
||
78 | } |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * {@inheritdoc} |
||
83 | * |
||
84 | * @throws ModelManagerException if the document manager throws any exception |
||
85 | */ |
||
86 | public function update($object) |
||
87 | { |
||
88 | try { |
||
89 | $this->dm->persist($object); |
||
90 | $this->dm->flush(); |
||
91 | } catch (\Exception $e) { |
||
92 | throw new ModelManagerException('', 0, $e); |
||
93 | } |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * {@inheritdoc} |
||
98 | * |
||
99 | * @throws ModelManagerException if the document manager throws any exception |
||
100 | */ |
||
101 | public function delete($object) |
||
102 | { |
||
103 | try { |
||
104 | $this->dm->remove($object); |
||
105 | $this->dm->flush(); |
||
106 | } catch (\Exception $e) { |
||
107 | throw new ModelManagerException('', 0, $e); |
||
108 | } |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Find one object from the given class repository. |
||
113 | * |
||
114 | * {@inheritdoc} |
||
115 | */ |
||
116 | public function find($class, $id) |
||
117 | { |
||
118 | if (!isset($id)) { |
||
119 | return; |
||
120 | } |
||
121 | |||
122 | if (null === $class) { |
||
123 | return $this->dm->find(null, $id); |
||
124 | } |
||
125 | |||
126 | return $this->dm->getRepository($class)->find($id); |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * {@inheritdoc} |
||
131 | * |
||
132 | * @return FieldDescription |
||
133 | * |
||
134 | * @throws \RunTimeException if $name is not a string |
||
135 | */ |
||
136 | public function getNewFieldDescriptionInstance($class, $name, array $options = array()) |
||
137 | { |
||
138 | if (!is_string($name)) { |
||
139 | throw new \RunTimeException('The name argument must be a string'); |
||
140 | } |
||
141 | |||
142 | $metadata = $this->getMetadata($class); |
||
143 | |||
144 | $fieldDescription = new FieldDescription(); |
||
145 | $fieldDescription->setName($name); |
||
146 | $fieldDescription->setOptions($options); |
||
147 | |||
148 | if (isset($metadata->associationMappings[$name])) { |
||
0 ignored issues
–
show
|
|||
149 | $fieldDescription->setAssociationMapping($metadata->associationMappings[$name]); |
||
0 ignored issues
–
show
The property
associationMappings does not seem to exist. Did you mean mappings ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.
Loading history...
|
|||
150 | } |
||
151 | |||
152 | if (isset($metadata->fieldMappings[$name])) { |
||
153 | $fieldDescription->setFieldMapping($metadata->fieldMappings[$name]); |
||
154 | } |
||
155 | |||
156 | return $fieldDescription; |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * {@inheritdoc} |
||
161 | */ |
||
162 | public function findBy($class, array $criteria = array()) |
||
163 | { |
||
164 | return $this->dm->getRepository($class)->findBy($criteria); |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * {@inheritdoc} |
||
169 | */ |
||
170 | public function findOneBy($class, array $criteria = array()) |
||
171 | { |
||
172 | return $this->dm->getRepository($class)->findOneBy($criteria); |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * @return DocumentManager The PHPCR-ODM document manager responsible for |
||
177 | * this model |
||
178 | */ |
||
179 | public function getDocumentManager() |
||
180 | { |
||
181 | return $this->dm; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * {@inheritdoc} |
||
186 | * |
||
187 | * @return FieldDescriptionInterface |
||
188 | */ |
||
189 | public function getParentFieldDescription($parentAssociationMapping, $class) |
||
190 | { |
||
191 | $fieldName = $parentAssociationMapping['fieldName']; |
||
192 | |||
193 | $metadata = $this->getMetadata($class); |
||
194 | |||
195 | $associatingMapping = $metadata->associationMappings[$parentAssociationMapping]; |
||
0 ignored issues
–
show
The property
associationMappings does not seem to exist. Did you mean mappings ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.
Loading history...
|
|||
196 | |||
197 | $fieldDescription = $this->getNewFieldDescriptionInstance($class, $fieldName); |
||
198 | $fieldDescription->setName($parentAssociationMapping); |
||
199 | $fieldDescription->setAssociationMapping($associatingMapping); |
||
200 | |||
201 | return $fieldDescription; |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * @param string $class the fully qualified class name to search for |
||
206 | * @param string $alias alias to use for this class when accessing fields, |
||
207 | * defaults to 'a' |
||
208 | * |
||
209 | * @return ProxyQueryInterface |
||
210 | * |
||
211 | * @throws \InvalidArgumentException if alias is not a string or an empty string |
||
212 | */ |
||
213 | public function createQuery($class, $alias = 'a') |
||
214 | { |
||
215 | $qb = $this->getDocumentManager()->createQueryBuilder(); |
||
216 | $qb->from()->document($class, $alias); |
||
217 | |||
218 | return new ProxyQuery($qb, $alias); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * @param ProxyQuery $query |
||
223 | * |
||
224 | * @return mixed |
||
225 | */ |
||
226 | public function executeQuery($query) |
||
227 | { |
||
228 | return $query->execute(); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * {@inheritdoc} |
||
233 | */ |
||
234 | public function getModelIdentifier($classname) |
||
235 | { |
||
236 | return $this->getMetadata($classname)->identifier; |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Transforms the document into the PHPCR path. |
||
241 | * |
||
242 | * Note: This is returning an array because Doctrine ORM for example can |
||
243 | * have multiple identifiers, e.g. if the primary key is composed of |
||
244 | * several columns. We only ever have one, but return that wrapped into an |
||
245 | * array to adhere to the interface. |
||
246 | * |
||
247 | * {@inheritdoc} |
||
248 | */ |
||
249 | public function getIdentifierValues($document) |
||
250 | { |
||
251 | $class = $this->getMetadata(ClassUtils::getClass($document)); |
||
252 | $path = $class->reflFields[$class->identifier]->getValue($document); |
||
253 | |||
254 | return array($path); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * {@inheritdoc} |
||
259 | */ |
||
260 | public function getIdentifierFieldNames($class) |
||
261 | { |
||
262 | return array($this->getModelIdentifier($class)); |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * This is just taking the id out of the array again. |
||
267 | * |
||
268 | * {@inheritdoc} |
||
269 | * |
||
270 | * @throws \InvalidArgumentException if $document is not an object or null |
||
271 | */ |
||
272 | public function getNormalizedIdentifier($document) |
||
273 | { |
||
274 | if (is_scalar($document)) { |
||
275 | throw new \InvalidArgumentException('Invalid argument, object or null required'); |
||
276 | } |
||
277 | |||
278 | // the document is not managed |
||
279 | if (!$document || !$this->getDocumentManager()->contains($document)) { |
||
280 | return; |
||
281 | } |
||
282 | |||
283 | $values = $this->getIdentifierValues($document); |
||
284 | |||
285 | return $values[0]; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Currently only the leading slash is removed. |
||
290 | * |
||
291 | * @param object $document |
||
292 | * |
||
293 | * @return null|string |
||
294 | */ |
||
295 | public function getUrlsafeIdentifier($document) |
||
296 | { |
||
297 | $id = $this->getNormalizedIdentifier($document); |
||
298 | if (null !== $id) { |
||
299 | return substr($id, 1); |
||
300 | } |
||
301 | |||
302 | return; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * {@inheritdoc} |
||
307 | */ |
||
308 | public function addIdentifiersToQuery($class, ProxyQueryInterface $queryProxy, array $idx) |
||
309 | { |
||
310 | /* @var $queryProxy ProxyQuery */ |
||
311 | $qb = $queryProxy->getQueryBuilder(); |
||
312 | |||
313 | $orX = $qb->andWhere()->orX(); |
||
314 | |||
315 | foreach ($idx as $id) { |
||
316 | $path = $this->getBackendId($id); |
||
317 | $orX->same($path, $queryProxy->getAlias()); |
||
318 | } |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Add leading slash to construct valid phpcr document id. |
||
323 | * |
||
324 | * The phpcr-odm QueryBuilder uses absolute paths and expects id´s to start with a forward slash |
||
325 | * because SonataAdmin uses object id´s for constructing URL´s it has to use id´s without the |
||
326 | * leading slash. |
||
327 | * |
||
328 | * @param string $id |
||
329 | * |
||
330 | * @return string |
||
331 | */ |
||
332 | public function getBackendId($id) |
||
333 | { |
||
334 | return substr($id, 0, 1) === '/' ? $id : '/'.$id; |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * {@inheritdoc} |
||
339 | * |
||
340 | * @throws ModelManagerException if anything goes wrong during query execution |
||
341 | */ |
||
342 | public function batchDelete($class, ProxyQueryInterface $queryProxy) |
||
343 | { |
||
344 | try { |
||
345 | $i = 0; |
||
346 | $res = $queryProxy->execute(); |
||
347 | foreach ($res as $object) { |
||
348 | $this->dm->remove($object); |
||
349 | |||
350 | if ((++$i % 20) == 0) { |
||
351 | $this->dm->flush(); |
||
352 | $this->dm->clear(); |
||
353 | } |
||
354 | } |
||
355 | |||
356 | $this->dm->flush(); |
||
357 | $this->dm->clear(); |
||
358 | } catch (\Exception $e) { |
||
359 | throw new ModelManagerException('', 0, $e); |
||
360 | } |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * {@inheritdoc} |
||
365 | * |
||
366 | * @return object |
||
367 | */ |
||
368 | public function getModelInstance($class) |
||
369 | { |
||
370 | return new $class(); |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * {@inheritdoc} |
||
375 | */ |
||
376 | public function getSortParameters(FieldDescriptionInterface $fieldDescription, DatagridInterface $datagrid) |
||
377 | { |
||
378 | $values = $datagrid->getValues(); |
||
379 | |||
380 | if ($fieldDescription->getName() == $values['_sort_by']->getName()) { |
||
381 | if ($values['_sort_order'] == 'ASC') { |
||
382 | $values['_sort_order'] = 'DESC'; |
||
383 | } else { |
||
384 | $values['_sort_order'] = 'ASC'; |
||
385 | } |
||
386 | |||
387 | $values['_sort_by'] = $fieldDescription->getName(); |
||
388 | } else { |
||
389 | $values['_sort_order'] = 'ASC'; |
||
390 | $values['_sort_by'] = $fieldDescription->getName(); |
||
391 | } |
||
392 | |||
393 | return array('filter' => $values); |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * {@inheritdoc} |
||
398 | */ |
||
399 | public function getPaginationParameters(DatagridInterface $datagrid, $page) |
||
400 | { |
||
401 | $values = $datagrid->getValues(); |
||
402 | |||
403 | $values['_sort_by'] = $values['_sort_by']->getName(); |
||
404 | $values['_page'] = $page; |
||
405 | |||
406 | return array('filter' => $values); |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * {@inheritdoc} |
||
411 | */ |
||
412 | public function getDefaultSortValues($class) |
||
413 | { |
||
414 | return array( |
||
415 | '_sort_order' => 'ASC', |
||
416 | '_sort_by' => $this->getModelIdentifier($class), |
||
417 | '_page' => 1, |
||
418 | ); |
||
419 | } |
||
420 | |||
421 | /** |
||
422 | * {@inheritdoc} |
||
423 | * |
||
424 | * @return object |
||
425 | */ |
||
426 | public function modelTransform($class, $instance) |
||
427 | { |
||
428 | return $instance; |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * {@inheritdoc} |
||
433 | * |
||
434 | * @return object |
||
435 | * |
||
436 | * @throws NoSuchPropertyException if the class has no magic setter and |
||
437 | * public property for a field in array |
||
438 | */ |
||
439 | public function modelReverseTransform($class, array $array = array()) |
||
440 | { |
||
441 | $instance = $this->getModelInstance($class); |
||
442 | $metadata = $this->getMetadata($class); |
||
443 | |||
444 | $reflClass = $metadata->reflClass; |
||
445 | foreach ($array as $name => $value) { |
||
446 | $reflection_property = false; |
||
447 | // property or association ? |
||
448 | if (array_key_exists($name, $metadata->fieldMappings)) { |
||
449 | $property = $metadata->fieldMappings[$name]['fieldName']; |
||
450 | $reflection_property = $metadata->reflFields[$name]; |
||
451 | } elseif (array_key_exists($name, $metadata->associationMappings)) { |
||
0 ignored issues
–
show
The property
associationMappings does not seem to exist. Did you mean mappings ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.
Loading history...
|
|||
452 | $property = $metadata->associationMappings[$name]['fieldName']; |
||
0 ignored issues
–
show
The property
associationMappings does not seem to exist. Did you mean mappings ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.
Loading history...
|
|||
453 | } else { |
||
454 | $property = $name; |
||
455 | } |
||
456 | |||
457 | // TODO: use PropertyAccess https://github.com/sonata-project/SonataDoctrinePhpcrAdminBundle/issues/187 |
||
458 | $setter = 'set'.$this->camelize($name); |
||
459 | |||
460 | if ($reflClass->hasMethod($setter)) { |
||
461 | if (!$reflClass->getMethod($setter)->isPublic()) { |
||
462 | throw new NoSuchPropertyException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName())); |
||
463 | } |
||
464 | |||
465 | $instance->$setter($value); |
||
466 | } elseif ($reflClass->hasMethod('__set')) { |
||
467 | // needed to support magic method __set |
||
468 | $instance->$property = $value; |
||
469 | } elseif ($reflClass->hasProperty($property)) { |
||
470 | if (!$reflClass->getProperty($property)->isPublic()) { |
||
471 | throw new NoSuchPropertyException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "set%s()"?', $property, $reflClass->getName(), ucfirst($property))); |
||
472 | } |
||
473 | |||
474 | $instance->$property = $value; |
||
475 | } elseif ($reflection_property) { |
||
476 | $reflection_property->setValue($instance, $value); |
||
477 | } |
||
478 | } |
||
479 | |||
480 | return $instance; |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * {@inheritdoc} |
||
485 | */ |
||
486 | public function getModelCollectionInstance($class) |
||
487 | { |
||
488 | return new ArrayCollection(); |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * {@inheritdoc} |
||
493 | */ |
||
494 | public function collectionClear(&$collection) |
||
495 | { |
||
496 | return $collection->clear(); |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * {@inheritdoc} |
||
501 | */ |
||
502 | public function collectionHasElement(&$collection, &$element) |
||
503 | { |
||
504 | return $collection->contains($element); |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * {@inheritdoc} |
||
509 | */ |
||
510 | public function collectionAddElement(&$collection, &$element) |
||
511 | { |
||
512 | return $collection->add($element); |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * {@inheritdoc} |
||
517 | */ |
||
518 | public function collectionRemoveElement(&$collection, &$element) |
||
519 | { |
||
520 | return $collection->removeElement($element); |
||
521 | } |
||
522 | |||
523 | /** |
||
524 | * {@inheritdoc} |
||
525 | */ |
||
526 | public function getDataSourceIterator(DatagridInterface $datagrid, array $fields, $firstResult = null, $maxResult = null) |
||
527 | { |
||
528 | throw new \RuntimeException('Datasourceiterator not implemented.'); |
||
529 | } |
||
530 | |||
531 | /** |
||
532 | * {@inheritdoc} |
||
533 | * |
||
534 | * Not really implemented. |
||
535 | */ |
||
536 | public function getExportFields($class) |
||
537 | { |
||
538 | return array(); |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * Method taken from PropertyPath. |
||
543 | * |
||
544 | * NEXT_MAJOR: remove when doing https://github.com/sonata-project/SonataDoctrinePhpcrAdminBundle/issues/187 |
||
545 | * |
||
546 | * @param string $property |
||
547 | * |
||
548 | * @return string |
||
549 | * |
||
550 | * @deprecated |
||
551 | */ |
||
552 | protected function camelize($property) |
||
553 | { |
||
554 | return preg_replace(array('/(^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\2')", "'_'.strtoupper('\\1')"), $property); |
||
555 | } |
||
556 | } |
||
557 |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.