Passed
Pull Request — next (#84)
by Bas
15:17 queued 11:12
created

BuildsUpdates::upsert()   B

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 Illuminate\Database\Query\Expression;
8
use Illuminate\Support\Arr;
9
use InvalidArgumentException;
10
use LaravelFreelancerNL\Aranguent\Query\Grammar;
11
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException;
12
13
/**
14
 * @method applyBeforeQueryCallbacks()
15
 */
16
trait BuildsUpdates
17
{
18
    /**
19
     * @param array<mixed> $values
20
     * @return array<mixed>
21
     */
22 31
    protected function prepareValuesForUpdate(array $values)
23
    {
24 31
        foreach($values as $key => $value) {
25 31
            if ($value instanceof Expression) {
26 5
                $values[$key] = $value->getValue($this->grammar);
27
28 5
                continue;
29
            }
30
31 27
            if (is_array($value)) {
32 2
                $values[$key] = $this->prepareValuesForUpdate($value);
33 2
                continue;
34
            }
35
36 27
            $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

36
            /** @scrutinizer ignore-call */ 
37
            $values[$key]  = $this->bindValue($value, 'update');
Loading history...
37
        }
38
39 31
        return $values;
40
    }
41
42
    /**
43
     * Update records in the database.
44
     *
45
     * @param  array<mixed>  $values
46
     * @return int
47
     */
48 31
    public function update(array $values)
49
    {
50
        assert($this->grammar instanceof Grammar);
51
52 31
        $this->applyBeforeQueryCallbacks();
53
54 31
        $values = Arr::undot($this->grammar->convertJsonFields($values));
55
56 31
        $values = $this->prepareValuesForUpdate($values);
57
58 31
        $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

58
        $aql = $this->grammar->compileUpdate(/** @scrutinizer ignore-type */ $this, $values);
Loading history...
59
60 31
        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

60
        return $this->connection->update($aql, $this->/** @scrutinizer ignore-call */ getBindings());
Loading history...
61
    }
62
63
    /**
64
     * Insert or update a record matching the attributes, and fill it with values.
65
     *
66
     * @param array<mixed> $attributes
67
     * @param array<mixed> $values
68
     * @return bool
69
     * @throws BindException
70
     */
71 2
    public function updateOrInsert(array $attributes, array $values = [])
72
    {
73 2
        if (!$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

73
        if (!$this->/** @scrutinizer ignore-call */ where($attributes)->exists()) {
Loading history...
74 1
            $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...
75 1
            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

75
            return $this->/** @scrutinizer ignore-call */ insert(array_merge($attributes, $values));
Loading history...
76
        }
77
78 1
        if (empty($values)) {
79
            return true;
80
        }
81
82 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

82
        return (bool) $this->/** @scrutinizer ignore-call */ limit(1)->update($values);
Loading history...
83
    }
84
85
    /**
86
     * Increment the given column's values by the given amounts.
87
     *
88
     * @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...
89
     * @param  array<string, mixed>  $extra
90
     * @return int
91
     *
92
     * @throws \InvalidArgumentException
93
     */
94 3
    public function incrementEach(array $columns, array $extra = [])
95
    {
96 3
        foreach ($columns as $column => $amount) {
97 3
            if (!is_numeric($amount)) {
98
                throw new InvalidArgumentException("Non-numeric value passed as increment amount for column: '$column'.");
99 3
            } elseif (!is_string($column)) {
100
                throw new InvalidArgumentException('Non-associative array passed to incrementEach method.');
101
            }
102
103 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

103
            $columns[$column] = new Expression($this->/** @scrutinizer ignore-call */ getTableAlias($this->from) . '.' . $column . ' + ' . $amount);
Loading history...
104
        }
105
106 3
        return $this->update(array_merge($columns, $extra));
107
    }
108
109
    /**
110
     * Decrement the given column's values by the given amounts.
111
     *
112
     * @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...
113
     * @param  array<string, mixed>  $extra
114
     * @return int
115
     *
116
     * @throws \InvalidArgumentException
117
     */
118 2
    public function decrementEach(array $columns, array $extra = [])
119
    {
120 2
        foreach ($columns as $column => $amount) {
121 2
            if (!is_numeric($amount)) {
122
                throw new InvalidArgumentException("Non-numeric value passed as decrement amount for column: '$column'.");
123 2
            } elseif (!is_string($column)) {
124
                throw new InvalidArgumentException('Non-associative array passed to decrementEach method.');
125
            }
126
127 2
            $columns[$column] = new Expression($this->getTableAlias($this->from) . '.' . $column . ' - ' . $amount);
128
        }
129
130 2
        return $this->update(array_merge($columns, $extra));
131
    }
132
133
    /**
134
     * Insert new records or update the existing ones.
135
     *
136
     * @param array<mixed> $values
137
     * @param array<mixed>|string $uniqueBy
138
     * @param array<mixed>|null $update
139
     * @return int
140
     * @throws BindException
141
     */
142 6
    public function upsert(array $values, $uniqueBy, $update = null)
143
    {
144
        assert($this->grammar instanceof Grammar);
145
146 6
        if (empty($values)) {
147 1
            return 0;
148 5
        } elseif ($update === []) {
149
            return (int) $this->insert($values);
150
        }
151
152 5
        if (!is_array(reset($values))) {
153 2
            $values = [$values];
154
        }
155
156 5
        foreach($values as $key => $value) {
157 5
            $values[$key] = $this->grammar->convertJsonFields($value);
158 5
            $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

158
            /** @scrutinizer ignore-call */ 
159
            $values[$key] = $this->convertIdToKey($values[$key]);
Loading history...
159 5
            $values[$key] = Arr::undot($values[$key]);
160
        }
161
162 5
        foreach($values as $key => $value) {
163 5
            foreach ($value as $dataKey => $data) {
164 5
                $values[$key][$dataKey] = $this->bindValue($data, 'upsert');
165
            }
166
        }
167
168 5
        $uniqueBy = $this->grammar->convertJsonFields($uniqueBy);
169
170 5
        if (is_null($update)) {
171 1
            $update = array_keys(reset($values));
172
        }
173
174 5
        foreach ($update as $key => $value) {
175 5
            $update[$key] = $this->convertIdToKey($value);
176
        }
177
178 5
        $update = $this->grammar->convertJsonFields($update);
179
180 5
        $this->applyBeforeQueryCallbacks();
181
182 5
        $bindings = $this->bindings['upsert'];
183
184 5
        $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

184
        $aql = $this->grammar->compileUpsert(/** @scrutinizer ignore-type */ $this, $values, (array) $uniqueBy, $update);
Loading history...
185
186 5
        return $this->connection->affectingStatement(
187 5
            $aql,
188 5
            $bindings
189 5
        );
190
    }
191
}
192