Completed
Pull Request — master (#32)
by
unknown
08:14 queued 06:58
created

HasStates::transitionableStates()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 4
nc 3
nop 2
1
<?php
2
3
namespace Spatie\ModelStates;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Collection;
9
use Spatie\ModelStates\Exceptions\CouldNotPerformTransition;
10
use Spatie\ModelStates\Exceptions\InvalidConfig;
11
12
/**
13
 * @mixin \Illuminate\Database\Eloquent\Model
14
 *
15
 * @method static Builder whereState(string $field, string|string[] $states)
16
 * @method static Builder whereNotState(string $field, string|string[] $states)
17
 */
18
trait HasStates
19
{
20
    protected static ?array
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected '?', expecting T_FUNCTION or T_CONST
Loading history...
21
22
 $stateFields = null;
23
24
    abstract protected function registerStates(): void;
25
26
    public static function bootHasStates(): void
27
    {
28
        $serializeState = function (StateConfig $stateConfig) {
29
            return function (Model $model) use ($stateConfig) {
30
                $value = $model->getAttribute($stateConfig->field);
31
32
                if ($value === null) {
33
                    $value = $stateConfig->defaultStateClass;
34
                }
35
36
                if ($value === null) {
37
                    return;
38
                }
39
40
                $stateClass = $stateConfig->stateClass::resolveStateClass($value);
41
42
                if (! is_subclass_of($stateClass, $stateConfig->stateClass)) {
43
                    throw InvalidConfig::fieldDoesNotExtendState(
44
                        $stateConfig->field,
45
                        $stateConfig->stateClass,
46
                        $stateClass
47
                    );
48
                }
49
50
                $model->setAttribute(
51
                    $stateConfig->field,
52
                    State::resolveStateName($value)
53
                );
54
            };
55
        };
56
57
        $unserializeState = function (StateConfig $stateConfig) {
58
            return function (Model $model) use ($stateConfig) {
59
                $stateClass = $stateConfig->stateClass::resolveStateClass($model->getAttribute($stateConfig->field));
60
61
                $defaultState = $stateConfig->defaultStateClass
62
                    ? new $stateConfig->defaultStateClass($model)
63
                    : null;
64
65
                /** @var null|\Spatie\ModelStates\State $state */
66
                $state = $defaultState;
67
68
                if (class_exists($stateClass)) {
69
                    $state = new $stateClass($model);
70
71
                    $state->setField($stateConfig->field);
72
                }
73
74
                $model->setAttribute(
75
                    $stateConfig->field,
76
                    $state
77
                );
78
            };
79
        };
80
81
        foreach (self::getStateConfig() as $stateConfig) {
82
            static::retrieved($unserializeState($stateConfig));
83
            static::created($unserializeState($stateConfig));
84
            static::saved($unserializeState($stateConfig));
85
86
            static::updating($serializeState($stateConfig));
87
            static::creating($serializeState($stateConfig));
88
            static::saving($serializeState($stateConfig));
89
        }
90
    }
91
92
    public function initializeHasStates(): void
93
    {
94
        foreach (self::getStateConfig() as $stateConfig) {
95
            if (! $stateConfig->defaultStateClass) {
96
                continue;
97
            }
98
99
            $this->{$stateConfig->field} = new $stateConfig->defaultStateClass($this);
100
        }
101
    }
102
103
    public function scopeWhereState(Builder $builder, string $column, $states): Builder
104
    {
105
        $field = Arr::last(explode('.', $column));
106
107
        /** @var \Spatie\ModelStates\StateConfig|null $stateConfig */
108
        $stateConfig = self::getStateConfig()[$field] ?? null;
109
110
        if (! $stateConfig) {
111
            throw InvalidConfig::unknownState($field, $this);
112
        }
113
114
        $abstractStateClass = $stateConfig->stateClass;
115
116
        $stateNames = collect((array) $states)->map(
117
            fn ($state) => $abstractStateClass::resolveStateName($state)
118
        );
119
120
        return $builder->whereIn($column ?? $column, $stateNames);
121
    }
122
123
    public function scopeWhereNotState(Builder $builder, string $column, $states): Builder
124
    {
125
        $field = Arr::last(explode('.', $column));
126
127
        /** @var \Spatie\ModelStates\StateConfig|null $stateConfig */
128
        $stateConfig = self::getStateConfig()[$field] ?? null;
129
130
        if (! $stateConfig) {
131
            throw InvalidConfig::unknownState($field, $this);
132
        }
133
134
        $stateNames = collect((array) $states)->map(
135
            fn ($state) => $stateConfig->stateClass::resolveStateName($state)
136
        );
137
138
        return $builder->whereNotIn($column ?? $column, $stateNames);
139
    }
140
141
    /**
142
     * @param \Spatie\ModelStates\State|string $state
143
     * @param string|null $field
144
     *
145
     * @return \Illuminate\Database\Eloquent\Model
146
     */
147
    public function transitionTo($state, string $field = null)
148
    {
149
        $stateConfig = self::getStateConfig();
150
151
        if ($field === null && count($stateConfig) > 1) {
152
            throw CouldNotPerformTransition::couldNotResolveTransitionField($this);
153
        }
154
155
        $field = $field ?? reset($stateConfig)->field;
156
157
        return $this->{$field}->transitionTo($state);
158
    }
159
160
    /**
161
     * @param string $fromClass
162
     * @param string $toClass
163
     *
164
     * @return \Spatie\ModelStates\Transition|string|null
165
     */
166
    public function resolveTransitionClass(string $fromClass, string $toClass)
167
    {
168
        foreach (static::getStateConfig() as $stateConfig) {
169
            $transitionClass = $stateConfig->resolveTransition($this, $fromClass, $toClass);
170
171
            if ($transitionClass) {
172
                return $transitionClass;
173
            }
174
        }
175
176
        throw CouldNotPerformTransition::notFound($fromClass, $toClass, $this);
177
    }
178
179
    protected function addState(string $field, string $stateClass): StateConfig
180
    {
181
        $stateConfig = new StateConfig($field, $stateClass);
182
183
        static::$stateFields[$stateConfig->field] = $stateConfig;
184
185
        return $stateConfig;
186
    }
187
188
    /**
189
     * @return \Spatie\ModelStates\StateConfig[]
190
     */
191
    public static function getStateConfig(): array
192
    {
193
        if (static::$stateFields === null) {
194
            static::$stateFields = [];
195
196
            (new static)->registerStates();
197
        }
198
199
        return static::$stateFields ?? [];
200
    }
201
202
    public static function getStates(): Collection
203
    {
204
        return collect(static::getStateConfig())
205
            ->map(
206
                fn ($state) => $state->stateClass::all()->map(
207
                    fn ($stateClass) => $stateClass::getMorphClass()
208
                )
209
            );
210
    }
211
212
    public static function getStatesFor(string $column): Collection
213
    {
214
        return static::getStates()->get($column, new Collection);
215
    }
216
217
    public static function getDefaultStates(): Collection
218
    {
219
        return collect(static::getStateConfig())->map(
220
            fn ($state) => $state->defaultStateClass
221
        );
222
    }
223
224
    public static function getDefaultStateFor(string $column): string
225
    {
226
        return static::getDefaultStates()->get($column);
227
    }
228
}
229