Completed
Pull Request — master (#61)
by Anton
04:22
created

RecordIterator::key()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\ORM\Entities;
9
10
use Spiral\ORM\Exceptions\IteratorException;
11
use Spiral\ORM\Exceptions\ORMException;
12
use Spiral\ORM\ORM;
13
use Spiral\ORM\RecordEntity;
14
use Spiral\ORM\RecordInterface;
15
16
/**
17
 * Provides iteration over set of specified records data using internal instances cache. In
18
 * addition, allows to decorate set of callbacks with association by their name (@see __call()).
19
 * Keeps record context.
20
 */
21
class RecordIterator implements \Iterator, \Countable, \JsonSerializable
22
{
23
    /**
24
     * Current iterator position.
25
     *
26
     * @var int
27
     */
28
    private $position = 0;
29
30
    /**
31
     * Set of "methods" to be decorated.
32
     *
33
     * @var callable[]
34
     */
35
    private $callbacks = [];
36
37
    /**
38
     * @var string
39
     */
40
    protected $class = '';
41
42
    /**
43
     * Indication that entity cache must be used.
44
     *
45
     * @var bool
46
     */
47
    protected $cache = true;
48
49
    /**
50
     * Data to be iterated.
51
     *
52
     * @var array
53
     */
54
    protected $data = [];
55
56
    /**
57
     * Constructed record instances. Cache.
58
     *
59
     * @var RecordEntity[]
60
     */
61
    protected $instances = [];
62
63
    /**
64
     * @invisible
65
     * @var ORM
66
     */
67
    protected $orm = null;
68
69
    /**
70
     * @param ORM        $orm
71
     * @param string     $class
72
     * @param array      $data
73
     * @param bool       $cache
74
     * @param callable[] $callbacks
75
     */
76
    public function __construct(ORM $orm, $class, array $data, $cache = true, array $callbacks = [])
77
    {
78
        $this->class = $class;
79
        $this->cache = $cache;
80
81
        $this->orm = $orm;
82
        $this->data = $data;
83
84
        //Magic functionality provided by outer parent
85
        $this->callbacks = $callbacks;
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function count()
92
    {
93
        return count($this->data);
94
    }
95
96
    /**
97
     * Get all Records as array.
98
     *
99
     * @return RecordInterface[]
100
     */
101
    public function all()
102
    {
103
        $result = [];
104
105
        /**
106
         * @var self|RecordInterface[] $iterator
107
         */
108
        $iterator = clone $this;
109
        foreach ($iterator as $nested) {
110
            $result[] = $nested;
111
        }
112
113
        //Copying instances just in case
114
        $this->instances = $iterator->instances;
115
116
        return $result;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     *
122
     * @return RecordInterface
123
     * @see ORM::record()
124
     * @see Record::setContext()
125
     * @throws ORMException
126
     */
127
    public function current()
128
    {
129
        if (isset($this->instances[$this->position])) {
130
            //Due record was pre-constructed we must update it's context to force values for relations
131
            //and pivot fields
132
            return $this->instances[$this->position];
133
        }
134
135
        //Let's ask ORM to create needed record
136
        return $this->instances[$this->position] = $this->orm->record(
137
            $this->class,
138
            $this->data[$this->position],
139
            $this->cache
140
        );
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function next()
147
    {
148
        $this->position++;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function key()
155
    {
156
        return $this->position;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162
    public function valid()
163
    {
164
        return isset($this->data[$this->position]);
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function rewind()
171
    {
172
        $this->position = 0;
173
    }
174
175
    /**
176
     * Check if record or record with specified id presents in iteration.
177
     *
178
     * @param RecordEntity|string|int $record
179
     * @return true
180
     */
181
    public function has($record)
182
    {
183
        /**
184
         * @var self|RecordEntity[] $iterator
185
         */
186
        $iterator = clone $this;
187
        foreach ($iterator as $nested) {
188
            $found = false;
189
            if (is_array($record)) {
190
                if (array_intersect_assoc($nested->getFields(), $record) == $record) {
191
                    //Comparing fields intersection
192
                    $found = true;
193
                }
194
            } elseif (!$record instanceof RecordEntity) {
195
196
                if (!empty($record) && $nested->primaryKey() == $record) {
197
                    //Comparing using primary keys
198
                    $found = true;
199
                }
200
            } elseif ($nested == $record || $nested->getFields() == $record->getFields()) {
201
                //Comparing as class
202
                $found = true;
203
            }
204
205
            if ($found) {
206
                //They all must be iterated already
207
                $this->instances = $iterator->instances;
208
209
                return true;
210
            }
211
        }
212
213
        //They all must be iterated already
214
        $this->instances = $iterator->instances;
215
216
        return false;
217
    }
218
219
    /**
220
     * Executes decorated method providing itself as function argument.
221
     *
222
     * @param string $method
223
     * @param array  $arguments
224
     * @return mixed
225
     * @throws IteratorException
226
     */
227
    public function __call($method, array $arguments)
228
    {
229
        if (!isset($this->callbacks[$method])) {
230
            throw new IteratorException("Undefined method or callback.");
231
        }
232
233
        return call_user_func($this->callbacks[$method], $this);
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function jsonSerialize()
240
    {
241
        return $this->all();
242
    }
243
244
    /**
245
     * @return RecordEntity[]
246
     */
247
    public function __debugInfo()
248
    {
249
        return $this->all();
250
    }
251
252
    /**
253
     * Flushing references.
254
     */
255
    public function __destruct()
256
    {
257
        $this->data = [];
258
        $this->instances = [];
259
        $this->orm = null;
260
    }
261
}