Completed
Push — master ( 731a88...e0ddb3 )
by Philip
06:14
created

CRUDEntity::validateUnique()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 8.8571
cc 6
eloc 11
nc 5
nop 4
1
<?php
2
3
/*
4
 * This file is part of the CRUDlex package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CRUDlex;
13
14
use CRUDlex\CRUDEntityDefinition;
15
use CRUDlex\CRUDData;
16
17
/**
18
 * Represents a single set of data in field value pairs like the row in a
19
 * database. Depends of course on the {@see CRUDData} implementation being used.
20
 * With this objects, the data is passed arround and validated.
21
 */
22
class CRUDEntity {
23
24
    /**
25
     * The {@see CRUDEntityDefinition} defining how this entity looks like.
26
     */
27
    protected $definition;
28
29
    /**
30
     * Holds the key value data of the entity.
31
     */
32
    protected $entity = array();
33
34
    /**
35
     * Validates the given field for the required constraint.
36
     *
37
     * @param string $field the field to validate
38
     * @param array &$errors the error collecting array
39
     * @param boolean &$valid the validation flag
40
     */
41
    private function validateRequired($field, &$errors, &$valid) {
42
        if ($this->definition->isRequired($field) && !$this->definition->getFixedValue($field) &&
43
            (!array_key_exists($field, $this->entity)
44
            || $this->entity[$field] === null
45
            || $this->entity[$field] === '')) {
46
            $errors[$field]['required'] = true;
47
            $valid = false;
48
        }
49
    }
50
51
    /**
52
     * Validates the given field for the unique constraint.
53
     *
54
     * @param string $field the field to validate
55
     * @param CRUDData $data the data instance to work with
56
     * @param array &$errors the error collecting array
57
     * @param boolean &$valid the validation flag
58
     */
59
    private function validateUnique($field, CRUDData $data, &$errors, &$valid) {
60
        if ($this->definition->isUnique($field) && array_key_exists($field, $this->entity) && $this->entity[$field]) {
61
            $params = array($field => $this->entity[$field]);
62
            $paramsOperators = array($field => '=');
63
            if ($this->entity['id'] !== null) {
64
                $params['id'] = $this->entity['id'];
65
                $paramsOperators['id'] = '!=';
66
            }
67
            $amount = intval($data->countBy($this->definition->getTable(), $params, $paramsOperators, true));
68
            if ($amount > 0) {
69
                $errors[$field]['unique'] = true;
70
                $valid = false;
71
            }
72
        }
73
    }
74
75
    /**
76
     * Validates the given field for the set type.
77
     *
78
     * @param string $field the field to validate
79
     * @param array &$errors the error collecting array
80
     * @param boolean &$valid the validation flag
81
     */
82
    private function validateSet($field, &$errors, &$valid) {
83
        $type = $this->definition->getType($field);
84
        if ($type == 'set' && $this->entity[$field]) {
85
            $setItems = $this->definition->getSetItems($field);
86
            if (!in_array($this->entity[$field], $setItems)) {
87
                $errors[$field]['input'] = true;
88
                $valid = false;
89
            }
90
        }
91
    }
92
93
    /**
94
     * Validates the given field for the int type.
95
     *
96
     * @param string $field the field to validate
97
     * @param array &$errors the error collecting array
98
     * @param boolean &$valid the validation flag
99
     */
100 View Code Duplication
    private function validateInt($field, &$errors, &$valid) {
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...
101
        $type = $this->definition->getType($field);
102
        if ($type == 'int' && $this->entity[$field] !== '' && $this->entity[$field] !== null && (string)(int)$this->entity[$field] != $this->entity[$field]) {
103
            $errors[$field]['input'] = true;
104
            $valid = false;
105
        }
106
    }
107
108
    /**
109
     * Validates the given field for the float type.
110
     *
111
     * @param string $field the field to validate
112
     * @param array &$errors the error collecting array
113
     * @param boolean &$valid the validation flag
114
     */
115 View Code Duplication
    private function validateFloat($field, &$errors, &$valid) {
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...
116
        $type = $this->definition->getType($field);
117
        if ($type == 'float' && $this->entity[$field] !== '' && $this->entity[$field] !== null && (string)(float)$this->entity[$field] != $this->entity[$field]) {
118
            $errors[$field]['input'] = true;
119
            $valid = false;
120
        }
121
    }
122
123
    /**
124
     * Validates the given field for the date type.
125
     *
126
     * @param string $field the field to validate
127
     * @param array &$errors the error collecting array
128
     * @param boolean &$valid the validation flag
129
     */
130
    private function validateDate($field, &$errors, &$valid) {
131
        $type = $this->definition->getType($field);
132
        if ($type == 'date' && $this->entity[$field] && \DateTime::createFromFormat('Y-m-d', $this->entity[$field]) === false) {
133
            $errors[$field]['input'] = true;
134
            $valid = false;
135
        }
136
    }
137
138
    /**
139
     * Validates the given field for the datetime type.
140
     *
141
     * @param string $field the field to validate
142
     * @param array &$errors the error collecting array
143
     * @param boolean &$valid the validation flag
144
     */
145
    private function validateDateTime($field, &$errors, &$valid) {
146
        $type = $this->definition->getType($field);
147
        if ($type == 'datetime' && $this->entity[$field] &&
148
            \DateTime::createFromFormat('Y-m-d H:i', $this->entity[$field]) === false &&
149
            \DateTime::createFromFormat('Y-m-d H:i:s', $this->entity[$field]) === false) {
150
            $errors[$field]['input'] = true;
151
            $valid = false;
152
        }
153
    }
154
155
    /**
156
     * Validates the given field for the reference type.
157
     *
158
     * @param string $field the field to validate
159
     * @param CRUDData $data the data instance to work with
160
     * @param array &$errors the error collecting array
161
     * @param boolean &$valid the validation flag
162
     */
163
    private function validateReference($field, CRUDData $data, &$errors, &$valid) {
164
        $type = $this->definition->getType($field);
165
        if ($type == 'reference' && $this->entity[$field] !== '' && $this->entity[$field] !== null) {
166
            $params = array('id' => $this->entity[$field]);
167
            $paramsOperators = array('id' => '=');
168
            $amount = $data->countBy($this->definition->getReferenceTable($field), $params, $paramsOperators, false);
169
            if ($amount == 0) {
170
                $errors[$field]['input'] = true;
171
                $valid = false;
172
            }
173
        }
174
    }
175
176
    /**
177
     * Constructor.
178
     *
179
     * @param CRUDEntityDefinition $definition
180
     * the definition how this entity looks
181
     */
182
    public function __construct(CRUDEntityDefinition $definition) {
183
        $this->definition = $definition;
184
    }
185
186
    /**
187
     * Sets a field value pair of this entity.
188
     *
189
     * @param string $field
190
     * the field
191
     * @param mixed $value
192
     * the value
193
     */
194
    public function set($field, $value) {
195
        $this->entity[$field] = $value;
196
    }
197
198
    /**
199
     * Gets the value of a field.
200
     *
201
     * @param string $field
202
     * the field
203
     *
204
     * @return mixed
205
     * null on invalid field, an int if the definition says that the
206
     * type of the field is an int, a boolean if the field is a bool or
207
     * else the raw value
208
     */
209
    public function get($field) {
210
211
        if ($this->definition->getFixedValue($field) !== null) {
212
            return $this->definition->getFixedValue($field);
213
        }
214
215
        if (!array_key_exists($field, $this->entity)) {
216
            return null;
217
        }
218
        $value = $this->entity[$field];
219
220
        switch ($this->definition->getType($field)) {
221
            case 'int':
222
                $value = $value !== '' && $value !== null ? intval($value) : null;
223
                break;
224
            case 'float':
225
                $value = $value !== '' && $value !== null ? floatval($value) : null;
226
                break;
227
            case 'bool':
228
                $value = $value && $value !== '0';
229
                break;
230
        }
231
        return $value;
232
    }
233
234
    /**
235
     * Gets the entity definition.
236
     *
237
     * @return CRUDEntityDefinition
238
     * the definition
239
     */
240
    public function getDefinition() {
241
        return $this->definition;
242
    }
243
244
    /**
245
     * Validates the entity against the definition.
246
     *
247
     * @param CRUDData $data
248
     * the data access instance used for counting things
249
     *
250
     * @return array
251
     * an array with the fields "valid" and "errors"; valid provides a quick
252
     * check whether the given entity passes the validation and errors is an
253
     * array with all fields as keys and arrays as values; this field arrays
254
     * contain three keys: required, unique and input; each of them represents
255
     * with a boolean whether the input is ok in that way; if "required" is
256
     * true, the field wasn't set, unique means the uniqueness of the field in
257
     * the datasource and input is used to indicate whether the form of the
258
     * value is correct (a valid int, date, depending on the type in the
259
     * definition)
260
     */
261
    public function validate(CRUDData $data) {
262
263
        $fields = $this->definition->getEditableFieldNames();
264
        $errors = array();
265
        $valid = true;
266
        foreach ($fields as $field) {
267
            $errors[$field] = array('required' => false, 'unique' => false, 'input' => false);
268
269
            $this->validateRequired($field, $errors, $valid);
270
            $this->validateUnique($field, $data, $errors, $valid);
271
272
            $this->validateSet($field, $errors, $valid);
273
            $this->validateInt($field, $errors, $valid);
274
            $this->validateFloat($field, $errors, $valid);
275
            $this->validateDate($field, $errors, $valid);
276
            $this->validateDateTime($field, $errors, $valid);
277
            $this->validateReference($field, $data, $errors, $valid);
278
279
        }
280
        return array('valid' => $valid, 'errors' => $errors);
281
    }
282
283
}
284