Completed
Branch feature/pre-split (c871cb)
by Anton
03:37
created

ManyToManyRelation::partial()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ORM\Entities\Relations;
8
9
use Spiral\Database\Exceptions\QueryException;
10
use Spiral\ORM\CommandInterface;
11
use Spiral\ORM\Commands\NullCommand;
12
use Spiral\ORM\ContextualCommandInterface;
13
use Spiral\ORM\Entities\RecordIterator;
14
use Spiral\ORM\Entities\Relations\Traits\MatchTrait;
15
use Spiral\ORM\Exceptions\RelationException;
16
use Spiral\ORM\Exceptions\SelectorException;
17
use Spiral\ORM\RecordInterface;
18
use Spiral\ORM\RelationInterface;
19
20
class ManyToManyRelation extends AbstractRelation implements \IteratorAggregate
21
{
22
    use MatchTrait;
23
24
    /**
25
     * @var bool
26
     */
27
    private $autoload = true;
28
29
    /**
30
     * @var \SplObjectStorage
31
     */
32
    private $pivotData;
33
34
    /**
35
     * @var RecordInterface[]
36
     */
37
    private $linked = [];
38
39
    /**
40
     * @var RecordInterface[]
41
     */
42
    private $unlinked = [];
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function withContext(
48
        RecordInterface $parent,
49
        bool $loaded = false,
50
        array $data = null
51
    ): RelationInterface {
52
        /**
53
         * @var self $relation
54
         */
55
        $relation = parent::withContext($parent, $loaded, $data);
56
        $relation->pivotData = new \SplObjectStorage();
57
58
        return $relation;
59
    }
60
61
    /**
62
     * Partial selections will not be autoloaded.
63
     *
64
     * Example:
65
     *
66
     * $post = $this->findPost(); //no comments
67
     * $post->tags->partial(true);
68
     * assert($post->tags->count() == 0); //never loaded
69
     *
70
     * $post->comments->add($comment);
71
     *
72
     * @param bool $partial
73
     *
74
     * @return ManyToManyRelation
75
     */
76
    public function partial(bool $partial = true): self
77
    {
78
        $this->autoload = !$partial;
79
80
        return $this;
81
    }
82
83
    /**
84
     * @return bool
85
     */
86
    public function isPartial(): bool
87
    {
88
        return !$this->autoload;
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function setRelated($value)
95
    {
96
        if (is_null($value)) {
97
            $value = [];
98
        }
99
100
        if (!is_array($value)) {
101
            throw new RelationException("HasMany relation can only be set with array of entities");
102
        }
103
104
        //Sync values without forcing it (no autoloading), i.e. clear CURRENT associations
105
        $this->sync($value, [], false);
106
    }
107
108
    /**
109
     * @return $this
110
     */
111
    public function getRelated()
112
    {
113
        return $this;
114
    }
115
116
    /**
117
     * Iterate over linked instances, will force pre-loading unless partial.
118
     *
119
     * @return \ArrayIterator
120
     */
121
    public function getIterator()
122
    {
123
        return new \ArrayIterator($this->loadData(true)->linked);
124
    }
125
126
    /**
127
     * Get all unlinked records.
128
     *
129
     * @return \ArrayIterator
130
     */
131
    public function getUnlinked()
132
    {
133
        return new \ArrayIterator($this->unlinked);
134
    }
135
136
    /**
137
     * todo: write docs
138
     *
139
     * @param array $records
140
     * @param array $pivotData
141
     * @param bool  $force
142
     */
143
    public function sync(array $records, array $pivotData = [], bool $force = true)
0 ignored issues
show
Unused Code introduced by
The parameter $records is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $pivotData is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
144
    {
145
        if ($force) {
146
            //Load existed data
147
            $this->loadData(true);
148
        }
149
    }
150
151
    public function setPivot($record, array $pivotData)
152
    {
153
        $this->pivotData->offsetSet($record, $pivotData);
154
    }
155
156
    public function getPivot($record)
157
    {
158
        return $this->pivotData->offsetGet($record);
159
    }
160
161
    public function link($record, array $pivotData = [])
162
    {
163
        //Linkage!
164
        //$this->linked[] = $record;
165
166
        $this->pivotData->offsetSet($record, $pivotData);
167
    }
168
169
    public function unlink($record)
0 ignored issues
show
Unused Code introduced by
The parameter $record is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
170
    {
171
    }
172
173
    public function has($query)
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
174
    {
175
176
    }
177
178
    public function matchOne($query)
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
179
    {
180
    }
181
182
    public function matchMultiple($query)
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
183
    {
184
185
    }
186
187
    public function queueCommands(ContextualCommandInterface $command): CommandInterface
188
    {
189
        return new NullCommand();
190
    }
191
192
    /**
193
     * Load related records from database.
194
     *
195
     * @param bool $autoload
196
     *
197
     * @return self
198
     *
199
     * @throws SelectorException
200
     * @throws QueryException (needs wrapping)
201
     */
202
    protected function loadData(bool $autoload = true): self
203
    {
204
        if ($this->loaded) {
205
            return $this;
206
        }
207
208
        $this->loaded = true;
209
210
        if (empty($this->data) || !is_array($this->data)) {
211
            if ($this->autoload && $autoload) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
212
                //Only for non partial selections
213
                //   $this->data = $this->loadRelated();
214
            } else {
215
                $this->data = [];
216
            }
217
        }
218
219
        return $this->initInstances();
220
    }
221
222
    /**
223
     * Init relations and populate pivot map.
224
     *
225
     * @return ManyToManyRelation
226
     */
227
    private function initInstances(): self
228
    {
229
        if (is_array($this->data) && !empty($this->data)) {
230
            //Iterates and instantiate records
231
            $iterator = new RecordIterator($this->data, $this->class, $this->orm);
232
233
            foreach ($iterator as $pivotData => $item) {
234
                if ($this->has($item)) {
235
                    //Skip duplicates
236
                    continue;
237
                }
238
239
                $this->pivotData->attach($item, $pivotData);
240
                $this->linked[] = $item;
241
            }
242
        }
243
244
        //Memory free
245
        $this->data = null;
246
247
        return $this;
248
    }
249
}