Completed
Pull Request — master (#116)
by Arnaud
05:17
created

ORMDataProvider   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 71
dl 0
loc 190
rs 10
c 0
b 0
f 0
wmc 22

9 Methods

Rating   Name   Duplication   Size   Complexity  
B getFields() 0 37 9
A get() 0 17 2
A getCollection() 0 30 2
A getRepository() 0 3 1
A save() 0 4 1
A create() 0 5 1
A delete() 0 7 2
A isJoinColumnNullable() 0 11 3
A __construct() 0 8 1
1
<?php
2
3
namespace LAG\AdminBundle\Bridge\Doctrine\ORM\DataProvider;
4
5
use Doctrine\Common\Persistence\ObjectRepository;
6
use Doctrine\ORM\EntityManagerInterface;
7
use Doctrine\ORM\EntityRepository;
8
use Doctrine\ORM\Mapping\ClassMetadataInfo;
9
use LAG\AdminBundle\Admin\AdminInterface;
10
use LAG\AdminBundle\DataProvider\DataProviderInterface;
11
use LAG\AdminBundle\Event\Events;
12
use LAG\AdminBundle\Bridge\Doctrine\ORM\Event\ORMFilterEvent;
13
use LAG\AdminBundle\Exception\Exception;
14
use LAG\AdminBundle\Field\Definition\FieldDefinition;
15
use Pagerfanta\Adapter\DoctrineORMAdapter;
16
use Pagerfanta\Pagerfanta;
17
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
20
class ORMDataProvider implements DataProviderInterface
21
{
22
    /**
23
     * @var EntityManagerInterface
24
     */
25
    private $entityManager;
26
27
    /**
28
     * @var EventDispatcherInterface
29
     */
30
    private $eventDispatcher;
31
32
    /**
33
     * @var RequestStack
34
     */
35
    private $requestStack;
36
37
    /**
38
     * DoctrineORMDataProvider constructor.
39
     *
40
     * @param EntityManagerInterface $entityManager
41
     * @param EventDispatcherInterface $eventDispatcher
42
     * @param RequestStack $requestStack
43
     */
44
    public function __construct(
45
        EntityManagerInterface $entityManager,
46
        EventDispatcherInterface $eventDispatcher,
47
        RequestStack $requestStack
48
    ) {
49
        $this->entityManager = $entityManager;
50
        $this->eventDispatcher = $eventDispatcher;
51
        $this->requestStack = $requestStack;
52
    }
53
54
    /**
55
     * Load a collection of entities.
56
     *
57
     * @param AdminInterface $admin
58
     * @param array          $filters
59
     *
60
     * @return mixed
61
     */
62
    public function getCollection(AdminInterface $admin, array $filters = [])
63
    {
64
        $adminConfiguration = $admin->getConfiguration();
65
        $actionConfiguration = $admin->getAction()->getConfiguration();
66
67
        // Create a query builder for the configured entity class
68
        $queryBuilder = $this
69
            ->getRepository($adminConfiguration->getParameter('entity'))
70
            ->createQueryBuilder('entity')
71
        ;
72
73
        // Dispatch an event to allow filter alteration on the query builder
74
        $event = new ORMFilterEvent($queryBuilder, $admin, $filters);
75
        $this->eventDispatcher->dispatch(Events::DOCTRINE_ORM_FILTER, $event);
76
77
        if ('pagerfanta' === $actionConfiguration->getParameter('pager')) {
78
            $pageParameter = $actionConfiguration->getParameter('page_parameter');
79
            $request = $this->requestStack->getCurrentRequest();
80
            $page = (int) $request->get($pageParameter, 1);
81
82
            $adapter = new DoctrineORMAdapter($queryBuilder);
83
            $pager = new Pagerfanta($adapter);
84
            $pager->setCurrentPage($page);
85
            $pager->setMaxPerPage($actionConfiguration->getParameter('max_per_page'));
86
            $entities = $pager;
87
        } else {
88
            $entities = $queryBuilder->getQuery()->getResult();
89
        }
90
91
        return $entities;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function get(AdminInterface $admin, string $identifier)
98
    {
99
        $class = $admin->getConfiguration()->getParameter('entity');
100
        $item = $this
101
            ->getRepository($class)
102
            ->find($identifier)
103
        ;
104
105
        if (null === $item) {
106
            throw new Exception(sprintf(
107
                'Item of class "%s" with identifier "%s" not found.',
108
                $class,
109
                $identifier
110
            ));
111
        }
112
113
        return $item;
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function save(AdminInterface $admin): void
120
    {
121
        $this->entityManager->persist($admin->getEntities()->first());
122
        $this->entityManager->flush();
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function create(AdminInterface $admin)
129
    {
130
        $class = $admin->getConfiguration()->getParameter('entity');
131
132
        return new $class();
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function delete(AdminInterface $admin): void
139
    {
140
        if ($admin->getEntities()->isEmpty()) {
141
            throw new Exception('The admin "'.$admin->getName().'" has no loaded entity');
142
        }
143
        $this->entityManager->remove($admin->getEntities()->first());
144
        $this->entityManager->flush();
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function getFields(AdminInterface $admin): array
151
    {
152
        $metadata = $this->entityManager->getClassMetadata($admin->getEntityClass());
153
        $fieldNames = (array) $metadata->fieldNames;
0 ignored issues
show
Bug introduced by
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
154
        $fields = [];
155
156
        foreach ($fieldNames as $fieldName) {
157
            // Remove the primary key field if it's not managed manually
158
            if (!$metadata->isIdentifierNatural() && in_array($fieldName, $metadata->identifier)) {
0 ignored issues
show
Bug introduced by
The method isIdentifierNatural() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean isIdentifier()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
            if (!$metadata->/** @scrutinizer ignore-call */ isIdentifierNatural() && in_array($fieldName, $metadata->identifier)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
159
                continue;
160
            }
161
            $mapping = $metadata->getFieldMapping($fieldName);
162
            $formOptions = [];
163
164
            // When a field is defined as nullable in the Doctrine entity configuration, the associated form field
165
            // should not be required neither
166
            if (key_exists('nullable', $mapping) && true === $mapping['nullable']) {
167
                $formOptions['required'] = false;
168
            }
169
            $fields[$fieldName] = new FieldDefinition($metadata->getTypeOfField($fieldName), $formOptions);
170
        }
171
172
        foreach ($metadata->associationMappings as $fieldName => $relation) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
173
            $formOptions = [];
174
            $formType = 'choice';
175
176
            if (ClassMetadataInfo::MANY_TO_MANY === $relation['type']) {
177
                $formOptions['expanded'] = true;
178
                $formOptions['multiple'] = true;
179
            }
180
            if ($this->isJoinColumnNullable($relation)) {
181
                $formOptions['required'] = false;
182
            }
183
            $fields[$fieldName] = new FieldDefinition($formType, $formOptions);
184
        }
185
186
        return $fields;
187
    }
188
189
    /**
190
     * @param string $entityClass
191
     *
192
     * @return ObjectRepository|EntityRepository
193
     */
194
    private function getRepository(string $entityClass)
195
    {
196
        return $this->entityManager->getRepository($entityClass);
197
    }
198
199
    private function isJoinColumnNullable(array $relation)
200
    {
201
        if (!key_exists('joinColumns', $relation)) {
202
            return false;
203
        }
204
205
        if (!key_exists('nullable', $relation['joinColumns'])) {
206
            return false;
207
        }
208
209
        return false === $relation['joinColumns']['nullable'];
210
    }
211
}
212