OneToManyReader   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 2
dl 0
loc 170
rs 10
c 0
b 0
f 0
ccs 57
cts 57
cp 1

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 2
B current() 0 33 5
A getRowId() 0 14 2
A next() 0 5 1
A key() 0 4 1
A valid() 0 4 2
A rewind() 0 5 1
A getFields() 0 4 1
A count() 0 4 1
1
<?php
2
3
namespace Ddeboer\DataImport\Reader;
4
5
use Ddeboer\DataImport\Reader;
6
use Ddeboer\DataImport\Exception\ReaderException;
7
8
/**
9
 * Takes multiple readers for processing in the same workflow
10
 *
11
 * @author Adam Paterson <[email protected]>
12
 * @author Aydin Hassan <[email protected]>
13
 */
14
class OneToManyReader implements CountableReader
15
{
16
    /**
17
     * @var Reader
18
     */
19
    protected $leftReader;
20
21
    /**
22
     * @var Reader
23
     */
24
    protected $rightReader;
25
26
    /**
27
     * @var string
28
     */
29
    protected $leftJoinField;
30
31
    /**
32
     * @var string
33
     */
34
    protected $rightJoinField;
35
36
    /**
37
     * Key to nest the rightRows under
38
     *
39
     * @var string
40
     */
41
    protected $nestKey;
42
43
    /**
44
     * @param Reader $leftReader
45
     * @param Reader $rightReader
46
     * @param string $nestKey
47
     * @param string $leftJoinField
48
     * @param string $rightJoinField
49
     */
50 9
    public function __construct(
51
        Reader $leftReader,
52
        Reader $rightReader,
53
        $nestKey,
54
        $leftJoinField,
55
        $rightJoinField = null
56
    ) {
57 9
        if (is_null($rightJoinField)) {
58 8
            $rightJoinField = $leftJoinField;
59 8
        }
60
61 9
        $this->leftJoinField  = $leftJoinField;
62 9
        $this->rightJoinField = $rightJoinField;
63 9
        $this->leftReader     = $leftReader;
64 9
        $this->rightReader    = $rightReader;
65 9
        $this->nestKey        = $nestKey;
66 9
    }
67
68
    /**
69
     * Create an array of children in the leftRow,
70
     * with the data returned from the right reader
71
     * Where the ID fields Match
72
     *
73
     * @return array
74
     *
75
     * @throws ReaderException
76
     */
77 7
    public function current()
78
    {
79 7
        $leftRow = $this->leftReader->current();
80
81 7
        if (array_key_exists($this->nestKey, $leftRow)) {
82 1
            throw new ReaderException(
83 1
                sprintf(
84 1
                    'Left Row: "%s" Reader already contains a field named "%s". Please choose a different nest key field',
85 1
                    $this->key(),
86 1
                    $this->nestKey
87 1
                )
88 1
            );
89
        }
90 6
        $leftRow[$this->nestKey] = [];
91
92 6
        $leftId     = $this->getRowId($leftRow, $this->leftJoinField);
93 5
        $rightRow   = $this->rightReader->current();
94 5
        $rightId    = $this->getRowId($rightRow, $this->rightJoinField);
95
96 4
        while ($leftId == $rightId && $this->rightReader->valid()) {
97
98 3
            $leftRow[$this->nestKey][] = $rightRow;
99 3
            $this->rightReader->next();
100
101 3
            $rightRow = $this->rightReader->current();
102
103 3
            if($this->rightReader->valid()) {
104 3
                $rightId = $this->getRowId($rightRow, $this->rightJoinField);
105 3
            }
106 3
        }
107
108 4
        return $leftRow;
109
    }
110
111
    /**
112
     * @param array  $row
113
     * @param string $idField
114
     *
115
     * @return mixed
116
     *
117
     * @throws ReaderException
118
     */
119 6
    protected function getRowId(array $row, $idField)
120
    {
121 6
        if (!array_key_exists($idField, $row)) {
122 2
            throw new ReaderException(
123 2
                sprintf(
124 2
                    'Row: "%s" has no field named "%s"',
125 2
                    $this->key(),
126
                    $idField
127 2
                )
128 2
            );
129
        }
130
131 5
        return $row[$idField];
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 4
    public function next()
138
    {
139 4
        $this->leftReader->next();
140
        //right reader is iterated in current() method.
141 4
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 3
    public function key()
147
    {
148 3
        return $this->leftReader->key();
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 4
    public function valid()
155
    {
156 4
        return $this->leftReader->valid() && $this->rightReader->valid();
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 7
    public function rewind()
163
    {
164 7
        $this->leftReader->rewind();
165 7
        $this->rightReader->rewind();
166 7
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 1
    public function getFields()
172
    {
173 1
        return array_merge($this->leftReader->getFields(), [$this->nestKey]);
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 1
    public function count()
180
    {
181 1
        return $this->leftReader->count();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Ddeboer\DataImport\Reader as the method count() does only exist in the following implementations of said interface: Ddeboer\DataImport\Reader\ArrayReader, Ddeboer\DataImport\Reader\CountableIteratorReader, Ddeboer\DataImport\Reader\CsvReader, Ddeboer\DataImport\Reader\DbalReader, Ddeboer\DataImport\Reader\DoctrineReader, Ddeboer\DataImport\Reader\ExcelReader, Ddeboer\DataImport\Reader\OneToManyReader, Ddeboer\DataImport\Reader\PdoReader.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
182
    }
183
}
184