Completed
Push — master ( c91a13...f49584 )
by max
02:26
created

FinderAggregateRepository::with()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 10
rs 9.4285
cc 2
eloc 5
nc 2
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
            $criteria = $this->relatedRepository[$relatedEntityName]->createCriteria(['id.in' => (array)$relatedEntityIds[$relatedEntityName]]);
132
            $relatedEntities[$relatedEntityName] = $this->relatedRepository[$relatedEntityName]->findMany($criteria);
133
        }
134
135
        $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...
136
137
        $entityArgs = [
138
            'data' => $this->mapper->fromTableRow($row)
139
        ];
140
141 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...
142
            if (isset($relatedEntities[$entityName])) {
143
                if (isset($relatedEntities[$entityName][$row[$relatedField]])) {
144
                    $entityArgs['aggregateItems'][] = $relatedEntities[$entityName][$row[$relatedField]];
145
                } else {
146
                    $entityArgs['aggregateItems'][] = null;
147
                }
148
            } else {
149
                $entityArgs['aggregateItems'][] = null;
150
            }
151
        }
152
153
        $entity = $this->entityFactory->create($entityArgs);
154
155
        $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...
156
157
        return $entity;
158
    }
159
160
    /**
161
     * @param mixed $criteria
162
     * @return EntityInterface[]
163
     */
164
    public function findMany($criteria)
165
    {
166
        if (empty($this->with)) {
167
            return $this->entityRepository->findMany($criteria);
168
        }
169
170
        /** @var Select $select */
171
        $select = $criteria->getQuery();
172
173
        $rows = $this->tableGateway->selectWith($select)->toArray();
174
175 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...
176
            foreach ($this->with as $relatedEntityName => $cascadeWith) {
177
                $relatedField = $this->getRelatedField($relatedEntityName);
178
179
                if (!isset($row[$relatedField])) {
180
                    throw new \RuntimeException(get_class($this) . ": relation field $relatedEntityName not fetched");
181
                }
182
183
                if (!isset($relatedEntityIds[$relatedEntityName])) {
184
                    $relatedEntityIds[$relatedEntityName] = new ArrayObject();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$relatedEntityIds was never initialized. Although not strictly required by PHP, it is generally a good practice to add $relatedEntityIds = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
185
                }
186
187
                if (!in_array($row[$relatedField], (array)$relatedEntityIds[$relatedEntityName])) {
188
                    $relatedEntityIds[$relatedEntityName]->append($row[$relatedField]);
0 ignored issues
show
Bug introduced by
The variable $relatedEntityIds 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...
189
                }
190
            }
191
        }
192
193
        $relatedEntities = [];
194 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...
195
            $criteria = $this->relatedRepository[$relatedEntityName]->createCriteria(['id.in' => (array)$relatedEntityIds[$relatedEntityName]]);
196
            $relatedEntities[$relatedEntityName] = $this->relatedRepository[$relatedEntityName]->findMany($criteria);
197
        }
198
199
        $entitiesArgs = [];
200
        foreach ($rows as &$row) {
201
            $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...
202
203
            $entityArgs = [
204
                'data' => $this->mapper->fromTableRow($row)
205
            ];
206
207 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...
208
                if (isset($relatedEntities[$entityName])) {
209
                    if (isset($relatedEntities[$entityName][$row[$relatedField]])) {
210
                        $entityArgs['aggregateItems'][] = $relatedEntities[$entityName][$row[$relatedField]];
211
                    } else {
212
                        $entityArgs['aggregateItems'][] = null;
213
                    }
214
                } else {
215
                    $entityArgs['aggregateItems'][] = null;
216
                }
217
            }
218
219
            $entitiesArgs[] = $entityArgs;
220
        }
221
222
        $entities = $this->entityFactory->createCollection($entitiesArgs);
223
224
        $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...
225
226
        return $entities;
227
    }
228
229
    /**
230
     * @param mixed $id
231
     * @return EntityInterface|null
232
     */
233
    public function findById($id)
234
    {
235
        $criteria = $this->createCriteria();
236
        $criteria->equalTo('id', $id);
237
238
        return $this->find($criteria);
239
    }
240
241
    private function getRelatedField($entityName)
242
    {
243
        if (!isset($this->relationsConfig[$entityName])) {
244
            throw new \RuntimeException(get_class($this) . ": relation $entityName not exists");
245
        }
246
247
        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...
248
249
        return $field;
250
    }
251
252
    /**
253
     * @param mixed $criteria
254
     * @return int
255
     */
256
    public function count($criteria)
257
    {
258
        return $this->entityRepository->count($criteria);
259
    }
260
261
    /**
262
     * @param array $filter
263
     * @return CriteriaInterface
264
     */
265
    public function createCriteria(array $filter = [])
266
    {
267
        return $this->entityRepository->createCriteria($filter);
268
    }
269
270
    /**
271
     * @param EntityInterface $entity
272
     * @return EntityInterface|int|null
273
     */
274
    public function add(EntityInterface $entity)
275
    {
276
        throw new \RuntimeException(get_class($this) . ' cannot adding');
277
    }
278
279
    /**
280
     * @param EntityInterface $entity
281
     * @return void
282
     */
283
    public function remove(EntityInterface $entity)
284
    {
285
        throw new \RuntimeException(get_class($this) . ' cannot removing');
286
    }
287
}
288