BuilderTrait::isVersionedKey()   B
last analyzed

Complexity

Conditions 7
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 8
nc 4
nop 2
dl 0
loc 17
rs 8.2222
c 0
b 0
f 0
1
<?php
2
3
namespace ProAI\Versioning;
4
5
use Exception;
6
7
trait BuilderTrait
8
{
9
    /**
10
     * Get the hydrated models without eager loading.
11
     *
12
     * @param  array  $columns
13
     * @return \Illuminate\Database\Eloquent\Model[]
14
     */
15
    public function getModels($columns = array('*'))
16
    {
17
        // make sure that we select the version table, if the main table is selected
18
        $tempColumns = isset($this->query->columns)
19
            ? array_merge($columns, $this->query->columns)
20
            : $columns;
21
        foreach($tempColumns as $column) {
22
            $segments = explode('.', $column);
23
            if ($segments[0] == $this->model->getTable()) {
24
                $this->query->addSelect($this->model->getVersionTable().'.*');
25
                break;
26
            }
27
        }
28
29
        return parent::getModels($columns);
30
    }
31
32
    /**
33
     * Insert a new record into the database.
34
     *
35
     * @param  array  $values
36
     * @return bool
37
     */
38
    public function insert(array $values)
39
    {
40
        // get version values & values
41
        $versionValues = $this->getVersionValues($values);
42
        $values = $this->getValues($values);
43
44
        // set version, ref_id and latest_version
45
        $values[$this->model->getLatestVersionColumn()] = 1;
46
47
        // insert main table record
48
        if (! $id = $this->query->insertGetId($values)) {
49
            return false;
50
        }
51
52
        $versionValues[$this->model->getVersionKeyName()] = $id;
53
        $versionValues[$this->model->getVersionColumn()] = 1;
54
55
        // insert version table record
56
        $db = $this->model->getConnection();
57
        return $db->table($this->model->getVersionTable())->insert($versionValues);
58
    }
59
60
    /**
61
     * Insert a new record and get the value of the primary key.
62
     *
63
     * @param  array   $values
64
     * @param  string  $sequence
65
     * @return int
66
     */
67
    public function insertGetId(array $values, $sequence = null)
68
    {
69
        // get version values & values
70
        $versionValues = $this->getVersionValues($values);
71
        $values = $this->getValues($values);
72
73
        // set version and latest_version
74
        $values[$this->model->getLatestVersionColumn()] = 1;
75
        $versionValues[$this->model->getVersionColumn()] = 1;
76
77
        // insert main table record
78
        if (! $id = $this->query->insertGetId($values, $sequence)) {
79
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
80
        }
81
82
        // set ref_id
83
        $versionValues[$this->model->getVersionKeyName()] = $id;
84
85
        // insert version table record
86
        $db = $this->model->getConnection();
87
        if (! $db->table($this->model->getVersionTable())->insert($versionValues)) {
88
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
89
        }
90
91
        // fill the latest version value
92
        $this->model->{$this->model->getLatestVersionColumn()} = 1;
93
94
        return $id;
95
    }
96
97
    /**
98
     * Update a record in the database.
99
     *
100
     * @param  array  $values
101
     * @return int
102
     */
103
    public function update(array $values)
104
    {
105
        // update timestamps
106
        $values = $this->addUpdatedAtColumn($values);
0 ignored issues
show
Bug introduced by
It seems like addUpdatedAtColumn() 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

106
        /** @scrutinizer ignore-call */ 
107
        $values = $this->addUpdatedAtColumn($values);
Loading history...
107
108
        // get version values & values
109
        $versionValues = $this->getVersionValues($values);
110
        $values = $this->getValues($values);
111
112
        // get records
113
        $affectedRecords = $this->getAffectedRecords();
114
115
        // update main table records
116
        if (! $this->query->increment($this->model->getLatestVersionColumn(), 1, $values)) {
117
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
118
        }
119
120
        // update version table records
121
        $db = $this->model->getConnection();
122
        foreach ($affectedRecords as $record) {
123
            // get versioned values from record
124
            foreach($this->model->getVersionedAttributeNames() as $key) {
125
                $recordVersionValues[$key] = (isset($versionValues[$key])) ? $versionValues[$key] : $record->{$key};
126
            }
127
128
            // merge versioned values from record and input
129
            $recordVersionValues = array_merge($recordVersionValues, $versionValues);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $recordVersionValues seems to be defined later in this foreach loop on line 125. Are you sure it is defined here?
Loading history...
130
131
            // set version and ref_id
132
            $recordVersionValues[$this->model->getVersionKeyName()] = $record->{$this->model->getKeyName()};
133
            $recordVersionValues[$this->model->getVersionColumn()] = $record->{$this->model->getLatestVersionColumn()}+1;
134
135
            // insert new version
136
            if(! $db->table($this->model->getVersionTable())->insert($recordVersionValues)) {
137
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
138
            }
139
        }
140
141
        // fill the latest version value
142
        $this->model->{$this->model->getLatestVersionColumn()} += 1;
143
144
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type integer.
Loading history...
145
    }
146
147
    /**
148
     * Delete a record from the database.
149
     *
150
     * @return mixed
151
     */
152
    public function delete()
153
    {
154
        if (isset($this->onDelete)) {
155
            return call_user_func($this->onDelete, $this);
156
        }
157
158
        $this->forceDelete();
159
    }
160
161
    /**
162
     * Run the default delete function on the builder.
163
     *
164
     * @return mixed
165
     */
166
    public function forceDelete()
167
    {
168
        // get records
169
        $affectedRecords = $this->getAffectedRecords()->toArray();
170
        $ids = array_map(function($record) {
171
            return $record->{$this->model->getKeyName()};
172
        }, $affectedRecords);
173
174
        // delete main table records
175
        if (! $this->query->delete()) {
176
            return false;
177
        }
178
179
        // delete version table records
180
        $db = $this->model->getConnection();
181
        return $db->table($this->model->getVersionTable())
182
            ->whereIn($this->model->getVersionKeyName(), $ids)
183
            ->delete();
184
    }
185
186
    /**
187
     * Get affected records.
188
     *
189
     * @return array
190
     */
191
    protected function getAffectedRecords()
192
    {
193
        // model only
194
        if ($this->model->getKey()) {
195
            $records = [$this->model];
196
        }
197
198
        // mass assignment
199
        else {
200
            $records = $this->query->get();
201
        }
202
203
        return $records;
204
    }
205
206
    /**
207
     * Get affected ids.
208
     *
209
     * @param  array  $values
210
     * @return array
211
     */
212
    protected function getValues(array $values)
213
    {
214
        $array = [];
215
216
        $versionedKeys = array_merge(
217
            $this->model->getVersionedAttributeNames(),
218
            [$this->model->getLatestVersionColumn(), $this->model->getVersionColumn(), $this->model->getVersionKeyName()]
219
        );
220
221
        foreach ($values as $key => $value) {
222
            if (! $this->isVersionedKey($key, $versionedKeys)) {
223
                $array[$key] = $value;
224
            }
225
        }
226
227
        return $array;
228
    }
229
230
    /**
231
     * Get affected ids.
232
     *
233
     * @param  array  $values
234
     * @return array
235
     */
236
    protected function getVersionValues(array $values)
237
    {
238
        $array = [];
239
240
        $versionedKeys = $this->model->getVersionedAttributeNames();
241
242
        foreach ($values as $key => $value) {
243
            if ($newKey = $this->isVersionedKey($key, $versionedKeys)) {
244
                $array[$newKey] = $value;
245
            }
246
        }
247
248
        return $array;
249
    }
250
251
    /**
252
     * Check if key is in versioned keys.
253
     *
254
     * @param  string  $key
255
     * @param  array  $versionedKeys
256
     * @return string|null
257
     */
258
    protected function isVersionedKey($key, array $versionedKeys)
259
    {
260
        $segments = explode(".",$key);
261
262
        if (count($segments) > 2) {
263
            throw new Exception("Key '".$key."' has too many fractions.");
264
        }
265
266
        if (count($segments) == 1 && in_array($segments[0], $versionedKeys)) {
267
            return $segments[0];
268
        }
269
270
        if (count($segments) == 2 && $segments[0] == $this->model->getVersionTable() && in_array($segments[1], $versionedKeys)) {
271
            return $segments[1];
272
        }
273
274
        return null;
275
    }
276
277
}
278