BuildsUpdates::upsert()   B
last analyzed

Complexity

Conditions 9
Paths 50

Size

Total Lines 47
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 9.0046

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 26
c 1
b 0
f 0
nc 50
nop 3
dl 0
loc 47
ccs 25
cts 26
cp 0.9615
crap 9.0046
rs 8.0555
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Query\Concerns;
6
7
use Closure;
8
use Illuminate\Database\Query\Expression;
9
use Illuminate\Support\Arr;
10
use InvalidArgumentException;
11
use LaravelFreelancerNL\Aranguent\Query\Grammar;
12
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException;
13
14
/**
15
 * @method applyBeforeQueryCallbacks()
16
 */
17
trait BuildsUpdates
18
{
19
    /**
20
     * @param array<mixed> $values
21
     * @return array<mixed>
22
     */
23 26
    protected function prepareValuesForUpdate(array $values)
24
    {
25 26
        foreach ($values as $key => $value) {
26 26
            if ($value instanceof Expression) {
27 5
                $values[$key] = $value->getValue($this->grammar);
28
29 5
                continue;
30
            }
31
32 22
            if (is_array($value)) {
33 2
                $values[$key] = $this->prepareValuesForUpdate($value);
34 2
                continue;
35
            }
36
37 22
            $values[$key]  = $this->bindValue($value, 'update');
0 ignored issues
show
Bug introduced by
It seems like bindValue() 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

37
            /** @scrutinizer ignore-call */ 
38
            $values[$key]  = $this->bindValue($value, 'update');
Loading history...
38
        }
39
40 26
        return $values;
41
    }
42
43
    /**
44
     * Update records in the database.
45
     *
46
     * @param  array<mixed>  $values
47
     * @return int
48
     */
49 26
    public function update(array $values)
50
    {
51
        assert($this->grammar instanceof Grammar);
52
53 26
        $this->applyBeforeQueryCallbacks();
54
55 26
        $values = Arr::undot($this->grammar->convertJsonFields($values));
56
57 26
        $values = $this->prepareValuesForUpdate($values);
58
59 26
        $aql = $this->grammar->compileUpdate($this, $values);
0 ignored issues
show
Bug introduced by
$this of type LaravelFreelancerNL\Aran...\Concerns\BuildsUpdates is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of LaravelFreelancerNL\Aran...rammar::compileUpdate(). ( Ignorable by Annotation )

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

59
        $aql = $this->grammar->compileUpdate(/** @scrutinizer ignore-type */ $this, $values);
Loading history...
60
61 26
        return $this->connection->update($aql, $this->getBindings());
0 ignored issues
show
Bug introduced by
It seems like getBindings() 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

61
        return $this->connection->update($aql, $this->/** @scrutinizer ignore-call */ getBindings());
Loading history...
62
    }
63
64
    /**
65
     * Insert or update a record matching the attributes, and fill it with values.
66
     *
67
     * @param array<mixed> $attributes
68
     * @param array<mixed>|callable $values
69
     * @return bool
70
     * @throws BindException
71
     */
72 4
    public function updateOrInsert(array $attributes, array|callable $values = [])
73
    {
74 4
        $exists = $this->where($attributes)->exists();
0 ignored issues
show
Bug introduced by
It seems like where() 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

74
        $exists = $this->/** @scrutinizer ignore-call */ where($attributes)->exists();
Loading history...
75
76 4
        if ($values instanceof Closure) {
0 ignored issues
show
introduced by
$values is never a sub-type of Closure.
Loading history...
77 1
            $values = $values($exists);
78
        }
79
80 4
        if (! $exists) {
81 2
            $this->bindings['where'] = [];
0 ignored issues
show
Bug Best Practice introduced by
The property bindings does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
82 2
            return $this->insert(array_merge($attributes, $values));
0 ignored issues
show
Bug introduced by
It seems like insert() 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

82
            return $this->/** @scrutinizer ignore-call */ insert(array_merge($attributes, $values));
Loading history...
83
        }
84
85 2
        if (empty($values)) {
86 1
            return true;
87
        }
88
89 1
        return (bool) $this->limit(1)->update($values);
0 ignored issues
show
Bug introduced by
It seems like limit() 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

89
        return (bool) $this->/** @scrutinizer ignore-call */ limit(1)->update($values);
Loading history...
90
    }
91
92
    /**
93
     * Increment the given column's values by the given amounts.
94
     *
95
     * @param  array<string, float|int|numeric-string>  $columns
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, float|int|numeric-string> at position 8 could not be parsed: Unknown type name 'numeric-string' at position 8 in array<string, float|int|numeric-string>.
Loading history...
96
     * @param  array<string, mixed>  $extra
97
     * @return int
98
     *
99
     * @throws \InvalidArgumentException
100
     */
101 3
    public function incrementEach(array $columns, array $extra = [])
102
    {
103 3
        foreach ($columns as $column => $amount) {
104 3
            if (!is_numeric($amount)) {
105
                throw new InvalidArgumentException("Non-numeric value passed as increment amount for column: '$column'.");
106 3
            } elseif (!is_string($column)) {
107
                throw new InvalidArgumentException('Non-associative array passed to incrementEach method.');
108
            }
109
110 3
            $columns[$column] = new Expression($this->getTableAlias($this->from) . '.' . $column . ' + ' . $amount);
0 ignored issues
show
Bug introduced by
It seems like getTableAlias() 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

110
            $columns[$column] = new Expression($this->/** @scrutinizer ignore-call */ getTableAlias($this->from) . '.' . $column . ' + ' . $amount);
Loading history...
Bug introduced by
$this->getTableAlias($th...olumn . ' + ' . $amount of type string is incompatible with the type Illuminate\Database\Query\TValue expected by parameter $value of Illuminate\Database\Quer...pression::__construct(). ( Ignorable by Annotation )

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

110
            $columns[$column] = new Expression(/** @scrutinizer ignore-type */ $this->getTableAlias($this->from) . '.' . $column . ' + ' . $amount);
Loading history...
111
        }
112
113 3
        return $this->update(array_merge($columns, $extra));
114
    }
115
116
    /**
117
     * Decrement the given column's values by the given amounts.
118
     *
119
     * @param  array<string, float|int|numeric-string>  $columns
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, float|int|numeric-string> at position 8 could not be parsed: Unknown type name 'numeric-string' at position 8 in array<string, float|int|numeric-string>.
Loading history...
120
     * @param  array<string, mixed>  $extra
121
     * @return int
122
     *
123
     * @throws \InvalidArgumentException
124
     */
125 2
    public function decrementEach(array $columns, array $extra = [])
126
    {
127 2
        foreach ($columns as $column => $amount) {
128 2
            if (!is_numeric($amount)) {
129
                throw new InvalidArgumentException("Non-numeric value passed as decrement amount for column: '$column'.");
130 2
            } elseif (!is_string($column)) {
131
                throw new InvalidArgumentException('Non-associative array passed to decrementEach method.');
132
            }
133
134 2
            $columns[$column] = new Expression($this->getTableAlias($this->from) . '.' . $column . ' - ' . $amount);
0 ignored issues
show
Bug introduced by
$this->getTableAlias($th...olumn . ' - ' . $amount of type string is incompatible with the type Illuminate\Database\Query\TValue expected by parameter $value of Illuminate\Database\Quer...pression::__construct(). ( Ignorable by Annotation )

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

134
            $columns[$column] = new Expression(/** @scrutinizer ignore-type */ $this->getTableAlias($this->from) . '.' . $column . ' - ' . $amount);
Loading history...
135
        }
136
137 2
        return $this->update(array_merge($columns, $extra));
138
    }
139
140
    /**
141
     * Insert new records or update the existing ones.
142
     *
143
     * @param array<mixed> $values
144
     * @param array<mixed>|string $uniqueBy
145
     * @param array<mixed>|null $update
146
     * @return int
147
     * @throws BindException
148
     */
149 7
    public function upsert(array $values, $uniqueBy, $update = null)
150
    {
151
        assert($this->grammar instanceof Grammar);
152
153 7
        if (empty($values)) {
154 1
            return 0;
155 6
        } elseif ($update === []) {
156
            return (int) $this->insert($values);
157
        }
158
159 6
        if (!is_array(reset($values))) {
160 2
            $values = [$values];
161
        }
162
163 6
        foreach ($values as $key => $value) {
164 6
            $values[$key] = $this->grammar->convertJsonFields($value);
165 6
            $values[$key] = $this->convertIdToKey($values[$key]);
0 ignored issues
show
Bug introduced by
It seems like convertIdToKey() 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

165
            /** @scrutinizer ignore-call */ 
166
            $values[$key] = $this->convertIdToKey($values[$key]);
Loading history...
166 6
            $values[$key] = Arr::undot($values[$key]);
167
        }
168
169 6
        foreach ($values as $key => $value) {
170 6
            foreach ($value as $dataKey => $data) {
171 6
                $values[$key][$dataKey] = $this->bindValue($data, 'upsert');
172
            }
173
        }
174
175 6
        $uniqueBy = $this->grammar->convertJsonFields($uniqueBy);
176
177 6
        if (is_null($update)) {
178 1
            $update = array_keys(reset($values));
179
        }
180
181 6
        foreach ($update as $key => $value) {
182 6
            $update[$key] = $this->convertIdToKey($value);
183
        }
184
185 6
        $update = $this->grammar->convertJsonFields($update);
186
187 6
        $this->applyBeforeQueryCallbacks();
188
189 6
        $bindings = $this->bindings['upsert'];
190
191 6
        $aql = $this->grammar->compileUpsert($this, $values, (array) $uniqueBy, $update);
0 ignored issues
show
Bug introduced by
$this of type LaravelFreelancerNL\Aran...\Concerns\BuildsUpdates is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of LaravelFreelancerNL\Aran...rammar::compileUpsert(). ( Ignorable by Annotation )

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

191
        $aql = $this->grammar->compileUpsert(/** @scrutinizer ignore-type */ $this, $values, (array) $uniqueBy, $update);
Loading history...
192
193 6
        return $this->connection->affectingStatement(
194 6
            $aql,
195 6
            $bindings,
196 6
        );
197
    }
198
}
199