Completed
Push — master ( 74c3c7...591b88 )
by Eric
08:09
created

Repository   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 217
Duplicated Lines 14.29 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 29
c 1
b 0
f 0
lcom 1
cbo 8
dl 31
loc 217
ccs 78
cts 78
cp 1
rs 10

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A findForIndex() 0 8 1
A findForShow() 0 4 1
A findForUpdate() 0 4 1
A findForDelete() 0 4 1
A findOneBy() 0 4 1
A findBy() 14 14 3
A createQueryBuilder() 0 4 2
A createQueryBuilderForCollection() 0 4 1
A createDataSourceBuilder() 0 4 1
A getProperty() 0 4 1
A buildQueryBuilder() 9 9 2
B applyCriteria() 0 24 5
A applySorting() 8 8 3
A createParameter() 0 4 1
A createPlaceholder() 0 4 1
A getRootAlias() 0 8 2
A getAlias() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/*
4
 * This file is part of the Lug package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Lug\Component\Resource\Repository\Doctrine\ORM;
13
14
use Doctrine\ORM\EntityManager;
15
use Doctrine\ORM\EntityRepository;
16
use Doctrine\ORM\Mapping\ClassMetadata;
17
use Doctrine\ORM\QueryBuilder;
18
use Lug\Component\Grid\DataSource\Doctrine\ORM\DataSourceBuilder;
19
use Lug\Component\Resource\Model\ResourceInterface;
20
use Lug\Component\Resource\Repository\RepositoryInterface;
21
use Pagerfanta\Adapter\DoctrineORMAdapter;
22
use Pagerfanta\Pagerfanta;
23
24
/**
25
 * @author GeLo <[email protected]>
26
 */
