Completed
Push — master ( 2d0f09...92bffa )
by max
02:45
created

FinderAggregateRepository   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 260
Duplicated Lines 20.77 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 31
c 1
b 0
f 1
lcom 1
cbo 7
dl 54
loc 260
rs 9.8

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A findWith() 0 8 2
C find() 26 60 10
C findMany() 28 60 11
A findById() 0 7 1
A getRelatedField() 0 10 2
A count() 0 4 1
A createCriteria() 0 4 1
A add() 0 4 1
A remove() 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
namespace T4webInfrastructure;
4
5
use ArrayObject;
6
use Zend\Db\TableGateway\TableGateway;
7
use Zend\Db\Sql\Select;
8
use T4webDomainInterface\Infrastructure\RepositoryInterface;
9
use T4webDomainInterface\Infrastructure\CriteriaInterface;
10
use T4webDomainInterface\EntityInterface;
11
use T4webDomainInterface\EntityFactoryInterface;
12
13
class FinderAggregateRepository implements RepositoryInterface
14
{
15
    /**
16
     * @var TableGateway
17
     */
18
    private $tableGateway;
19
20
    /**
21
     * @var Mapper
22
     */
23
    private $mapper;
24
25
    /**
26
     * @var EntityFactoryInterface
27
     */
28
    private $entityFactory;
29
30
    /**
31
     * @var RepositoryInterface
32
     */
33
    private $entityRepository;
34
35
    /**
36
     * @var RepositoryInterface[]
37
     */
38
    private $relatedRepository;
39
40
    /**
41
     * @var array
42
     */
43
    private $relationsConfig;
44
45
    /**
46
     * @var ArrayObject[]
47
     */
48
    private $with;
49
50
    /**
51
     * FinderAggregateRepository constructor.
52
     * @param TableGateway $tableGateway
53
     * @param Mapper $mapper
54
     * @param EntityFactoryInterface $entityFactory
55
     * @param RepositoryInterface $entityRepository
56
     * @param RepositoryInterface[] $relatedRepository
57
     * @param array $relationsConfig
58
     */
59
    public function __construct(
60
        TableGateway $tableGateway,
61
        Mapper $mapper,
62
        EntityFactoryInterface $entityFactory,
63
        RepositoryInterface $entityRepository,
64
        array $relatedRepository,
65
        array $relationsConfig)
66
    {
67
        $this->tableGateway = $tableGateway;
68
        $this->mapper = $mapper;
69
        $this->entityFactory = $entityFactory;
70
        $this->entityRepository = $entityRepository;
71
        $this->relatedRepository = $relatedRepository;
72
        $this->relationsConfig = $relationsConfig;
73
    }
74
75
    public function findWith($entityName)
76
    {
77
        if (!isset($this->relatedRepository[$entityName])) {
78
            throw new \RuntimeException(get_class($this) . ": related $entityName repository not exists");
79
        }
80
81
        $this->with[$entityName] = new ArrayObject();
82
    }
83
84
    /**
85
     * @param mixed $criteria
86
     * @return EntityInterface|null
87
     */
88
    public function find($criteria)
89
    {
90
        if (empty($this->with)) {
91
            return $this->entityRepository->find($criteria);
92
        }
93
94
        /** @var Select $select */
95
        $select = $criteria->getQuery();
96
97
        $select->limit(1)->offset(0);
98
        $result = $this->tableGateway->selectWith($select)->toArray();
99
100
        if (!isset($result[0])) {
101
            return;
102
        }
103
104
        $row = $result[0];
105
106 View Code Duplication
        foreach ($this->with as $relatedEntityName => $relatedEntityIds) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
107
            $relatedField = $this->getRelatedField($relatedEntityName);
108
109
            if (!isset($row[$relatedField])) {
110
                throw new \RuntimeException(get_class($this) . ": relation field $relatedEntityName not fetched");
111
            }
112
113
            if (!in_array($row[$relatedField], (array)$relatedEntityIds)) {
114
                $relatedEntityIds->append($row[$relatedField]);
115
            }
116
        }
117
118
        $relatedEntities = [];
119 View Code Duplication
        foreach ($this->with as $relatedEntityName => $relatedEntityIds) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
120
            $criteria = $this->relatedRepository[$relatedEntityName]->createCriteria(['id.in' => (array)$relatedEntityIds]);
121
            $relatedEntities[$relatedEntityName] = $this->relatedRepository[$relatedEntityName]->findMany($criteria);
122
        }
123
124
        $relatedField = $this->getRelatedField($relatedEntityName);
0 ignored issues
show
Bug introduced by
The variable $relatedEntityName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
125
126
        $entityArgs = [
127
            'data' => $this->mapper->fromTableRow($row)
128
        ];
129
130 View Code Duplication
        foreach ($this->relationsConfig as $entityName => $joinRule) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
