Passed
Push — master ( dde86b...2fbf09 )
by Anton
02:00
created

ManyToManyLoader::__clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Cycle DataMapper ORM
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
declare(strict_types=1);
9
10
namespace Cycle\ORM\Select\Loader;
11
12
use Cycle\ORM\ORMInterface;
13
use Cycle\ORM\Parser\AbstractNode;
14
use Cycle\ORM\Parser\SingularNode;
15
use Cycle\ORM\Parser\Typecast;
16
use Cycle\ORM\Relation;
17
use Cycle\ORM\Schema;
18
use Cycle\ORM\Select\JoinableLoader;
19
use Cycle\ORM\Select\LoaderInterface;
20
use Cycle\ORM\Select\Traits\WhereTrait;
21
use Spiral\Database\Injection\Parameter;
22
use Spiral\Database\Query\SelectQuery;
23
24
class ManyToManyLoader extends JoinableLoader
25
{
26
    use WhereTrait;
27
28
    /**
29
     * Default set of relation options. Child implementation might defined their of default options.
30
     *
31
     * @var array
32
     */
33
    protected $options = [
34
        'load'      => false,
35
        'constrain' => true,
36
        'method'    => self::POSTLOAD,
37
        'minify'    => true,
38
        'as'        => null,
39
        'using'     => null,
40
        'where'     => null,
41
    ];
42
43
    /** @var PivotLoader */
44
    protected $pivot;
45
46
    /**
47
     * {@inheritdoc}
48
     */
49
    public function __construct(ORMInterface $orm, string $name, string $target, array $schema)
50
    {
51
        parent::__construct($orm, $name, $target, $schema);
52
        $this->pivot = new PivotLoader($orm, 'pivot', $schema[Relation::THOUGH_ENTITY], $schema);
53
    }
54
55
    /**
56
     * Make sure that pivot loader is always carried with parent relation.
57
     */
58
    public function __clone()
59
    {
60
        parent::__clone();
61
        $this->pivot = clone $this->pivot;
62
    }
63
64
    /**
65
     * @param LoaderInterface $parent
66
     * @param array           $options
67
     * @return LoaderInterface
68
     */
69
    public function withContext(LoaderInterface $parent, array $options = []): LoaderInterface
70
    {
71
        /** @var ManyToManyLoader $loader */
72
        $loader = parent::withContext($parent, $options);
73
        $loader->pivot = $loader->pivot->withContext(
74
            $loader,
75
            [
76
                'load'   => $loader->isLoaded(),
77
                'method' => $options['method'] ?? self::JOIN,
78
            ] + ($options['pivot'] ?? [])
79
        );
80
81
        return $loader;
82
    }
83
84
    /**
85
     * @param string $relation
86
     * @param array  $options
87
     * @param bool   $join
88
     * @param bool   $load
89
     * @return LoaderInterface
90
     */
91
    public function loadRelation(
92
        string $relation,
93
        array $options,
94
        bool $join = false,
95
        bool $load = false
96
    ): LoaderInterface {
97
        if ($relation == '@') {
98
            unset($options['method']);
99
100
            if (!empty($options)) {
101
                $this->pivot = $this->pivot->withContext($this, $options);
102
            }
103
104
            return $this->pivot;
105
        }
106
107
        return parent::loadRelation($relation, $options, $join, $load);
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function configureQuery(SelectQuery $query, array $outerKeys = []): SelectQuery
114
    {
115
        if (!empty($this->options['using'])) {
116
            // use pre-defined query
117
            return parent::configureQuery($this->pivot->configureQuery($query), $outerKeys);
118
        }
119
120
        // Manually join pivoted table
121
        if ($this->isJoined()) {
122
            $query->join(
123
                $this->getJoinMethod(),
124
                $this->pivot->getJoinTable()
125
            )->on(
126
                $this->pivot->localKey(Relation::THOUGH_INNER_KEY),
127
                $this->parentKey(Relation::INNER_KEY)
128
            );
129
130
            $query->innerJoin(
131
                $this->getJoinTable()
132
            )->on(
133
                $this->localKey(Relation::OUTER_KEY),
134
                $this->pivot->localKey(Relation::THOUGH_OUTER_KEY)
135
            );
136
        } else {
137
            // reset all the columns when query is isolated (we have to do it manually
138
            // since underlying loader believes it's loaded)
139
            $query->columns([]);
140
141
            $query->innerJoin(
142
                $this->pivot->getJoinTable()
143
            )->on(
144
                $this->pivot->localKey(Relation::THOUGH_OUTER_KEY),
145
                $this->localKey(Relation::OUTER_KEY)
146
            )->where(
147
                $this->pivot->localKey(Relation::THOUGH_INNER_KEY),
148
                new Parameter($outerKeys)
149
            );
150
        }
151
152
        // user specified WHERE conditions
153
        $this->setWhere(
154
            $query,
155
            $this->getAlias(),
156
            $this->isJoined() ? 'onWhere' : 'where',
157
            $this->options['where'] ?? $this->schema[Relation::WHERE] ?? []
158
        );
159
160
        return parent::configureQuery($this->pivot->configureQuery($query));
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function createNode(): AbstractNode
167
    {
168
        $node = $this->pivot->createNode();
169
        $node->joinNode('@', parent::createNode());
170
171
        return $node;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    protected function mountColumns(
178
        SelectQuery $query,
179
        bool $minify = false,
180
        string $prefix = '',
181
        bool $overwrite = false
0 ignored issues
show
Unused Code introduced by
The parameter $overwrite is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

181
        /** @scrutinizer ignore-unused */ bool $overwrite = false

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

Loading history...
182
    ): SelectQuery {
183
        // columns are reset on earlier stage to allow pivot loader mount it's own aliases
184
        return parent::mountColumns($query, $minify, $prefix, false);
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    protected function initNode(): AbstractNode
191
    {
192
        $node = new SingularNode(
193
            $this->columnNames(),
194
            $this->define(Schema::PRIMARY_KEY),
195
            $this->schema[Relation::OUTER_KEY],
196
            $this->schema[Relation::THOUGH_OUTER_KEY]
197
        );
198
199
        $typecast = $this->define(Schema::TYPECAST);
200
        if ($typecast !== null) {
201
            $node->setTypecast(new Typecast($typecast, $this->getSource()->getDatabase()));
202
        }
203
204
        return $node;
205
    }
206
}