Completed
Push — master ( 8edfdf...cb88f6 )
by Brent
27s queued 12s
created

HasStates::getDefaultStates()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Spatie\ModelStates;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Builder;
8
use Spatie\ModelStates\Exceptions\InvalidConfig;
9
use Spatie\ModelStates\Exceptions\CouldNotPerformTransition;
10
11
/**
12
 * @mixin \Illuminate\Database\Eloquent\Model
13
 */
14
trait HasStates
15
{
16
    /** @var \Spatie\ModelStates\StateConfig[]|null */
17
    protected static $stateFields = null;
18
19
    abstract protected function registerStates(): void;
20
21
    public static function bootHasStates(): void
22
    {
23
        $serialiseState = function (StateConfig $stateConfig) {
24
            return function (Model $model) use ($stateConfig) {
25
                $value = $model->getAttribute($stateConfig->field);
26
27
                if ($value === null) {
28
                    $value = $stateConfig->defaultStateClass;
29
                }
30
31
                if ($value === null) {
32
                    return;
33
                }
34
35
                $stateClass = $stateConfig->stateClass::resolveStateClass($value);
36
37
                if (! is_subclass_of($stateClass, $stateConfig->stateClass)) {
38
                    throw InvalidConfig::fieldDoesNotExtendState(
39
                        $stateConfig->field,
40
                        $stateConfig->stateClass,
41
                        $stateClass
42
                    );
43
                }
44
45
                $model->setAttribute(
46
                    $stateConfig->field,
47
                    State::resolveStateName($value)
48
                );
49
            };
50
        };
51
52
        $unserialiseState = function (StateConfig $stateConfig) {
53
            return function (Model $model) use ($stateConfig) {
54
                $stateClass = $stateConfig->stateClass::resolveStateClass($model->getAttribute($stateConfig->field));
55
56
                $defaultState = $stateConfig->defaultStateClass
57
                    ? new $stateConfig->defaultStateClass($model)
58
                    : null;
59
60
                $model->setAttribute(
61
                    $stateConfig->field,
62
                    class_exists($stateClass)
63
                        ? new $stateClass($model)
64
                        : $defaultState
65
                );
66
            };
67
        };
68
69
        foreach (self::getStateConfig() as $stateConfig) {
70
            static::retrieved($unserialiseState($stateConfig));
71
            static::created($unserialiseState($stateConfig));
72
            static::saved($unserialiseState($stateConfig));
73
74
            static::updating($serialiseState($stateConfig));
75
            static::creating($serialiseState($stateConfig));
76
            static::saving($serialiseState($stateConfig));
77
        }
78
    }
79
80
    public function initializeHasStates(): void
81
    {
82
        foreach (self::getStateConfig() as $stateConfig) {
83
            if (! $stateConfig->defaultStateClass) {
84
                continue;
85
            }
86
87
            $this->{$stateConfig->field} = new $stateConfig->defaultStateClass($this);
88
        }
89
    }
90
91 View Code Duplication
    public function scopeWhereState(Builder $builder, string $field, $states): Builder
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
92
    {
93
        self::getStateConfig();
94
95
        /** @var \Spatie\ModelStates\StateConfig|null $stateConfig */
96
        $stateConfig = self::getStateConfig()[$field] ?? null;
97
98
        if (! $stateConfig) {
99
            throw InvalidConfig::unknownState($field, $this);
100
        }
101
102
        $abstractStateClass = $stateConfig->stateClass;
103
104
        $stateNames = collect((array) $states)->map(function ($state) use ($abstractStateClass) {
105
            return $abstractStateClass::resolveStateName($state);
106
        });
107
108
        return $builder->whereIn($field, $stateNames);
109
    }
110
111 View Code Duplication
    public function scopeWhereNotState(Builder $builder, string $field, $states): Builder
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
112
    {
113
        /** @var \Spatie\ModelStates\StateConfig|null $stateConfig */
114
        $stateConfig = self::getStateConfig()[$field] ?? null;
115
116
        if (! $stateConfig) {
117
            throw InvalidConfig::unknownState($field, $this);
118
        }
119
120
        $stateNames = collect((array) $states)->map(function ($state) use ($stateConfig) {
121
            return $stateConfig->stateClass::resolveStateName($state);
122
        });
123
124
        return $builder->whereNotIn($field, $stateNames);
125
    }
126
127
    /**
128
     * @param \Spatie\ModelStates\State|string $state
129
     * @param string|null $field
130
     */
131
    public function transitionTo($state, string $field = null)
132
    {
133
        $stateConfig = self::getStateConfig();
134
135
        if ($field === null && count($stateConfig) > 1) {
136
            throw CouldNotPerformTransition::couldNotResolveTransitionField($this);
137
        }
138
139
        $field = $field ?? reset($stateConfig)->field;
140
141
        $this->{$field}->transitionTo($state);
142
    }
143
144
    /**
145
     * @param string $fromClass
146
     * @param string $toClass
147
     *
148
     * @return \Spatie\ModelStates\Transition|string|null
149
     */
150
    public function resolveTransitionClass(string $fromClass, string $toClass)
151
    {
152
        foreach (static::getStateConfig() as $stateConfig) {
153
            $transitionClass = $stateConfig->resolveTransition($this, $fromClass, $toClass);
154
155
            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...
156
                return $transitionClass;
157
            }
158
        }
159
160
        throw CouldNotPerformTransition::notFound($fromClass, $toClass, $this);
161
    }
162
163
    protected function addState(string $field, string $stateClass): StateConfig
164
    {
165
        $stateConfig = new StateConfig($field, $stateClass);
166
167
        static::$stateFields[$stateConfig->field] = $stateConfig;
168
169
        return $stateConfig;
170
    }
171
172
    /**
173
     * @return \Spatie\ModelStates\StateConfig[]
174
     */
175
    private static function getStateConfig(): array
176
    {
177
        if (static::$stateFields === null) {
178
            static::$stateFields = [];
179
180
            (new static)->registerStates();
181
        }
182
183
        return static::$stateFields ?? [];
184
    }
185
186
    public static function getStates(): Collection
187
    {
188
        return collect(static::getStateConfig())
189
            ->map(function ($state) {
190
                return $state->stateClass::all();
191
            });
192
    }
193
194
    public static function getStatesFor(string $column): Collection
195
    {
196
        return static::getStates()->get($column, new Collection);
197
    }
198
199
    public static function getDefaultStates(): Collection
200
    {
201
        return collect(static::getStateConfig())
202
            ->map(function ($state) {
203
                return $state->defaultStateClass;
204
            });
205
    }
206
207
    public static function getDefaultStateFor(string $column): string
208
    {
209
        return static::getDefaultStates()->get($column);
210
    }
211
}
212