1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Mangopixel\Adjuster; |
4
|
|
|
|
5
|
|
|
use Illuminate\Database\Eloquent\Model; |
6
|
|
|
use Illuminate\Database\Eloquent\Relations\Relation; |
7
|
|
|
use Illuminate\Support\Collection; |
8
|
|
|
use Mangopixel\Adjuster\Contracts\Adjustable; |
9
|
|
|
use Mangopixel\Adjuster\Exceptions\ModelAdjustedException; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* This trait is where most of the package logic lives. This trait satisfies the |
13
|
|
|
* entire Adjustable contract and you should only use the trait on classes that |
14
|
|
|
* extend Illuminate\Database\Eloquent\Model. |
15
|
|
|
* |
16
|
|
|
* @package Laravel Adjuster |
17
|
|
|
* @author Alexander Tømmerås <[email protected]> |
18
|
|
|
* @license The MIT License |
19
|
|
|
*/ |
20
|
|
|
trait CanBeAdjusted |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* A boolean to keep track of wether the model has been adjusted or not. |
24
|
|
|
* |
25
|
|
|
* @var bool |
26
|
|
|
*/ |
27
|
|
|
protected $adjusted = false; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* The booting method of the model trait. This method will be called once the model |
31
|
|
|
* has been booted, and registers an event listener that listens for save calls |
32
|
|
|
* to block if save protection is enabled. |
33
|
|
|
* |
34
|
|
|
* @return void |
35
|
|
|
* @throws ModelAdjustedException |
36
|
|
|
*/ |
37
|
|
|
protected static function bootCanBeAdjusted() |
38
|
|
|
{ |
39
|
|
|
static::saving( function ( Adjustable $model ) { |
40
|
|
|
if ( $model->isAdjusted() && $model->hasSaveProtection() ) { |
41
|
|
|
throw new ModelAdjustedException(); |
42
|
|
|
} |
43
|
|
|
} ); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Adjusts the model by updating an existing record in the adjustments table or adds |
48
|
|
|
* a new one if no previous adjustments are set. All changes will be merged with |
49
|
|
|
* old changes and old changes can be unset using null. |
50
|
|
|
* |
51
|
|
|
* @param array $changes |
52
|
|
|
* @param array $attributes |
53
|
|
|
* @return Model|null |
54
|
|
|
*/ |
55
|
|
|
public function adjust( array $changes, array $attributes = [ ] ) |
56
|
|
|
{ |
57
|
|
|
$changesColumn = config( 'adjuster.changes_column' ); |
58
|
|
|
$adjustment = $this->adjustment()->exists() ? $this->adjustment : app( 'adjuster.model' ); |
59
|
|
|
$existingChanges = collect( $adjustment->$changesColumn ); |
60
|
|
|
|
61
|
|
|
$changes = $existingChanges->merge( $changes )->filter( function ( $value, $attribute ) { |
62
|
|
|
return ! is_null( $value ) && $this->$attribute !== $value; |
63
|
|
|
} ); |
64
|
|
|
|
65
|
|
|
if ( $changes->isEmpty() ) { |
66
|
|
|
$adjustment->delete(); |
67
|
|
|
} else { |
68
|
|
|
$adjustment->fill( $attributes ); |
69
|
|
|
$adjustment->$changesColumn = $this->castChanges( $changes, $adjustment ); |
70
|
|
|
$this->adjustment()->save( $adjustment ); |
71
|
|
|
|
72
|
|
|
return $adjustment; |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Get the adjustment associated with the adjustable model. |
78
|
|
|
* |
79
|
|
|
* @return Relation |
80
|
|
|
*/ |
81
|
|
|
public function adjustment():Relation |
82
|
|
|
{ |
83
|
|
|
if ( config( 'adjuster.polymorphic' ) ) { |
84
|
|
|
return $this->morphOne( config( 'adjuster.adjustment_model' ), config( 'adjuster.adjustable_column' ) ); |
85
|
|
|
} else { |
86
|
|
|
return $this->hasOne( config( 'adjuster.adjustment_model' ), config( 'adjuster.adjustable_column' ) ); |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Fill the model instance with the adjusted values, replacing the original values. |
92
|
|
|
* |
93
|
|
|
* @return $this |
94
|
|
|
*/ |
95
|
|
|
public function applyAdjustments():Model |
96
|
|
|
{ |
97
|
|
|
$changes = $this->adjustment->{config( 'adjuster.changes_column' )} ?? null; |
98
|
|
|
|
99
|
|
|
if ( is_null( $changes ) ) { |
100
|
|
|
return $this; |
101
|
|
|
} elseif ( is_string( $changes ) ) { |
102
|
|
|
$changes = json_decode( $changes, true ); |
103
|
|
|
} elseif ( $changes instanceof Collection ) { |
104
|
|
|
$changes = $changes->toArray(); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
$this->fill( $changes ); |
108
|
|
|
$this->adjusted = true; |
109
|
|
|
|
110
|
|
|
return $this; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Checks if the given model has applied the adjustments. |
115
|
|
|
* |
116
|
|
|
* @return bool |
117
|
|
|
*/ |
118
|
|
|
public function isAdjusted():bool |
119
|
|
|
{ |
120
|
|
|
return $this->adjusted; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Checks if save protection is enabled. Save protection protects you from persisting |
125
|
|
|
* the model after applying the adjustments. |
126
|
|
|
* |
127
|
|
|
* @return bool |
128
|
|
|
*/ |
129
|
|
|
public function hasSaveProtection():bool |
130
|
|
|
{ |
131
|
|
|
return $this->saveProtection ?? config( 'adjuster.save_protection' ); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Check if the changes attribute has any set casts on the given model, and if so we |
136
|
|
|
* cast the changes collection to the appropiate type. |
137
|
|
|
* |
138
|
|
|
* @param Collection $changes |
139
|
|
|
* @param Model $adjustment |
140
|
|
|
* @return mixed |
141
|
|
|
*/ |
142
|
|
|
protected function castChanges( Collection $changes, Model $adjustment ) |
143
|
|
|
{ |
144
|
|
|
switch ( $adjustment->hasCast( config( 'adjuster.changes_column' ) ) ) { |
145
|
|
|
case 'collection': |
|
|
|
|
146
|
|
|
return $changes; |
147
|
|
|
case 'array': |
|
|
|
|
148
|
|
|
case 'json': |
149
|
|
|
return $changes->toArray(); |
150
|
|
|
default: |
151
|
|
|
return $changes->toJson(); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Fill the model with an array of attributes. |
157
|
|
|
* |
158
|
|
|
* @param array $attributes |
159
|
|
|
* @return $this |
160
|
|
|
* @throws \Illuminate\Database\Eloquent\MassAssignmentException |
161
|
|
|
*/ |
162
|
|
|
abstract public function fill( array $attributes ); |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Define a polymorphic one-to-one relationship. |
166
|
|
|
* |
167
|
|
|
* @param string $related |
168
|
|
|
* @param string $name |
169
|
|
|
* @param string|null $type |
170
|
|
|
* @param string|null $id |
171
|
|
|
* @param string|null $localKey |
172
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\MorphOne |
173
|
|
|
*/ |
174
|
|
|
abstract public function morphOne( $related, $name, $type = null, $id = null, $localKey = null ); |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Define a one-to-one relationship. |
178
|
|
|
* |
179
|
|
|
* @param string $related |
180
|
|
|
* @param string|null $foreignKey |
181
|
|
|
* @param string|null $localKey |
182
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasOne |
183
|
|
|
*/ |
184
|
|
|
abstract public function hasOne( $related, $foreignKey = null, $localKey = null ); |
185
|
|
|
} |
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break
.To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.