Passed
Pull Request — master (#362)
by Arman
03:27
created

Join::applyJoinTo()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
c 1
b 0
f 0
nc 4
nop 4
dl 0
loc 16
rs 9.9
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.9
13
 */
14
15
namespace Quantum\Libraries\Database\Adapters\Sleekdb\Statements;
16
17
use Quantum\Libraries\Database\Adapters\Sleekdb\SleekDbal;
18
use Quantum\Libraries\Database\Contracts\DbalInterface;
19
use SleekDB\Exceptions\InvalidArgumentException;
20
use Quantum\Libraries\Database\Enums\Relation;
21
use Quantum\Model\Exceptions\ModelException;
22
use Quantum\Model\QtModel;
23
use SleekDB\QueryBuilder;
24
25
/**
26
 * Trait Join
27
 * @package Quantum\Libraries\Database
28
 */
29
trait Join
30
{
31
32
    /**
33
     * @inheritDoc
34
     */
35
    public function joinTo(QtModel $model, bool $switch = true): DbalInterface
36
    {
37
        $this->joins[] = [
0 ignored issues
show
Bug Best Practice introduced by
The property joins does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
38
            'model' => serialize($model),
39
            'switch' => $switch,
40
        ];
41
42
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Quantum\Libraries\Databa...Sleekdb\Statements\Join which is incompatible with the type-hinted return Quantum\Libraries\Database\Contracts\DbalInterface.
Loading history...
43
    }
44
45
    /**
46
     * Starts to apply joins
47
     * @throws ModelException
48
     */
49
    private function applyJoins()
50
    {
51
        if (!empty($this->joins)) {
52
            $this->applyJoin($this->queryBuilder, $this, $this->joins[0]);
53
        }
54
    }
55
56
    /**
57
     * Apply the join to query builder
58
     * @param QueryBuilder $queryBuilder
59
     * @param SleekDbal $currentItem
60
     * @param array $nextItem
61
     * @param int $level
62
     * @return QueryBuilder
63
     * @throws ModelException
64
     */
65
    private function applyJoin(QueryBuilder $queryBuilder, SleekDbal $currentItem, array $nextItem, int $level = 1): QueryBuilder
66
    {
67
        $modelToJoin = unserialize($nextItem['model']);
68
        $switch = $nextItem['switch'];
69
70
        $queryBuilder->join(function ($item) use ($currentItem, $modelToJoin, $switch, $level) {
71
72
            $sleekModel = new self(
73
                $modelToJoin->table,
0 ignored issues
show
Unused Code introduced by
The call to Quantum\Libraries\Databa...nts\Join::__construct() has too many arguments starting with $modelToJoin->table. ( Ignorable by Annotation )

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

73
            $sleekModel = /** @scrutinizer ignore-call */ new self(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
74
                get_class($modelToJoin),
75
                $modelToJoin->idColumn,
76
                $modelToJoin->relations()
77
            );
78
79
            $newQueryBuilder = $sleekModel->getOrmModel()->createQueryBuilder();
0 ignored issues
show
Bug introduced by
It seems like getOrmModel() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

79
            $newQueryBuilder = $sleekModel->/** @scrutinizer ignore-call */ getOrmModel()->createQueryBuilder();
Loading history...
80
81
            $this->applyJoinTo($newQueryBuilder, $modelToJoin, $currentItem, $item);
82
83
            if ($switch && isset($this->joins[$level])) {
84
                $this->applyJoin($newQueryBuilder, $sleekModel, $this->joins[$level], $level + 1);
85
            }
86
87
            return $newQueryBuilder;
88
89
        }, $modelToJoin->table);
90
91
        if (!$switch && isset($this->joins[$level])) {
92
            $this->applyJoin($queryBuilder, $currentItem, $this->joins[$level], $level + 1);
93
        }
94
95
        return $queryBuilder;
96
    }
97
98
    /**
99
     * Apply join condition for JOINTO type
100
     * @param QueryBuilder $queryBuilder
101
     * @param QtModel $relatedModel
102
     * @param SleekDbal $currentModel
103
     * @param array $currentItem
104
     * @return void
105
     * @throws InvalidArgumentException
106
     * @throws ModelException
107
     */
108
    private function applyJoinTo(QueryBuilder $queryBuilder, QtModel $relatedModel, SleekDbal $currentModel, array $currentItem): void
109
    {
110
        $relation = $this->getValidatedRelation($currentModel, $relatedModel);
111
112
        switch ($relation['type']) {
113
            case Relation::HAS_ONE:
114
            case Relation::HAS_MANY:
115
                $this->applyHasRelation($queryBuilder, $currentItem, $relation);
116
                break;
117
118
            case Relation::BELONGS_TO:
119
                $this->applyBelongsTo($queryBuilder, $currentItem, $relation, $currentModel);
120
                break;
121
122
            default:
123
                throw ModelException::unsupportedRelationType($relation['type']);
124
        }
125
    }
126
127
    /**
128
     * @param QueryBuilder $queryBuilder
129
     * @param array $currentItem
130
     * @param array $relation
131
     * @return void
132
     * @throws InvalidArgumentException
133
     */
134
    private function applyHasRelation(QueryBuilder $queryBuilder, array $currentItem, array $relation): void
135
    {
136
        $queryBuilder->where([
137
            $relation['foreign_key'],
138
            '=',
139
            $currentItem[$relation['local_key']]
140
        ]);
141
    }
142
143
    /**
144
     * @param QueryBuilder $queryBuilder
145
     * @param array $currentItem
146
     * @param array $relation
147
     * @param SleekDbal $currentModel
148
     * @return void
149
     * @throws InvalidArgumentException
150
     * @throws ModelException
151
     */
152
    private function applyBelongsTo(QueryBuilder $queryBuilder, array $currentItem, array $relation, SleekDbal $currentModel): void
153
    {
154
        if (!isset($currentItem[$relation['foreign_key']])) {
155
            throw ModelException::missingForeignKeyValue($currentModel->getModelName(), $relation['foreign_key']);
156
        }
157
158
        $queryBuilder->where([
159
            $relation['local_key'],
160
            '=',
161
            $currentItem[$relation['foreign_key']]
162
        ]);
163
    }
164
165
    /**
166
     * @param SleekDbal $currentModel
167
     * @param QtModel $relatedModel
168
     * @return array
169
     * @throws ModelException
170
     */
171
    private function getValidatedRelation(SleekDbal $currentModel, QtModel $relatedModel): array
172
    {
173
        $relations = $currentModel->getForeignKeys();
174
        $relatedModelName = get_class($relatedModel);
175
176
        if (!isset($relations[$relatedModelName])) {
177
            throw ModelException::wrongRelation($currentModel->getModelName(), $relatedModelName);
178
        }
179
180
        $relation = $relations[$relatedModelName];
181
182
        if (empty($relation['type'])) {
183
            throw ModelException::relationTypeMissing($currentModel->getModelName(), $relatedModelName);
184
        }
185
186
        if (empty($relation['foreign_key']) || empty($relation['local_key'])) {
187
            throw ModelException::missingRelationKeys($currentModel->getModelName(), $relatedModelName);
188
        }
189
190
        return $relation;
191
    }
192
}