CompilesMySqlAdjacencyLists   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
eloc 43
c 1
b 0
f 0
dl 0
loc 188
ccs 48
cts 48
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getRecursivePathBindings() 0 3 1
A compileOrderByPath() 0 22 2
A compileInitialPath() 0 3 1
A selectPathList() 0 5 1
A compileRecursivePath() 0 6 2
A supportsUnionInRecursiveExpression() 0 3 1
A compilePivotColumnNullValue() 0 19 2
A compileCycleDetectionInitialSelect() 0 3 1
A compileCycleDetectionRecursiveSelect() 0 3 1
A compileCycleDetectionStopConstraint() 0 3 1
A compileCycleDetection() 0 6 1
A getCycleDetectionBindings() 0 3 1
1
<?php
2
3
namespace Staudenmeir\LaravelAdjacencyList\Query\Grammars\Traits;
4
5
use Illuminate\Database\Query\Builder;
6
use Staudenmeir\LaravelAdjacencyList\Query\Grammars\OrdersByPath;
7
8
trait CompilesMySqlAdjacencyLists
9
{
10
    use OrdersByPath;
11
12
    /**
13
     * Compile an initial path.
14
     *
15
     * @param string $column
16
     * @param string $alias
17
     * @return string
18
     */
19 322
    public function compileInitialPath($column, $alias)
20
    {
21 322
        return 'cast(' . $this->wrap($column) . ' as char(65535)) as ' . $this->wrap($alias);
0 ignored issues
show
Bug introduced by
It seems like wrap() 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

21
        return 'cast(' . $this->/** @scrutinizer ignore-call */ wrap($column) . ' as char(65535)) as ' . $this->wrap($alias);
Loading history...
22
    }
23
24
    /**
25
     * Compile a recursive path.
26
     *
27
     * @param string $column
28
     * @param string $alias
29
     * @param bool $reverse
30
     * @return string
31
     */
32 386
    public function compileRecursivePath($column, $alias, bool $reverse = false)
33
    {
34 386
        $wrappedColumn = $this->wrap($column);
35 386
        $wrappedAlias = $this->wrap($alias);
36
37 386
        return $reverse ? "concat($wrappedColumn, ?, $wrappedAlias)" : "concat($wrappedAlias, ?, $wrappedColumn)";
38
    }
39
40
    /**
41
     * Get the recursive path bindings.
42
     *
43
     * @param string $separator
44
     * @return array
45
     */
46 386
    public function getRecursivePathBindings($separator)
47
    {
48 386
        return [$separator];
49
    }
50
51
    /**
52
     * Select a concatenated list of paths.
53
     *
54
     * @param \Illuminate\Database\Query\Builder $query
55
     * @param string $expression
56
     * @param string $column
57
     * @param string $pathSeparator
58
     * @param string $listSeparator
59
     * @return \Illuminate\Database\Query\Builder
60
     */
61 34
    public function selectPathList(Builder $query, $expression, $column, $pathSeparator, $listSeparator)
0 ignored issues
show
Unused Code introduced by
The parameter $pathSeparator 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

61
    public function selectPathList(Builder $query, $expression, $column, /** @scrutinizer ignore-unused */ $pathSeparator, $listSeparator)

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...
62
    {
63 34
        return $query->selectRaw(
64 34
            'group_concat(' . $this->wrap($column) . " separator '$listSeparator')"
65 34
        )->from($expression);
0 ignored issues
show
Bug introduced by
$expression of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $table of Illuminate\Database\Query\Builder::from(). ( Ignorable by Annotation )

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

65
        )->from(/** @scrutinizer ignore-type */ $expression);
Loading history...
66
    }
67
68
    /**
69
     * Compile an "order by path" clause.
70
     *
71
     * @return string
72
     */
73 8
    public function compileOrderByPath()