131
            if (isset($relatedEntities[$entityName])) {
132
                if (isset($relatedEntities[$entityName][$row[$relatedField]])) {
133
                    $entityArgs['aggregateItems'][] = $relatedEntities[$entityName][$row[$relatedField]];
134
                } else {
135
                    $entityArgs['aggregateItems'][] = null;
136
                }
137
            } else {
138
                $entityArgs['aggregateItems'][] = null;
139
            }
140
        }
141
142
        $entity = $this->entityFactory->create($entityArgs);
143
144
        $this->with = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<ArrayObject>> of property $with.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
145
146
        return $entity;
147
    }
148
149
    /**
150
     * @param mixed $criteria
151
     * @return EntityInterface[]
152
     */
153
    public function findMany($criteria)
154
    {
155
        if (empty($this->with)) {
156
            return $this->entityRepository->findMany($criteria);
157
        }
158
159
        /** @var Select $select */
160
        $select = $criteria->getQuery();
161
162
        $rows = $this->tableGateway->selectWith($select)->toArray();
163
164 View Code Duplication
        foreach ($rows as $row) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
165
            foreach ($this->with as $relatedEntityName => $relatedEntityIds) {
166
                $relatedField = $this->getRelatedField($relatedEntityName);
167
168
                if (!isset($row[$relatedField])) {
169
                    throw new \RuntimeException(get_class($this) . ": relation field $relatedEntityName not fetched");
170
                }
171
172
                if (!in_array($row[$relatedField], (array)$relatedEntityIds)) {
173
                    $relatedEntityIds->append($row[$relatedField]);
174
                }
175
            }
176
        }
177
178
        $relatedEntities = [];
179 View Code Duplication
        foreach ($this->with as $relatedEntityName => $relatedEntityIds) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
180
            $criteria = $this->relatedRepository[$relatedEntityName]->createCriteria(['id.in' => (array)$relatedEntityIds]);
181
            $relatedEntities[$relatedEntityName] = $this->relatedRepository[$relatedEntityName]->findMany($criteria);
182
        }
183
184
        $entitiesArgs = [];
185
        foreach ($rows as &$row) {
186
            $relatedField = $this->getRelatedField($relatedEntityName);
0 ignored issues
show
Bug introduced by
The variable $relatedEntityName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
187
188
            $entityArgs = [
189
                'data' => $this->mapper->fromTableRow($row)
190
            ];
191
192 View Code Duplication
            foreach ($this->relationsConfig as $entityName => $joinRule) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
193
                if (isset($relatedEntities[$entityName])) {
194
                    if (isset($relatedEntities[$entityName][$row[$relatedField]])) {
195
                        $entityArgs['aggregateItems'][] = $relatedEntities[$entityName][$row[$relatedField]];
196
                    } else {
197
                        $entityArgs['aggregateItems'][] = null;
198
                    }
199
                } else {
200
                    $entityArgs['aggregateItems'][] = null;
201
                }
202
            }
203
204
            $entitiesArgs[] = $entityArgs;
205
        }
206
207
        $entities = $this->entityFactory->createCollection($entitiesArgs);
208
209
        $this->with = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<ArrayObject>> of property $with.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
210
211
        return $entities;
212
    }
213
214
    /**
215
     * @param mixed $id
216
     * @return EntityInterface|null
217
     */
218
    public function findById($id)
219
    {
220
        $criteria = $this->createCriteria();
221
        $criteria->equalTo('id', $id);
222
223
        return $this->find($criteria);
224
    }
225
226
    private function getRelatedField($entityName)
227
    {
228
        if (!isset($this->relationsConfig[$entityName])) {
229
            throw new \RuntimeException(get_class($this) . ": relation $entityName not exists");
230
        }
231
232
        list($table, $field) = explode('.', $this->relationsConfig[$entityName][0]);
0 ignored issues
show
Unused Code introduced by
The assignment to $table is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
233
234
        return $field;
235
    }
236
237
    /**
238
     * @param mixed $criteria
239
     * @return int
240
     */
241
    public function count($criteria)
242
    {
243
        return $this->entityRepository->count($criteria);
244
    }
245
246
    /**
247
     * @param array $filter
248
     * @return CriteriaInterface
249
     */
250
    public function createCriteria(array $filter = [])
251
    {
252
        return $this->entityRepository->createCriteria($filter);
253
    }
254
255
    /**
256
     * @param EntityInterface $entity
257
     * @return EntityInterface|int|null
258
     */
259
    public function add(EntityInterface $entity)
260
    {
261
        throw new \RuntimeException(get_class($this) . ' cannot adding');
262
    }
263
264
    /**
265
     * @param EntityInterface $entity
266
     * @return void
267
     */
268
    public function remove(EntityInterface $entity)
269
    {
270
        throw new \RuntimeException(get_class($this) . ' cannot removing');
271
    }
272
}