Completed
Pull Request — 3.x (#239)
by Sullivan
03:05 queued 01:31
created

Model/ModelManager.php (1 issue)

Upgrade to new PHP Analysis Engine

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\DoctrineMongoDBAdminBundle\Model;
13
14
use Doctrine\ODM\MongoDB\Query\Builder;
15
use Exporter\Source\DoctrineODMQuerySourceIterator;
16
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
17
use Sonata\AdminBundle\Datagrid\DatagridInterface;
18
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
19
use Sonata\AdminBundle\Model\ModelManagerInterface;
20
use Sonata\DoctrineMongoDBAdminBundle\Admin\FieldDescription;
21
use Sonata\DoctrineMongoDBAdminBundle\Datagrid\ProxyQuery;
22
use Symfony\Bridge\Doctrine\ManagerRegistry;
23
use Symfony\Component\Form\Exception\PropertyAccessDeniedException;
24
25
class ModelManager implements ModelManagerInterface
26
{
27
    const ID_SEPARATOR = '-';
28
    protected $registry;
29
30
    /**
31
     * @param \Symfony\Bridge\Doctrine\ManagerRegistry $registry
32
     */
33
    public function __construct(ManagerRegistry $registry)
34
    {
35
        $this->registry = $registry;
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function getMetadata($class)
42
    {
43
        return $this->getDocumentManager($class)->getMetadataFactory()->getMetadataFor($class);
44
    }
45
46
    /**
47
     * Returns the model's metadata holding the fully qualified property, and the last
48
     * property name.
49
     *
50
     * @param string $baseClass        The base class of the model holding the fully qualified property
51
     * @param string $propertyFullName The name of the fully qualified property (dot ('.') separated
52
     *                                 property string)
53
     *
54
     * @return array(
55
     *                \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $parentMetadata,
56
     *                string $lastPropertyName,
57
     *                array $parentAssociationMappings
58
     *                )
59
     */
60
    public function getParentMetadataForProperty($baseClass, $propertyFullName)
61
    {
62
        $nameElements = explode('.', $propertyFullName);
63
        $lastPropertyName = array_pop($nameElements);
64
        $class = $baseClass;
65
        $parentAssociationMappings = array();
66
67
        foreach ($nameElements as $nameElement) {
68
            $metadata = $this->getMetadata($class);
69
            $parentAssociationMappings[] = $metadata->associationMappings[$nameElement];
70
            $class = $metadata->getAssociationTargetClass($nameElement);
71
        }
72
73
        return array($this->getMetadata($class), $lastPropertyName, $parentAssociationMappings);
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function hasMetadata($class)
80
    {
81
        return $this->getDocumentManager($class)->getMetadataFactory()->hasMetadataFor($class);
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function getNewFieldDescriptionInstance($class, $name, array $options = array())
88
    {
89
        if (!is_string($name)) {
90
            throw new \RunTimeException('The name argument must be a string');
91
        }
92
93
        if (!isset($options['route']['name'])) {
94
            $options['route']['name'] = 'edit';
95
        }
96
97
        if (!isset($options['route']['parameters'])) {
98
            $options['route']['parameters'] = array();
99
        }
100
101
        list($metadata, $propertyName, $parentAssociationMappings) = $this->getParentMetadataForProperty($class, $name);
102
103
        $fieldDescription = new FieldDescription();
104
        $fieldDescription->setName($name);
105
        $fieldDescription->setOptions($options);
106
        $fieldDescription->setParentAssociationMappings($parentAssociationMappings);
107
108
        /* @var ClassMetadata */
109
        if (isset($metadata->associationMappings[$propertyName])) {
110
            $fieldDescription->setAssociationMapping($metadata->associationMappings[$propertyName]);
111
        }
112
113
        if (isset($metadata->fieldMappings[$propertyName])) {
114
            $fieldDescription->setFieldMapping($metadata->fieldMappings[$propertyName]);
115
        }
116
117
        return $fieldDescription;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function create($object)
124
    {
125
        $documentManager = $this->getDocumentManager($object);
126
        $documentManager->persist($object);
127
        $documentManager->flush();
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function update($object)
134
    {
135
        $documentManager = $this->getDocumentManager($object);
136
        $documentManager->persist($object);
137
        $documentManager->flush();
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function delete($object)
144
    {
145
        $documentManager = $this->getDocumentManager($object);
0 ignored issues
show
$object is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
146
        $documentManager->remove($object);
147
        $documentManager->flush();
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function find($class, $id)
154
    {
155
        if (!isset($id)) {
156
            return;
157
        }
158
159
        $documentManager = $this->getDocumentManager($class);
160
161
        if (is_numeric($id)) {
162
            $value = $documentManager->getRepository($class)->find(intval($id));
163
164
            if (!empty($value)) {
165
                return $value;
166
            }
167
        }
168
169
        return $documentManager->getRepository($class)->find($id);
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function findBy($class, array $criteria = array())
176
    {
177
        return $this->getDocumentManager($class)->getRepository($class)->findBy($criteria);
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function findOneBy($class, array $criteria = array())
184
    {
185
        return $this->getDocumentManager($class)->getRepository($class)->findOneBy($criteria);
186
    }
187
188
    /**
189
     * @param string $class
190
     *
191
     * @throw \RuntimeException
192
     *
193
     * @return \Doctrine\ODM\MongoDB\DocumentManager
194
     */
195
    public function getDocumentManager($class)
196
    {
197
        if (is_object($class)) {
198
            $class = get_class($class);
199
        }
200
201
        $dm = $this->registry->getManagerForClass($class);
202
203
        if (!$dm) {
204
            throw new \RuntimeException(sprintf('No document manager defined for class %s', $class));
205
        }
206
207
        return $dm;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function getParentFieldDescription($parentAssociationMapping, $class)
214
    {
215
        $fieldName = $parentAssociationMapping['fieldName'];
216
217
        $metadata = $this->getMetadata($class);
218
219
        $associatingMapping = $metadata->associationMappings[$parentAssociationMapping];
220
221
        $fieldDescription = $this->getNewFieldDescriptionInstance($class, $fieldName);
222
        $fieldDescription->setName($parentAssociationMapping);
223
        $fieldDescription->setAssociationMapping($associatingMapping);
224
225
        return $fieldDescription;
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231
    public function createQuery($class, $alias = 'o')
232
    {
233
        $repository = $this->getDocumentManager($class)->getRepository($class);
234
235
        return new ProxyQuery($repository->createQueryBuilder());
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function executeQuery($query)
242
    {
243
        if ($query instanceof Builder) {
244
            return $query->getQuery()->execute();
245
        }
246
247
        return $query->execute();
248
    }
249
250
    /**
251
     * {@inheritdoc}
252
     */
253
    public function getModelIdentifier($class)
254
    {
255
        return $this->getMetadata($class)->identifier;
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261
    public function getIdentifierValues($document)
262
    {
263
        return array($this->getDocumentManager($document)->getUnitOfWork()->getDocumentIdentifier($document));
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     */
269
    public function getIdentifierFieldNames($class)
270
    {
271
        return array($this->getMetadata($class)->getIdentifier());
272
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277
    public function getNormalizedIdentifier($document)
278
    {
279
        if (is_scalar($document)) {
280
            throw new \RunTimeException('Invalid argument, object or null required');
281
        }
282
283
        // the entities is not managed
284
        if (!$document || !$this->getDocumentManager($document)->getUnitOfWork()->isInIdentityMap($document)) {
285
            return;
286
        }
287
288
        $values = $this->getIdentifierValues($document);
289
290
        return implode(self::ID_SEPARATOR, $values);
291
    }
292
293
    /**
294
     * {@inheritdoc}
295
     */
296
    public function getUrlsafeIdentifier($entity)
297
    {
298
        return $this->getNormalizedIdentifier($entity);
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304
    public function addIdentifiersToQuery($class, ProxyQueryInterface $queryProxy, array $idx)
305
    {
306
        $queryBuilder = $queryProxy->getQueryBuilder();
307
        $queryBuilder->field('_id')->in($idx);
308
    }
309
310
    /**
311
     * {@inheritdoc}
312
     */
313
    public function batchDelete($class, ProxyQueryInterface $queryProxy)
314
    {
315
        /** @var Query $queryBuilder */
316
        $queryBuilder = $queryProxy->getQuery();
317
318
        $documentManager = $this->getDocumentManager($class);
319
320
        $i = 0;
321
        foreach ($queryBuilder->execute() as $object) {
322
            $documentManager->remove($object);
323
324
            if ((++$i % 20) == 0) {
325
                $documentManager->flush();
326
                $documentManager->clear();
327
            }
328
        }
329
330
        $documentManager->flush();
331
        $documentManager->clear();
332
    }
333
334
    /**
335
     * {@inheritdoc}
336
     */
337
    public function getDataSourceIterator(DatagridInterface $datagrid, array $fields, $firstResult = null, $maxResult = null)
338
    {
339
        $datagrid->buildPager();
340
        $query = $datagrid->getQuery();
341
342
        $query->setFirstResult($firstResult);
343
        $query->setMaxResults($maxResult);
344
345
        return new DoctrineODMQuerySourceIterator($query instanceof ProxyQuery ? $query->getQuery() : $query, $fields);
346
    }
347
348
    /**
349
     * {@inheritdoc}
350
     */
351
    public function getExportFields($class)
352
    {
353
        $metadata = $this->getDocumentManager($class)->getClassMetadata($class);
354
355
        return $metadata->getFieldNames();
356
    }
357
358
    /**
359
     * {@inheritdoc}
360
     */
361
    public function getModelInstance($class)
362
    {
363
        return new $class();
364
    }
365
366
    /**
367
     * {@inheritdoc}
368
     */
369
    public function getSortParameters(FieldDescriptionInterface $fieldDescription, DatagridInterface $datagrid)
370
    {
371
        $values = $datagrid->getValues();
372
373
        if ($fieldDescription->getName() == $values['_sort_by']->getName() || $values['_sort_by']->getName() === $fieldDescription->getOption('sortable')) {
374
            if ($values['_sort_order'] == 'ASC') {
375
                $values['_sort_order'] = 'DESC';
376
            } else {
377
                $values['_sort_order'] = 'ASC';
378
            }
379
        } else {
380
            $values['_sort_order'] = 'ASC';
381
        }
382
383
        $values['_sort_by'] = is_string($fieldDescription->getOption('sortable')) ? $fieldDescription->getOption('sortable') : $fieldDescription->getName();
384
385
        return array('filter' => $values);
386
    }
387
388
    /**
389
     * {@inheritdoc}
390
     */
391
    public function getPaginationParameters(DatagridInterface $datagrid, $page)
392
    {
393
        $values = $datagrid->getValues();
394
395
        $values['_sort_by'] = $values['_sort_by']->getName();
396
        $values['_page'] = $page;
397
398
        return array('filter' => $values);
399
    }
400
401
    /**
402
     * {@inheritdoc}
403
     */
404
    public function getDefaultSortValues($class)
405
    {
406
        return array(
407
            '_sort_order' => 'ASC',
408
            '_sort_by' => $this->getModelIdentifier($class),
409
            '_page' => 1,
410
            '_per_page' => 25,
411
        );
412
    }
413
414
    /**
415
     * {@inheritdoc}
416
     */
417
    public function modelTransform($class, $instance)
418
    {
419
        return $instance;
420
    }
421
422
    /**
423
     * {@inheritdoc}
424
     */
425
    public function modelReverseTransform($class, array $array = array())
426
    {
427
        $instance = $this->getModelInstance($class);
428
        $metadata = $this->getMetadata($class);
429
430
        $reflClass = $metadata->reflClass;
431
        foreach ($array as $name => $value) {
432
            $reflection_property = false;
433
            // property or association ?
434
            if (array_key_exists($name, $metadata->fieldMappings)) {
435
                $property = $metadata->fieldMappings[$name]['fieldName'];
436
                $reflection_property = $metadata->reflFields[$name];
437
            } elseif (array_key_exists($name, $metadata->associationMappings)) {
438
                $property = $metadata->associationMappings[$name]['fieldName'];
439
            } else {
440
                $property = $name;
441
            }
442
443
            $setter = 'set'.$this->camelize($name);
444
445
            if ($reflClass->hasMethod($setter)) {
446
                if (!$reflClass->getMethod($setter)->isPublic()) {
447
                    throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName()));
448
                }
449
450
                $instance->$setter($value);
451
            } elseif ($reflClass->hasMethod('__set')) {
452
                // needed to support magic method __set
453
                $instance->$property = $value;
454
            } elseif ($reflClass->hasProperty($property)) {
455
                if (!$reflClass->getProperty($property)->isPublic()) {
456
                    throw new PropertyAccessDeniedException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "set%s()"?', $property, $reflClass->getName(), ucfirst($property)));
457
                }
458
459
                $instance->$property = $value;
460
            } elseif ($reflection_property) {
461
                $reflection_property->setValue($instance, $value);
462
            }
463
        }
464
465
        return $instance;
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471
    public function getModelCollectionInstance($class)
472
    {
473
        return new \Doctrine\Common\Collections\ArrayCollection();
474
    }
475
476
    /**
477
     * {@inheritdoc}
478
     */
479
    public function collectionClear(&$collection)
480
    {
481
        return $collection->clear();
482
    }
483
484
    /**
485
     * {@inheritdoc}
486
     */
487
    public function collectionHasElement(&$collection, &$element)
488
    {
489
        return $collection->contains($element);
490
    }
491
492
    /**
493
     * {@inheritdoc}
494
     */
495
    public function collectionAddElement(&$collection, &$element)
496
    {
497
        return $collection->add($element);
498
    }
499
500
    /**
501
     * {@inheritdoc}
502
     */
503
    public function collectionRemoveElement(&$collection, &$element)
504
    {
505
        return $collection->removeElement($element);
506
    }
507
508
    /**
509
     * method taken from PropertyPath.
510
     *
511
     * @param string $property
512
     *
513
     * @return mixed
514
     */
515
    protected function camelize($property)
516
    {
517
        return preg_replace(array('/(^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\2')", "'_'.strtoupper('\\1')"), $property);
518
    }
519
}
520