Completed
Pull Request — master (#21)
by
unknown
01:17
created

HasStates::getDefaultStates()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 1
nop 0
1
<?php
2
3
namespace Spatie\ModelStates;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Database\Eloquent\Builder;
7
use Spatie\ModelStates\Exceptions\InvalidConfig;
8
use Spatie\ModelStates\Exceptions\CouldNotPerformTransition;
9
10
/**
11
 * @mixin \Illuminate\Database\Eloquent\Model
12
 */
13
trait HasStates
14
{
15
    /** @var \Spatie\ModelStates\StateConfig[]|null */
16
    protected static $stateFields = null;
17
18
    abstract protected function registerStates(): void;
19
20
    public static function bootHasStates(): void
21
    {
22
        $serialiseState = function (StateConfig $stateConfig) {
23
            return function (Model $model) use ($stateConfig) {
24
                $value = $model->getAttribute($stateConfig->field);
25
26
                if ($value === null) {
27
                    $value = $stateConfig->defaultStateClass;
28
                }
29
30
                if ($value === null) {
31
                    return;
32
                }
33
34
                $stateClass = $stateConfig->stateClass::resolveStateClass($value);
35
36
                if (! is_subclass_of($stateClass, $stateConfig->stateClass)) {
37
                    throw InvalidConfig::fieldDoesNotExtendState(
38
                        $stateConfig->field,
39
                        $stateConfig->stateClass,
40
                        $stateClass
41
                    );
42
                }
43
44
                $model->setAttribute(
45
                    $stateConfig->field,
46
                    State::resolveStateName($value)
47
                );
48
            };
49
        };
50
51
        $unserialiseState = function (StateConfig $stateConfig) {
52
            return function (Model $model) use ($stateConfig) {
53
                $stateClass = $stateConfig->stateClass::resolveStateClass($model->getAttribute($stateConfig->field));
54
55
                $defaultState = $stateConfig->defaultStateClass
56
                    ? new $stateConfig->defaultStateClass($model)
57
                    : null;
58
59
                $model->setAttribute(
60
                    $stateConfig->field,
61
                    class_exists($stateClass)
62
                        ? new $stateClass($model)
63
                        : $defaultState
64
                );
65
            };
66
        };
67
68
        foreach (self::getStateConfig() as $stateConfig) {
69
            static::retrieved($unserialiseState($stateConfig));
70
            static::created($unserialiseState($stateConfig));
71
            static::saved($unserialiseState($stateConfig));
72
73
            static::updating($serialiseState($stateConfig));
74
            static::creating($serialiseState($stateConfig));
75
            static::saving($serialiseState($stateConfig));
76
        }
77
    }
78
79
    public function initializeHasStates(): void
80
    {
81
        foreach (self::getStateConfig() as $stateConfig) {
82
            if (! $stateConfig->defaultStateClass) {
83
                continue;
84
            }
85
86
            $this->{$stateConfig->field} = new $stateConfig->defaultStateClass($this);
87
        }
88
    }
89
90
    public function scopeWhereState(Builder $builder, string $field, $states): Builder
91
    {
92
        self::getStateConfig();
93
94
        /** @var \Spatie\ModelStates\StateConfig|null $stateConfig */
95
        $stateConfig = self::getStateConfig()[$field] ?? null;
96
97
        if (! $stateConfig) {
98
            throw InvalidConfig::unknownState($field, $this);
99
        }
100
101
        $abstractStateClass = $stateConfig->stateClass;
102
103
        $stateNames = collect((array) $states)->map(function ($state) use ($abstractStateClass) {
104
            return $abstractStateClass::resolveStateName($state);
105
        });
106
107
        return $builder->whereIn($field, $stateNames);
108
    }
109
110
    public function scopeWhereNotState(Builder $builder, string $field, $states): Builder
111
    {
112
        /** @var \Spatie\ModelStates\StateConfig|null $stateConfig */
113
        $stateConfig = self::getStateConfig()[$field] ?? null;
114
115
        if (! $stateConfig) {
116
            throw InvalidConfig::unknownState($field, $this);
117
        }
118
119
        $stateNames = collect((array) $states)->map(function ($state) use ($stateConfig) {
120
            return $stateConfig->stateClass::resolveStateName($state);
121
        });
122
123
        return $builder->whereNotIn($field, $stateNames);
124
    }
125
126
    /**
127
     * @param \Spatie\ModelStates\State|string $state
128
     * @param string|null $field
129
     */
130
    public function transitionTo($state, string $field = null)
131
    {
132
        $stateConfig = self::getStateConfig();
133
134
        if ($field === null && count($stateConfig) > 1) {
135
            throw CouldNotPerformTransition::couldNotResolveTransitionField($this);
136
        }
137
138
        $field = $field ?? reset($stateConfig)->field;
139
140
        $this->{$field}->transitionTo($state);
141
    }
142
143
    /**
144
     * @param string $fromClass
145
     * @param string $toClass
146
     *
147
     * @return \Spatie\ModelStates\Transition|string|null
148
     */
149
    public function resolveTransitionClass(string $fromClass, string $toClass)
150
    {
151
        foreach (static::getStateConfig() as $stateConfig) {
152
            $transitionClass = $stateConfig->resolveTransition($this, $fromClass, $toClass);
153
154
            if ($transitionClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $transitionClass of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
155
                return $transitionClass;
156
            }
157
        }
158
159
        throw CouldNotPerformTransition::notFound($fromClass, $toClass, $this);
160
    }
161
162
    protected function addState(string $field, string $stateClass): StateConfig
163
    {
164
        $stateConfig = new StateConfig($field, $stateClass);
165
166
        static::$stateFields[$stateConfig->field] = $stateConfig;
167
168
        return $stateConfig;
169
    }
170
171
    /**
172
     * @return \Spatie\ModelStates\StateConfig[]
173
     */
174
    private static function getStateConfig(): array
175
    {
176
        if (static::$stateFields === null) {
177
            static::$stateFields = [];
178
179
            (new static)->registerStates();
180
        }
181
182
        return static::$stateFields ?? [];
183
    }
184
185
    /**
186
     * Get the registered states for the model.
187
     *
188
     * @return \Illuminate\Support\Collection
189
     */
190
    public static function getStates(): \Illuminate\Support\Collection
191
    {
192
        return collect(static::getStateConfig())
193
            ->map( function($state) {
194
                return $state->stateClass::all()
195
                    ->map( function($state) {
196
                        return new $state(new static);
197
                    });
198
            });
199
    }
200
201
    /**
202
     * Get registered states for a specfic model column.
203
     *
204
     * @param  string $column
205
     * @return \Illuminate\Support\Collection
206
     */
207
    public static function getStatesFor(string $column): \Illuminate\Support\Collection
208
    {
209
        return static::getStates()->get($column, new \Illuminate\Support\Collection);
210
    }
211
212
    /**
213
     * Get default states for the model.
214
     *
215
     * @return \Illuminate\Support\Collection
216
     */
217
    public static function getDefaultStates(): \Illuminate\Support\Collection
218
    {
219
        return collect(static::getStateConfig())
220
            ->map( function($state) {
221
                return is_null($state->defaultStateClass)
222
                    ? null
223
                    : new $state->defaultStateClass(new static);
224
            });
225
    }
226
227
    /**
228
     * Get the default state for a specfic model column.
229
     *
230
     * @param  string $column
231
     * @return \Illuminate\Support\Collection|
232
     */
233
    public static function getDefaultStateFor(string $column): ?\Spatie\ModelStates\State
234
    {
235
        return static::getDefaultStates()->get($column);
236
    }
237
}
238