Completed
Push — master ( 4d9c8a...8c4b64 )
by Philip
02:40
created

DoctrineCrudService::removeAssociation()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 34
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 0
cts 31
cp 0
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 26
nc 4
nop 3
crap 20
1
<?php
2
3
namespace Dontdrinkandroot\Service;
4
5
use Doctrine\Common\Collections\Collection;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\EntityRepository;
8
use Doctrine\ORM\Mapping\ClassMetadata;
9
use Doctrine\ORM\QueryBuilder;
10
use Doctrine\ORM\Tools\Pagination\Paginator;
11
use Dontdrinkandroot\Repository\TransactionManager;
12
use Psr\Log\LoggerInterface;
13
use Psr\Log\NullLogger;
14
use Symfony\Component\PropertyAccess\PropertyAccess;
15
16
class DoctrineCrudService extends EntityRepository implements CrudServiceInterface
17
{
18
    /**
19
     * @var  LoggerInterface
20
     */
21
    private $logger;
22
23
    /**
24
     * @var TransactionManager
25
     */
26
    private $transactionManager;
27
28
    /**
29
     * DoctrineCrudService constructor.
30
     *
31
     * @param EntityManager        $em
32
     * @param ClassMetadata|string $class
33
     */
34
    public function __construct(EntityManager $em, $class)
35
    {
36
        $classMetaData = $class;
37
        if (is_string($classMetaData)) {
38
            $classMetaData = $em->getClassMetadata($classMetaData);
39
        }
40
        parent::__construct($em, $classMetaData);
41
        $this->transactionManager = new TransactionManager($em);
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function findAllPaginated(int $page = 1, int $perPage = 50): Paginator
48
    {
49
        $queryBuilder = $this->createFindAllQueryBuilder();
50
        $queryBuilder->setFirstResult(($page - 1) * $perPage);
51
        $queryBuilder->setMaxResults($perPage);
52
53
        return new Paginator($queryBuilder);
54
    }
55
56
    protected function createFindAllQueryBuilder(): QueryBuilder
57
    {
58
        $queryBuilder = $this->createQueryBuilder('entity');
59
60
        return $queryBuilder;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function create($entity)
67
    {
68
        $this->getEntityManager()->persist($entity);
69
        $this->getEntityManager()->flush($entity);
70
71
        return $entity;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function update($entity)
78
    {
79
        $this->getEntityManager()->flush($entity);
80
81
        return $entity;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function remove($entity)
88
    {
89
        $this->getEntityManager()->remove($entity);
90
        $this->getEntityManager()->flush($entity);
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function findAssociationPaginated($entity, string $fieldName, int $page = 1, $perPage = 50)
97
    {
98
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
99
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
100
101
        $inverseFieldName = $this->getInverseFieldName($fieldName, $classMetadata);
102
103
        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
104
        $queryBuilder->select('association');
105
        $queryBuilder->from($targetClass, 'association');
106
        $queryBuilder->join('association.' . $inverseFieldName, 'entity');
107
        $queryBuilder->where('entity = :entity');
108
        $queryBuilder->setParameter('entity', $entity);
109
110
        $queryBuilder->setFirstResult(($page - 1) * $perPage);
111
        $queryBuilder->setMaxResults($perPage);
112
113
        return new Paginator($queryBuilder);
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function createAssociation($entity, string $fieldName, $child)
120
    {
121
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
122
123
        $inverseFieldName = $this->getInverseFieldName($fieldName, $classMetadata);
124
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
125
        $propertyAccessor->setValue($child, $inverseFieldName, $entity);
126
127
        $this->getEntityManager()->persist($child);
128
        $this->getEntityManager()->flush($child);
129
130
        return $child;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function addAssociation($entity, string $fieldName, $id)
137
    {
138
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
139
        $collection = $classMetadata->isCollectionValuedAssociation($fieldName);
140
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
141
        $inverse = $classMetadata->isAssociationInverseSide($fieldName);
142
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
143
        $reference = $this->getEntityManager()->getReference($targetClass, $id);
144
145
        if (!$inverse) {
146
            if ($collection) {
147
                /** @var Collection $collection */
148
                $collection = $propertyAccessor->getValue($entity, $fieldName);
149
                $collection->add($reference);
150
            } else {
151
                $propertyAccessor->setValue($entity, $fieldName, $reference);
152
            }
153
            $this->getEntityManager()->flush($entity);
154
        } else {
155
            $inverseClassMetadata = $this->getEntityManager()->getClassMetadata($targetClass);
156
            $association = $classMetadata->getAssociationMapping($fieldName);
157
            $inverseFieldName = $association['mappedBy'];
158
            $inverseCollection = $inverseClassMetadata->isCollectionValuedAssociation($inverseFieldName);
159
            if ($inverseCollection) {
160
                /** @var Collection $collection */
161
                $collection = $propertyAccessor->getValue($reference, $inverseFieldName);
162
                $collection->add($entity);
163
            } else {
164
                $propertyAccessor->setValue($reference, $inverseFieldName, $entity);
165
            }
166
            $this->getEntityManager()->flush($reference);
167
        }
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function removeAssociation($entity, string $fieldName, $id = null)
174
    {
175
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
176
        $collection = $classMetadata->isCollectionValuedAssociation($fieldName);
177
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
178
        $inverse = $classMetadata->isAssociationInverseSide($fieldName);
179
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
180
181
        if ($inverse) {
182
            $reference = $this->getEntityManager()->getReference($targetClass, $id);
183
            $inverseClassMetadata = $this->getEntityManager()->getClassMetadata($targetClass);
184
            $association = $classMetadata->getAssociationMapping($fieldName);
185
            $inverseFieldName = $association['mappedBy'];
186
            $inverseCollection = $inverseClassMetadata->isCollectionValuedAssociation($inverseFieldName);
187
            if ($inverseCollection) {
188
                /** @var Collection $collection */
189
                $collection = $propertyAccessor->getValue($reference, $inverseFieldName);
190
                $collection->removeElement($entity);
191
            } else {
192
                $propertyAccessor->setValue($reference, $inverseFieldName, null);
193
            }
194
            $this->getEntityManager()->flush($reference);
195
        } else {
196
            if ($collection) {
197
                $reference = $this->getEntityManager()->getReference($targetClass, $id);
198
                /** @var Collection $collection */
199
                $collection = $propertyAccessor->getValue($entity, $fieldName);
200
                $collection->removeElement($reference);
201
            } else {
202
                $propertyAccessor->setValue($entity, $fieldName, null);
203
            }
204
            $this->getEntityManager()->flush($entity);
205
        }
206
    }
207
208
    /**
209
     * @param string        $fieldName
210
     * @param ClassMetadata $classMetadata
211
     *
212
     * @return string
213
     */
214
    protected function getInverseFieldName(string $fieldName, ClassMetadata $classMetadata)
215
    {
216
        $association = $classMetadata->getAssociationMapping($fieldName);
217
        if ($classMetadata->isAssociationInverseSide($fieldName)) {
218
            return $association['mappedBy'];
219
        } else {
220
            return $association['inversedBy'];
221
        }
222
    }
223
224
    /**
225
     * @param LoggerInterface $logger
226
     */
227
    public function setLogger(LoggerInterface $logger): void
228
    {
229
        $this->logger = $logger;
230
    }
231
232
    /**
233
     * @return LoggerInterface
234
     */
235
    public function getLogger(): LoggerInterface
236
    {
237
        if (null === $this->logger) {
238
            $this->logger = new NullLogger();
239
        }
240
241
        return $this->logger;
242
    }
243
}
244