Completed
Push — master ( 42800d...60ea26 )
by max
02:48
created

FinderAggregateRepository::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
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
    /**
76
     * @param string $entityName
77
     * @return $this
78
     */
79
    public function with($entityName)
80
    {
81
        if (!isset($this->relatedRepository[$entityName])) {
82
            throw new \RuntimeException(get_class($this) . ": related $entityName repository not exists");
83
        }
84
85
        $this->with[$entityName] = [];
86
87
        return $this;
88
    }
89
90
    /**
91
     * @param mixed $criteria
92
     * @return EntityInterface|null
93
     */
94
    public function find($criteria)
95
    {
96
        if (empty($this->with)) {
97
            return $this->entityRepository->find($criteria);
98
        }
99
100
        /** @var Select $select */
101
        $select = $criteria->getQuery();
102
103
        $select->limit(1)->offset(0);
104
        $result = $this->tableGateway->selectWith($select)->toArray();
105
106
        if (!isset($result[0])) {
107
            return;
108
        }
109
110
        $row = $result[0];
111
112
        $relatedEntityIds = [];
113 View Code Duplication
        foreach ($this->with as $relatedEntityName => $cascadeWith) {
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...
114
            $relatedField = $this->getRelatedField($relatedEntityName);
115
116
            if (!isset($row[$relatedField])) {
117
                throw new \RuntimeException(get_class($this) . ": relation field $relatedEntityName not fetched");
118
            }
119
120
            if (!isset($relatedEntityIds[$relatedEntityName])) {
121
                $relatedEntityIds[$relatedEntityName] = new ArrayObject();
122
            }
123
124
            if (!in_array($row[$relatedField], (array)$relatedEntityIds[$relatedEntityName])) {
125
                $relatedEntityIds[$relatedEntityName]->append($row[$relatedField]);
126
            }
127
        }
128
129
        $relatedEntities = [];
130 View Code Duplication
        foreach ($this->with as $relatedEntityName => $cascadeWith) {
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 (empty((array)$relatedEntityIds[$relatedEntityName])) {
132
                continue;
133
            }
134
            
135
            $criteria = $this->relatedRepository[$relatedEntityName]->createCriteria(['id.in' => (array)$relatedEntityIds[$relatedEntityName]]);
136
            $relatedEntities[$relatedEntityName] = $this->relatedRepository[$relatedEntityName]->findMany($criteria);
137
        }
138
139
        $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...
140
141
        $entityArgs = [
142
            'data' => $this->mapper->fromTableRow($row)
143
        ];
144
145 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...
146
            if (isset($relatedEntities[$entityName])) {
147
                if (isset($relatedEntities[$entityName][$row[$relatedField]])) {
148
                    $entityArgs['aggregateItems'][] = $relatedEntities[$entityName][$row[$relatedField]];
149
                } else {
150
                    $entityArgs['aggregateItems'][] = null;
151
                }
152
            } else {
153
                $entityArgs['aggregateItems'][] = null;
154
            }
155
        }
156
157
        $entity = $this->entityFactory->create($entityArgs);
158
159
        $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...
160
161
        return $entity;
162
    }
163
164
    /**
165
     * @param mixed $criteria
166
     * @return EntityInterface[]
167
     */
168
    public function findMany($criteria)
169
    {
170
        if (empty($this->with)) {
171
            return $this->entityRepository->findMany($criteria);
172
        }
173
174
        /** @var Select $select */
175
        $select = $criteria->getQuery();
176
177
        $rows = $this->tableGateway->selectWith($select)->toArray();
178
        
179
        if (empty($rows)) {
180
            return [];
181
        }
182
183
        $relatedEntityIds = [];
184 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...
185
            foreach ($this->with as $relatedEntityName => $cascadeWith) {
186
                $relatedField = $this->getRelatedField($relatedEntityName);
187
188
                if (!isset($row[$relatedField])) {
189
                    throw new \RuntimeException(get_class($this) . ": relation field $relatedEntityName not fetched");
190
                }
191
192
                if (!isset($relatedEntityIds[$relatedEntityName])) {
193
                    $relatedEntityIds[$relatedEntityName] = new ArrayObject();
194
                }
195
196
                if (!in_array($row[$relatedField], (array)$relatedEntityIds[$relatedEntityName])) {
197
                    $relatedEntityIds[$relatedEntityName]->append($row[$relatedField]);
198
                }
199
            }
200
        }
201
202
        $relatedEntities = [];
203 View Code Duplication
        foreach ($this->with as $relatedEntityName => $cascadeWith) {
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...
204
            if (empty((array)$relatedEntityIds[$relatedEntityName])) {
205
                continue;
206
            }
207
            
208
            $criteria = $this->relatedRepository[$relatedEntityName]->createCriteria(['id.in' => (array)$relatedEntityIds[$relatedEntityName]]);
209
            $relatedEntities[$relatedEntityName] = $this->relatedRepository[$relatedEntityName]->findMany($criteria);
210
        }
211
212
        $entitiesArgs = [];
213
        foreach ($rows as &$row) {
214
            $entityArgs = [
215
                'data' => $this->mapper->fromTableRow($row)
216
            ];
217
218 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...
219
                $relatedField = $this->getRelatedField($entityName);
220
                
221
                if (isset($relatedEntities[$entityName])) {
222
                    if (isset($relatedEntities[$entityName][$row[$relatedField]])) {
223
                        $entityArgs['aggregateItems'][] = $relatedEntities[$entityName][$row[$relatedField]];
224
                    } else {
225
                        $entityArgs['aggregateItems'][] = null;
226
                    }
227
                } else {
228
                    $entityArgs['aggregateItems'][] = null;
229
                }
230
            }
231
232
            $entitiesArgs[] = $entityArgs;
233
        }
234
235
        $entities = $this->entityFactory->createCollection($entitiesArgs);
236
237
        $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...
238
239
        return $entities;
240
    }
241
242
    /**
243
     * @param mixed $id
244
     * @return EntityInterface|null
245
     */
246
    public function findById($id)
247
    {
248
        $criteria = $this->createCriteria();
249
        $criteria->equalTo('id', $id);
250
251
        return $this->find($criteria);
252
    }
253
254
    private function getRelatedField($entityName)
255
    {
256
        if (!isset($this->relationsConfig[$entityName])) {
257
            throw new \RuntimeException(get_class($this) . ": relation $entityName not exists");
258
        }
259
260
        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...
261
262
        return $field;
263
    }
264
265
    /**
266
     * @param mixed $criteria
267
     * @return int
268
     */
269
    public function count($criteria)
270
    {
271
        return $this->entityRepository->count($criteria);
272
    }
273
274
    /**
275
     * @param array $filter
276
     * @return CriteriaInterface
277
     */
278
    public function createCriteria(array $filter = [])
279
    {
280
        return $this->entityRepository->createCriteria($filter);
281
    }
282
283
    /**
284
     * @param EntityInterface $entity
285
     * @return EntityInterface|int|null
286
     */
287
    public function add(EntityInterface $entity)
288
    {
289
        throw new \RuntimeException(get_class($this) . ' cannot adding');
290
    }
291
292
    /**
293
     * @param EntityInterface $entity
294
     * @return void
295
     */
296
    public function remove(EntityInterface $entity)
297
    {
298
        throw new \RuntimeException(get_class($this) . ' cannot removing');
299
    }
300
}
301