HasValidationTrait::getErrors()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
namespace Ronanchilvers\Orm\Traits;
4
5
use Respect\Validation\Exceptions\NestedValidationException;
6
use RuntimeException;
7
8
/**
9
 * Trait that adds validation support to models
10
 *
11
 * NB: This trait expects the class to use the respect/validation library.
12
 *
13
 * @author Ronan Chilvers <[email protected]>
14
 */
15
trait HasValidationTrait
16
{
17
    /**
18
     * Array of arrays of rule objects per field grouped by scenario
19
     *
20
     * @var array
21
     */
22
    protected $rules = [];
23
24
    /**
25
     * The errors found by the last validation run
26
     *
27
     * @var array
28
     */
29
    protected $errors = [];
30
31
    /**
32
     * Setup method for creating and registering rules
33
     *
34
     * This method is intended to be overriden by sub classes to set up their
35
     * validation rules.
36
     *
37
     * @author Ronan Chilvers <[email protected]>
38
     */
39
    protected function setupValidation()
40
    {
41
    }
42
43
    /**
44
     * Add a rule to this model
45
     *
46
     * @param array $rules      An array of rule objects
47
     * @param string $scenario  The validation scenario these rules apply to
48
     * @author Ronan Chilvers <[email protected]>
49
     */
50
    protected function registerRules(array $rules, $scenario = 'default')
51
    {
52
        if (!isset($this->rules[$scenario])) {
53
            $this->rules[$scenario] = [];
54
        }
55
        $this->rules[$scenario] = $rules;
56
    }
57
58
    /**
59
     * Clear the errors for this model
60
     *
61
     * @author Ronan Chilvers <[email protected]>
62
     */
63
    public function clearErrors()
64
    {
65
        $this->errors = [];
66
    }
67
68
    /**
69
     * Get the current error array for this model
70
     *
71
     * @return array
72
     * @author Ronan Chilvers <[email protected]>
73
     */
74
    public function getErrors()
75
    {
76
        return $this->errors;
77
    }
78
79
    /**
80
     * Does a given field have an error?
81
     *
82
     * @param string $field
83
     * @return boolean
84
     * @author Ronan Chilvers <[email protected]>
85
     */
86
    public function hasError($field)
87
    {
88
        $field = static::prefix($field);
89
90
        return isset($this->errors[$field]);
91
    }
92
93
    /**
94
     * Add an error to the errors array
95
     *
96
     * @param string $field
97
     * @param string $message
98
     * @author Ronan Chilvers <[email protected]>
99
     */
100
    protected function addError(string $field, string $message)
101
    {
102
        $field = static::prefix($field);
103
        if (!isset($this->errors[$field])) {
104
            $this->errors[$field] = [];
105
        }
106
        $this->errors[$field][] = $message;
107
    }
108
109
    /**
110
     * Get the error for a field
111
     *
112
     * @param string $field
113
     * @return string|null
114
     * @author Ronan Chilvers <[email protected]>
115
     */
116
    public function getError($field)
117
    {
118
        $field = static::prefix($field);
119
        if (isset($this->errors[$field])) {
120
            return $this->errors[$field];
121
        }
122
123
        return null;
124
    }
125
126
    /**
127
     * Validate this model with its current data
128
     *
129
     * @param string $scenario  The validation scenario to validate against
130
     * @return boolean
131
     * @author Ronan Chilvers <[email protected]>
132
     */
133
    public function validate($scenario = 'default')
134
    {
135
        $this->setupValidation();
136
        if (!isset($this->rules[$scenario])) {
137
            throw new RuntimeException(
138
                sprintf('Unable to validate non-existent scenario %s', $scenario)
139
            );
140
        }
141
        $rules        = $this->rules[$scenario];
142
        foreach ($rules as $field => $validator) {
143
            $field = static::prefix($field);
144
            $value = null;
145
            if (isset($this->data[$field])) {
146
                $value = $this->data[$field];
147
            }
148
            $name = ucwords(str_replace('_', ' ', strtolower(static::unprefix($field))));
149
            try {
150
                $validator
151
                    ->setName($name)
152
                    ->assert($value);
153
            } catch (NestedValidationException $ex) {
154
                foreach ($ex->getMessages() as $message) {
155
                    $this->addError($field, $message);
156
                }
157
            }
158
        }
159
160
        return 0 == count($this->errors);
161
    }
162
163
    /**
164
     * Save this model
165
     *
166
     * This method either inserts or updates the model row based on the presence
167
     * of an ID. It will return false if the save fails.
168
     *
169
     * @param string $scenario  The validation scenario to validate against
170
     * @return boolean
171
     * @author Ronan Chilvers <[email protected]>
172
     */
173
    public function saveWithValidation($scenario = 'default')
174
    {
175
        $this->clearErrors();
176
        if (false === $this->beforeSave()) {
0 ignored issues
show
Bug introduced by
It seems like beforeSave() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
        if (false === $this->/** @scrutinizer ignore-call */ beforeSave()) {
Loading history...
177
            return false;
178
        }
179
        if (true === $this->isLoaded()) {
0 ignored issues
show
Bug introduced by
It seems like isLoaded() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

179
        if (true === $this->/** @scrutinizer ignore-call */ isLoaded()) {
Loading history...
180
            if (false === $this->beforeUpdate()) {
0 ignored issues
show
Bug introduced by
It seems like beforeUpdate() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

180
            if (false === $this->/** @scrutinizer ignore-call */ beforeUpdate()) {
Loading history...
181
                return false;
182
            }
183
            if ($this->useTimestamps()) {
0 ignored issues
show
Bug introduced by
It seems like useTimestamps() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

183
            if ($this->/** @scrutinizer ignore-call */ useTimestamps()) {
Loading history...
184
                $this->updateTimestamps();
0 ignored issues
show
Bug introduced by
It seems like updateTimestamps() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
                $this->/** @scrutinizer ignore-call */ 
185
                       updateTimestamps();
Loading history...
185
            }
186
            if (false === $this->validate($scenario)) {
187
                return false;
188
            }
189
            if (true !== $this->persistUpdate()) {
0 ignored issues
show
Bug introduced by
It seems like persistUpdate() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

189
            if (true !== $this->/** @scrutinizer ignore-call */ persistUpdate()) {
Loading history...
190
                return false;
191
            }
192
            $this->afterUpdate();
0 ignored issues
show
Bug introduced by
It seems like afterUpdate() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

192
            $this->/** @scrutinizer ignore-call */ 
193
                   afterUpdate();
Loading history...
193
            $this->afterSave();
0 ignored issues
show
Bug introduced by
It seems like afterSave() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

193
            $this->/** @scrutinizer ignore-call */ 
194
                   afterSave();
Loading history...
194
195
            return true;
196
        }
197
        if (false === $this->beforeCreate()) {
0 ignored issues
show
Bug introduced by
It seems like beforeCreate() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

197
        if (false === $this->/** @scrutinizer ignore-call */ beforeCreate()) {
Loading history...
198
            return false;
199
        }
200
        if ($this->useTimestamps()) {
201
            $this->updateTimestamps();
202
        }
203
        if (false === $this->validate($scenario)) {
204
            return false;
205
        }
206
        if (true !== $this->persistInsert()) {
0 ignored issues
show
Bug introduced by
It seems like persistInsert() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

206
        if (true !== $this->/** @scrutinizer ignore-call */ persistInsert()) {
Loading history...
207
            return false;
208
        }
209
        $this->afterCreate();
0 ignored issues
show
Bug introduced by
It seems like afterCreate() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

209
        $this->/** @scrutinizer ignore-call */ 
210
               afterCreate();
Loading history...
210
        $this->afterSave();
211
212
        return true;
213
    }
214
}
215