74
    {
75 8
        $column = $this->model->getLocalKeyName();
76
77 8
        $path = $this->wrap(
78 8
            $this->model->getPathName()
79 8
        );
80
81 8
        $pathSeparator = $this->model->getPathSeparator();
82
83 8
        if (!$this->model->isIntegerAttribute($column)) {
84 2
            return "$path asc";
85
        }
86
87 6
        return <<<SQL
88 6
regexp_replace(
89
    regexp_replace(
90 6
        $path,
91 6
        '(^|[$pathSeparator])(\\\\d+)',
92
        '$100000000000000000000$2'
93
    ),
94 6
    '0+(\\\\d\{20\})([$pathSeparator]|$)',
95
    '$1$2'
96
) asc
97 6
SQL;
98
    }
99
100
    /**
101
     * Compile a pivot column null value.
102
     *
103
     * @param string $typeName
104
     * @param string $type
105
     * @return string
106
     */
107
    public function compilePivotColumnNullValue(string $typeName, string $type): string
108 44
    {
109
        if ($typeName === 'decimal') {
110 44
            preg_match('/\((\d+),(\d+)\)/', $type, $matches);
111 44
112 44
            [$precision, $scale] = [$matches[1], $matches[2]];
113 44
        } else {
114 44
            [$precision, $scale] = [null, null];
115 44
        }
116
117 44
        $cast = match ($typeName) {
118
            'bigint', 'boolean', 'integer', 'smallint', 'tinyint' => 'signed',
119
            'decimal' => "decimal($precision, $scale)",
120
            'timestamp' => 'datetime',
121
            'varchar' => 'char(65535)',
122
            default => $type,
123
        };
124
125
        return "cast(null as $cast)";
126
    }
127 52
128
    /**
129 52
     * Compile a cycle detection clause.
130 52
     *
131
     * @param string $localKey
132 52
     * @param string $path
133
     * @return string
134
     */
135
    public function compileCycleDetection(string $localKey, string $path): string
136
    {
137
        $localKey = $this->wrap($localKey);
138
        $path = $this->wrap($path);
139
140
        return "instr($path, concat($localKey, ?)) = 1 || instr($path, concat(?, $localKey, ?)) > 1";
141 52
    }
142
143 52
    /**
144
     * Get the cycle detection bindings.
145
     *
146
     * @param string $pathSeparator
147
     * @return array
148
     */
149
    public function getCycleDetectionBindings(string $pathSeparator): array
150
    {
151
        return [$pathSeparator, $pathSeparator, $pathSeparator];
152 26
    }
153
154 26
    /**
155
     * Compile the initial select expression for a cycle detection clause.
156
     *
157
     * @param string $column
158
     * @return string
159
     */
160
    public function compileCycleDetectionInitialSelect(string $column): string
161
    {
162
        return 'false as ' . $this->wrap($column);
163
    }
164 26
165
    /**
166 26
     * Compile the recursive select expression for a cycle detection clause.
167
     *
168
     * @param string $sql
169
     * @param string $column
170
     * @return string
171
     */
172
    public function compileCycleDetectionRecursiveSelect(string $sql, string $column): string
0 ignored issues
show
Unused Code introduced by
The parameter $column 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

172
    public function compileCycleDetectionRecursiveSelect(string $sql, /** @scrutinizer ignore-unused */ string $column): string

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...
173
    {
174
        return $sql;
175 26
    }
176
177 26
    /**
178
     * Compile the stop constraint for a cycle detection clause.
179
     *
180
     * @param string $column
181
     * @return string
182
     */
183
    public function compileCycleDetectionStopConstraint(string $column): string
184
    {
185 40
        return 'not ' . $this->wrap($column);
186
    }
187 40
188
    /**
189
     * Determine whether the database supports the UNION operator in a recursive expression.
190
     *
191
     * @return bool
192
     */
193
    public function supportsUnionInRecursiveExpression(): bool
194
    {
195
        return true;
196
    }
197
}
198