Completed
Push — master ( f084cc...7e5136 )
by Alexander
03:08
created

CanBeAdjusted::adjust()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 2
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':
0 ignored issues
show
Coding Style introduced by
CASE statements must be defined using a colon

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.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
146
                return $changes;
147
            case 'array':
0 ignored issues
show
Coding Style introduced by
CASE statements must be defined using a colon

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.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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
}