27
class Repository extends EntityRepository implements RepositoryInterface
28
{
29
    /**
30
     * @var ResourceInterface
31
     */
32
    private $resource;
33
34
    /**
35
     * @param EntityManager     $em
36
     * @param ClassMetadata     $class
37
     * @param ResourceInterface $resource
38
     */
39 22
    public function __construct($em, ClassMetadata $class, ResourceInterface $resource)
40
    {
41 22
        parent::__construct($em, $class);
42
43 22
        $this->resource = $resource;
44 22
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 1
    public function findForIndex(array $criteria, array $orderBy = [])
50
    {
51 1
        return new Pagerfanta(new DoctrineORMAdapter(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Pagerfanta\P... true), false, false)); (Pagerfanta\Pagerfanta) is incompatible with the return type declared by the interface Lug\Component\Resource\R...Interface::findForIndex of type object[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
52 1
            $this->buildQueryBuilder($criteria, $orderBy, true),
53 1
            false,
54
            false
55 1
        ));
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 1
    public function findForShow(array $criteria, array $orderBy = [])
62
    {
63 1
        return $this->findOneBy($criteria, $orderBy);
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 1
    public function findForUpdate(array $criteria, array $orderBy = [])
70
    {
71 1
        return $this->findOneBy($criteria, $orderBy);
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77 1
    public function findForDelete(array $criteria, array $orderBy = [])
78
    {
79 1
        return $this->findOneBy($criteria, $orderBy);
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 7
    public function findOneBy(array $criteria, array $orderBy = [])
86
    {
87 7
        return $this->buildQueryBuilder($criteria, $orderBy)->getQuery()->getOneOrNullResult();
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 1 View Code Duplication
    public function findBy(array $criteria, array $orderBy = [], $limit = null, $offset = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
    {
95 1
        $queryBuilder = $this->buildQueryBuilder($criteria, $orderBy);
96
97 1
        if ($limit !== null) {
98 1
            $queryBuilder->setMaxResults($limit);
99 1
        }
100
101 1
        if ($offset !== null) {
102 1
            $queryBuilder->setFirstResult($offset);
103 1
        }
104
105 1
        return $queryBuilder->getQuery()->getResult();
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 18
    public function createQueryBuilder($alias = null, $indexBy = null)
112
    {
113 18
        return parent::createQueryBuilder($alias ?: $this->getAlias(), $indexBy);
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119 5
    public function createQueryBuilderForCollection($alias = null, $indexBy = null)
120
    {
121 5
        return $this->createQueryBuilder($alias, $indexBy);
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127 1
    public function createDataSourceBuilder(array $options = [])
128
    {
129 1
        return new DataSourceBuilder($this, $options);
130
    }
131
132
    /**
133
     * @param string                   $property
134
     * @param QueryBuilder|string|null $root
135
     *
136
     * @return string
137
     */
138 11
    public function getProperty($property, $root = null)
139
    {
140 11
        return $this->getRootAlias($root).'.'.$property;
141
    }
142
143
    /**
144
     * @param mixed[]  $criteria
145
     * @param string[] $orderBy
146
     * @param bool     $collection
147
     *
148
     * @return QueryBuilder
149
     */
150 9 View Code Duplication
    protected function buildQueryBuilder(array $criteria, array $orderBy, $collection = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
    {
152 9
        $queryBuilder = $collection ? $this->createQueryBuilderForCollection() : $this->createQueryBuilder();
153
154 9
        $this->applyCriteria($queryBuilder, $criteria);
155 9
        $this->applySorting($queryBuilder, $orderBy);
156
157 9
        return $queryBuilder;
158
    }
159
160
    /**
161
     * @param QueryBuilder $queryBuilder
162
     * @param mixed[]      $criteria
163
     */
164 9
    private function applyCriteria(QueryBuilder $queryBuilder, array $criteria = null)
165
    {
166 9
        foreach ($criteria as $property => $value) {
0 ignored issues
show
Bug introduced by
The expression $criteria of type null|array<integer,*> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
167 9
            if ($value === null) {
168 1
                $queryBuilder->andWhere($queryBuilder->expr()->isNull(
169 1
                    $this->getProperty($property, $queryBuilder)
170 1
                ));
171 9
            } elseif (is_array($value)) {
172
                $queryBuilder
173 1
                    ->andWhere($queryBuilder->expr()->in(
174 1
                        $property = $this->getProperty($property, $queryBuilder),
175 1
                        $this->createPlaceholder($parameter = $this->createParameter($property))
176 1
                    ))
177 1
                    ->setParameter($parameter, $value);
178 8
            } elseif (!empty($value)) {
179
                $queryBuilder
180 7
                    ->andWhere($queryBuilder->expr()->eq(
181 7
                        $property = $this->getProperty($property, $queryBuilder),
182 7
                        $this->createPlaceholder($parameter = $this->createParameter($property))
183 7
                    ))
184 7
                    ->setParameter($parameter, $value);
185 7
            }
186 9
        }
187 9
    }
188
189
    /**
190
     * @param QueryBuilder $queryBuilder
191
     * @param string[]     $orderBy
192
     */
193 9 View Code Duplication
    private function applySorting(QueryBuilder $queryBuilder, array $orderBy = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
    {
195 9
        foreach ($orderBy as $property => $order) {
196 8
            if (!empty($order)) {
197 8
                $queryBuilder->addOrderBy($this->getProperty($property, $queryBuilder), $order);
198 8
            }
199 9
        }
200 9
    }
201
202
    /**
203
     * @param string $property
204
     *
205
     * @return string
206
     */
207 8
    private function createParameter($property)
208
    {
209 8
        return str_replace('.', '_', $property).'_'.str_replace('.', '', uniqid(null, true));
210
    }
211
212
    /**
213
     * @param string $parameter
214
     *
215
     * @return string
216
     */
217 8
    private function createPlaceholder($parameter)
218
    {
219 8
        return ':'.$parameter;
220
    }
221
222
    /**
223
     * @param QueryBuilder|string|null $root
224
     *
225
     * @return string
226
     */
227 11
    private function getRootAlias($root)
228
    {
229 11
        if ($root instanceof QueryBuilder) {
230 11
            $root = $root->getRootAliases()[0];
231 11
        }
232
233 11
        return $root;
234
    }
235
236
    /**
237
     * @return string
238
     */
239 16
    private function getAlias()
240
    {
241 16
        return $this->resource->getName();
242
    }
243
}
244