Completed
Push — master ( 97dcfe...b2d8a3 )
by Brent
01:09
created

HasStates::initializeHasStates()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 3
nc 3
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 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...
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 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...
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