Completed
Push — master ( 2f6e9d...9e3fd9 )
by Philip
04:29
created

DoctrineCrudService   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 36.84%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 10
dl 0
loc 241
ccs 42
cts 114
cp 0.3684
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A findAllPaginated() 0 8 1
A createFindAllQueryBuilder() 0 6 1
A create() 0 9 2
A update() 0 8 2
A remove() 0 7 2
B findAssociationPaginated() 0 27 3
A createAssociation() 0 12 1
B addAssociation() 0 33 4
B removeAssociation() 0 34 4
A getInverseFieldName() 0 9 2
A setLogger() 0 4 1
A getLogger() 0 8 2
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 8
    public function __construct(EntityManager $em, $class)
35
    {
36 8
        $classMetaData = $class;
37 8
        if (is_string($classMetaData)) {
38
            $classMetaData = $em->getClassMetadata($classMetaData);
39
        }
40 8
        parent::__construct($em, $classMetaData);
41 8
        $this->transactionManager = new TransactionManager($em);
42 8
    }
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, bool $flush = true)
67
    {
68
        $this->getEntityManager()->persist($entity);
69
        if ($flush) {
70
            $this->getEntityManager()->flush($entity);
71
        }
72
73
        return $entity;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function update($entity, bool $flush = false)
80
    {
81
        if ($flush) {
82
            $this->getEntityManager()->flush($entity);
83
        }
84
85
        return $entity;
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function remove($entity, bool $flush = false)
92
    {
93
        $this->getEntityManager()->remove($entity);
94
        if ($flush) {
95
            $this->getEntityManager()->flush($entity);
96
        }
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 6
    public function findAssociationPaginated($entity, string $fieldName, int $page = 1, $perPage = 50)
103
    {
104 6
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
105 6
        $association = $classMetadata->associationMappings[$fieldName];
106 6
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
107 6
        $inverseFieldName = $this->getInverseFieldName($fieldName, $classMetadata);
108
109 6
        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
110 6
        $queryBuilder->select('association');
111 6
        $queryBuilder->from($targetClass, 'association');
112 6
        $queryBuilder->join('association.' . $inverseFieldName, 'entity');
113 6
        $queryBuilder->where('entity = :entity');
114
115 6
        if (array_key_exists('orderBy', $association)) {
116 4
            $orderBy = $association['orderBy'];
117 4
            foreach ($orderBy as $fieldName => $order) {
118 4
                $queryBuilder->addOrderBy('association.' . $fieldName, $order);
119
            }
120
        }
121
122 6
        $queryBuilder->setParameter('entity', $entity);
123
124 6
        $queryBuilder->setFirstResult(($page - 1) * $perPage);
125 6
        $queryBuilder->setMaxResults($perPage);
126
127 6
        return new Paginator($queryBuilder);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function createAssociation($entity, string $fieldName)
134
    {
135
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
136
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
137
        $child = new $targetClass;
138
139
        $inverseFieldName = $this->getInverseFieldName($fieldName, $classMetadata);
140
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
141
        $propertyAccessor->setValue($child, $inverseFieldName, $entity);
142
143
        return $child;
0 ignored issues
show
Bug Compatibility introduced by
The expression return $child; of type object|array is incompatible with the return type declared by the interface Dontdrinkandroot\Service...face::createAssociation of type object as it can also be of type array which is not included in this return type.
Loading history...
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 2
    public function addAssociation($entity, string $fieldName, $id)
150
    {
151 2
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
152 2
        $collection = $classMetadata->isCollectionValuedAssociation($fieldName);
153 2
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
154 2
        $inverse = $classMetadata->isAssociationInverseSide($fieldName);
155 2
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
156 2
        $reference = $this->getEntityManager()->getReference($targetClass, $id);
157
158 2
        if (!$inverse) {
159 2
            if ($collection) {
160
                /** @var Collection $collection */
161 2
                $collection = $propertyAccessor->getValue($entity, $fieldName);
162 2
                $collection->add($reference);
163
            } else {
164
                $propertyAccessor->setValue($entity, $fieldName, $reference);
165
            }
166 2
            $this->getEntityManager()->flush($entity);
167
        } else {
168
            $inverseClassMetadata = $this->getEntityManager()->getClassMetadata($targetClass);
169
            $association = $classMetadata->getAssociationMapping($fieldName);
170
            $inverseFieldName = $association['mappedBy'];
171
            $inverseCollection = $inverseClassMetadata->isCollectionValuedAssociation($inverseFieldName);
172
            if ($inverseCollection) {
173
                /** @var Collection $collection */
174
                $collection = $propertyAccessor->getValue($reference, $inverseFieldName);
0 ignored issues
show
Bug introduced by
It seems like $reference defined by $this->getEntityManager(...ence($targetClass, $id) on line 156 can also be of type null; however, Symfony\Component\Proper...rtyAccessor::getValue() does only seem to accept object|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
175
                $collection->add($entity);
176
            } else {
177
                $propertyAccessor->setValue($reference, $inverseFieldName, $entity);
0 ignored issues
show
Bug introduced by
It seems like $reference defined by $this->getEntityManager(...ence($targetClass, $id) on line 156 can also be of type null; however, Symfony\Component\Proper...rtyAccessor::setValue() does only seem to accept object|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
178
            }
179
            $this->getEntityManager()->flush($reference);
180
        }
181 2
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186
    public function removeAssociation($entity, string $fieldName, $id = null)
187
    {
188
        $classMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
189
        $collection = $classMetadata->isCollectionValuedAssociation($fieldName);
190
        $targetClass = $classMetadata->getAssociationTargetClass($fieldName);
191
        $inverse = $classMetadata->isAssociationInverseSide($fieldName);
192
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
193
194
        if ($inverse) {
195
            $reference = $this->getEntityManager()->getReference($targetClass, $id);
196
            $inverseClassMetadata = $this->getEntityManager()->getClassMetadata($targetClass);
197
            $association = $classMetadata->getAssociationMapping($fieldName);
198
            $inverseFieldName = $association['mappedBy'];
199
            $inverseCollection = $inverseClassMetadata->isCollectionValuedAssociation($inverseFieldName);
200
            if ($inverseCollection) {
201
                /** @var Collection $collection */
202
                $collection = $propertyAccessor->getValue($reference, $inverseFieldName);
0 ignored issues
show
Bug introduced by
It seems like $reference defined by $this->getEntityManager(...ence($targetClass, $id) on line 195 can also be of type null; however, Symfony\Component\Proper...rtyAccessor::getValue() does only seem to accept object|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
203
                $collection->removeElement($entity);
204
            } else {
205
                $propertyAccessor->setValue($reference, $inverseFieldName, null);
0 ignored issues
show
Bug introduced by
It seems like $reference defined by $this->getEntityManager(...ence($targetClass, $id) on line 195 can also be of type null; however, Symfony\Component\Proper...rtyAccessor::setValue() does only seem to accept object|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
206
            }
207
            $this->getEntityManager()->flush($reference);
208
        } else {
209
            if ($collection) {
210
                $reference = $this->getEntityManager()->getReference($targetClass, $id);
211
                /** @var Collection $collection */
212
                $collection = $propertyAccessor->getValue($entity, $fieldName);
213
                $collection->removeElement($reference);
214
            } else {
215
                $propertyAccessor->setValue($entity, $fieldName, null);
216
            }
217
            $this->getEntityManager()->flush($entity);
218
        }
219
    }
220
221
    /**
222
     * @param string        $fieldName
223
     * @param ClassMetadata $classMetadata
224
     *
225
     * @return string
226
     */
227 6
    protected function getInverseFieldName(string $fieldName, ClassMetadata $classMetadata)
228
    {
229 6
        $association = $classMetadata->getAssociationMapping($fieldName);
230 6
        if ($classMetadata->isAssociationInverseSide($fieldName)) {
231 2
            return $association['mappedBy'];
232
        } else {
233 4
            return $association['inversedBy'];
234
        }
235
    }
236
237
    /**
238
     * @param LoggerInterface $logger
239
     */
240
    public function setLogger(LoggerInterface $logger): void
241
    {
242
        $this->logger = $logger;
243
    }
244
245
    /**
246
     * @return LoggerInterface
247
     */
248
    public function getLogger(): LoggerInterface
249
    {
250
        if (null === $this->logger) {
251
            $this->logger = new NullLogger();
252
        }
253
254
        return $this->logger;
255
    }
256
}
257