Test Failed
Push — master ( 741545...e25e21 )
by Julien
11:23
created

AbstractModel   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 367
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 32
eloc 93
c 2
b 0
f 0
dl 0
loc 367
ccs 0
cts 92
cp 0
rs 9.84

16 Methods

Rating   Name   Duplication   Size   Complexity  
A _() 0 6 1
A __set() 0 18 3
A addUuidValidation() 0 14 4
A addPositionValidation() 0 20 2
A addUpdatedValidation() 0 3 1
A addDefaultRelationships() 0 7 1
A addDeletedValidation() 0 3 1
A addRestoredValidation() 0 3 1
A __get() 0 16 3
A initialize() 0 4 1
A addCreatedValidation() 0 3 1
A addSoftDeleteValidation() 0 15 2
A addUserRelationship() 0 14 3
A __call() 0 15 4
A genericValidation() 0 15 1
A addCrudValidation() 0 30 3
1
<?php
2
/**
3
 * This file is part of the Zemit Framework.
4
 *
5
 * (c) Zemit Team <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE.txt
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Zemit\Models;
12
13
use Phalcon\Mvc\Model\Relation;
14
use Phalcon\Validation\Validator\Between;
15
use Phalcon\Validation\Validator\Date;
16
use Phalcon\Validation\Validator\Numericality;
17
use Phalcon\Validation\Validator\PresenceOf;
18
use Phalcon\Validation\Validator\Uniqueness;
19
use Zemit\Locale;
20
use Zemit\Validation;
21
22
/**
23
 * Class Base
24
 *
25
 * @package Zemit\Models
26
 */
27
abstract class AbstractModel extends \Zemit\Mvc\Model
28
{
29
    const LANG_FR = 'fr';
30
    const LANG_EN = 'en';
31
    const LANG_SP = 'sp';
32
    const MAX_UNSIGNED_INT = 4294967295;
33
    
34
    /**
35
     * Initialize method for model.
36
     */
37
    public function initialize()
38
    {
39
        parent::initialize();
40
        $this->addDefaultRelationships();
41
    }
42
    
43
    /**
44
     * Set the default relationships
45
     *
46
     * @return void
47
     */
48
    public function addDefaultRelationships(): void
49
    {
50
        $this->addUserRelationship('userId', 'UserEntity');
51
        $this->addUserRelationship('createdBy', 'CreatedByEntity');
52
        $this->addUserRelationship('updatedBy', 'UpdatedByEntity');
53
        $this->addUserRelationship('deletedBy', 'DeletedByEntity');
54
        $this->addUserRelationship('restoredBy', 'RestoredByEntity');
55
    }
56
    
57
    /**
58
     * @param ?string $class
59
     * @param string $field
60
     * @param string $ref
61
     * @param string $alias
62
     * @param string $type
63
     * @param array $params
64
     * @return ?Relation
65
     */
66
    public function addUserRelationship(
67
        string $field = 'userId',
68
        string $alias = 'UserEntity',
69
        array $params = [],
70
        string $ref = 'id',
71
        string $type = 'belongsTo',
72
        ?string $class = null
73
    ): ?Relation {
74
        if (property_exists($this, $field)) {
75
            $class ??= $this->getIdentity()->getUserClass() ?: $this->getDI()->get('config')->getModelClass(User::class);
76
            return $this->$type($field, $class, $ref, ['alias' => $alias, 'params ' => $params]);
77
        }
78
        
79
        return null;
80
    }
81
    
82
    /**
83
     * Add default basic validations
84
     * - Position
85
     * - Soft delete
86
     * - Create
87
     * - Update
88
     * - Delete
89
     * - Restore
90
     * - Uuid
91
     * - Uid
92
     * - Guid
93
     *
94
     * @param Validation|null $validator
95
     * @return Validation
96
     */
97
    public function genericValidation(?Validation $validator = null)
98
    {
99
        $validator ??= new Validation();
100
        
101
        $this->addPositionValidation($validator);
102
        $this->addSoftDeleteValidation($validator);
103
        $this->addCreatedValidation($validator);
104
        $this->addUpdatedValidation($validator);
105
        $this->addDeletedValidation($validator);
106
        $this->addRestoredValidation($validator);
107
        $this->addUuidValidation($validator, 'uid');
108
        $this->addUuidValidation($validator, 'uuid');
109
        $this->addUuidValidation($validator, 'guid');
110
        
111
        return $validator;
112
    }
113
    
114
    /**
115
     * Add basic validations for the position field to the validator
116
     * - Must be numeric
117
     * - Must be an unsigned integer
118
     *
119
     * @param Validation $validator
120
     * @param string $field
121
     * @param bool $allowEmpty
122
     * @return Validation
123
     */
124
    public function addPositionValidation(Validation $validator, string $field = 'position', bool $allowEmpty = true): Validation
125
    {
126
        if (property_exists($this, $field)) {
127
            
128
            // Must be numeric
129
            $validator->add($field, new Numericality([
130
                'message' => $this->_('not numeric'),
131
                'allowEmpty' => $allowEmpty,
132
            ]));
133
            
134
            // Must be an unsigned integer
135
            $validator->add($field, new Between([
136
                'minimum' => 0,
137
                'maximum' => self::MAX_UNSIGNED_INT,
138
                'message' => $this->_('not an unsigned integer'),
139
                'allowEmpty' => $allowEmpty,
140
            ]));
141
        }
142
        
143
        return $validator;
144
    }
145
    
146
    /**
147
     * Add basic validations for the position field to the validator
148
     * - Must be 0 or 1
149
     * - Must be numeric
150
     *
151
     * @param Validation $validator
152
     * @param string $field
153
     * @param bool $allowEmpty
154
     * @return void
155
     */
156
    public function addSoftDeleteValidation(Validation $validator, string $field = 'deleted', bool $allowEmpty = true)
157
    {
158
        if (property_exists($this, $field)) {
159
            
160
            // Must be 0 or 1
161
            $validator->add($field, new Between([
162
                'minimum' => 0,
163
                'maximum' => 1,
164
                'message' => $this->_('not 0 or 1'),
165
            ]));
166
            
167
            // Must be numeric
168
            $validator->add($field, new Numericality([
169
                'message' => $this->_('not numeric'),
170
                'allowEmpty' => $allowEmpty,
171
            ]));
172
        }
173
    }
174
    
175
    /**
176
     * Add basic validations for the $field to the validator
177
     * - Must be unique
178
     * - Field is required
179
     *
180
     * @param Validation $validator
181
     * @param string $field uuid field to validate
182
     * @param bool $required set to true to add the PresenceOf validation
183
     * @return Validation
184
     */
185
    public function addUuidValidation(Validation $validator, string $field = 'uuid', bool $required = true)
186
    {
187
        if (property_exists($this, $field) && $this->getModelsMetaData()->hasAttribute($this, $field)) {
188
            
189
            // If field is required
190
            if ($required) {
191
                $validator->add($field, new PresenceOf(['message' => $this->_('required')]));
192
            }
193
            
194
            // Must be unique
195
            $validator->add($field, new Uniqueness(['message' => $this->_('not unique')]));
196
        }
197
        
198
        return $validator;
199
    }
200
    
201
    /**
202
     * Add basic validations for the $userIdField and $dateField field to the validator
203
     * - $userIdField: Must be numeric
204
     * - $userIdField: Must be an unsigned integer
205
     * - $dateField: Must be a valid date
206
     *
207
     * @param Validation $validator
208
     * @param string $userIdField user id field to validate
209
     * @param string $dateField date field to validate
210
     * @param bool $allowEmpty set true to allow empty values in user and date field
211
     * @return Validation
212
     */
213
    public function addCrudValidation(Validation $validator, string $userIdField, string $dateField, bool $allowEmpty = true): Validation
214
    {
215
        if (property_exists($this, $userIdField)) {
216
            
217
            // Must be numeric
218
            $validator->add($userIdField, new Numericality([
219
                'message' => $this->_('not numeric'),
220
                'allowEmpty' => $allowEmpty,
221
            ]));
222
            
223
            // Must be an unsigned integer
224
            $validator->add($userIdField, new Between([
225
                'minimum' => 0,
226
                'maximum' => self::MAX_UNSIGNED_INT,
227
                'message' => $this->_('not an unsigned integer'),
228
                'allowEmpty' => $allowEmpty,
229
            ]));
230
        }
231
        
232
        if (property_exists($this, $dateField)) {
233
            
234
            // Must be a valid date format
235
            $validator->add($dateField, new Date([
236
                'format' => self::DATETIME_FORMAT,
237
                'message' => $this->_('invalid date format'),
238
                'allowEmpty' => $allowEmpty,
239
            ]));
240
        }
241
        
242
        return $validator;
243
    }
244
    
245
    /**
246
     * Add crud validation to the user id and date field
247
     *
248
     * @param Validation $validator
249
     * @param string $createdByField user id field to validate
250
     * @param string $createdAtField date field to validate
251
     * @param bool $allowEmpty set true to allow empty values in user and date field
252
     * @return Validation
253
     */
254
    public function addCreatedValidation(Validation $validator, string $createdByField = 'createdBy', string $createdAtField = 'createdAt', bool $allowEmpty = true): Validation
255
    {
256
        return $this->addCrudValidation($validator, $createdByField, $createdAtField, $allowEmpty);
257
    }
258
    
259
    /**
260
     * Add crud validation to the user id and date field
261
     *
262
     * @param Validation $validator
263
     * @param string $updatedByField user id field to validate
264
     * @param string $updatedAtField date field to validate
265
     * @param bool $allowEmpty set true to allow empty values in user and date field
266
     * @return Validation
267
     */
268
    public function addUpdatedValidation(Validation $validator, string $updatedByField = 'updatedBy', string $updatedAtField = 'updatedAt', bool $allowEmpty = true): Validation
269
    {
270
        return $this->addCrudValidation($validator, $updatedByField, $updatedAtField, $allowEmpty);
271
    }
272
    
273
    /**
274
     * Add crud validation to the user id and date field
275
     *
276
     * @param Validation $validator
277
     * @param string $deletedField user id field to validate
278
     * @param string $dateField date field to validate
279
     * @param bool $allowEmpty set true to allow empty values in user and date field
280
     * @return Validation
281
     */
282
    public function addDeletedValidation(Validation $validator, string $deletedField = 'deletedBy', string $dateField = 'deletedAt', bool $allowEmpty = true): Validation
283
    {
284
        return $this->addCrudValidation($validator, $deletedField, $dateField, $allowEmpty);
285
    }
286
    
287
    /**
288
     * Add crud validation to the user id and date field
289
     *
290
     * @param Validation $validator
291
     * @param string $restoredByField user id field to validate
292
     * @param string $restoredAtField date field to validate
293
     * @param bool $allowEmpty set true to allow empty values in user and date field
294
     * @return Validation
295
     */
296
    public function addRestoredValidation(Validation $validator, string $restoredByField = 'restoredBy', string $restoredAtField = 'restoredAt', bool $allowEmpty = true): Validation
297
    {
298
        return $this->addCrudValidation($validator, $restoredByField, $restoredAtField, $allowEmpty);
299
    }
300
    
301
    /**
302
     * Returns the translation string of the given key
303
     *
304
     * @param array $placeholders
305
     * @param string $translateKey
306
     * @return string
307
     */
308
    public function _(string $translateKey, array $placeholders = []): string
309
    {
310
        /** @var \Phalcon\Translate\Adapter\AbstractAdapter $translate */
311
        $translate = $this->getDI()->get('translate');
312
        
313
        return $translate->_($translateKey, $placeholders);
314
    }
315
    
316
    /**
317
     * Magic caller to set or get localed named field automagically using the current locale
318
     * - Allow to call $this->getName{Fr|En|Sp|...}
319
     * - Allow to call $this->setName{Fr|En|Sp|...}
320
     *
321
     * @param string $method method name
322
     * @param array $arguments method arguments
323
     * @return mixed
324
     * @throws \Phalcon\Mvc\Model\Exception
325
     */
326
    public function __call(string $method, array $arguments)
327
    {
328
        /** @var Locale $locale */
329
        $locale = $this->getDI()->get('locale');
330
        
331
        $lang = $locale->getLocale() ?: 'en';
332
        
333
        if (mb_strrpos($method, ucfirst($lang)) !== mb_strlen($method) - mb_strlen($lang)) {
334
            $call = $method . ucfirst($lang);
335
            if (method_exists($this, $call)) {
336
                return $this->$call(...$arguments);
337
            }
338
        }
339
        
340
        return parent::__call($method, $arguments);
341
    }
342
    
343
    /**
344
     * Magic setter to set localed named field automatically using the current locale
345
     * - Allow to set $this->name{Fr|En|Sp|...} from missing name property
346
     *
347
     * @param string $property property name
348
     * @param mixed $value value to set
349
     * @return void
350
     */
351
    public function __set(string $property, $value)
352
    {
353
        /** @var Locale $locale */
354
        $locale = $this->getDI()->get('locale');
355
        
356
        $lang = $locale->getLocale();
357
        
358
        if (mb_strrpos($property, ucfirst($lang)) !== mb_strlen($property) - 2) {
0 ignored issues
show
Bug introduced by
It seems like $lang can also be of type null; however, parameter $string of ucfirst() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

358
        if (mb_strrpos($property, ucfirst(/** @scrutinizer ignore-type */ $lang)) !== mb_strlen($property) - 2) {
Loading history...
359
            $set = $property . ucfirst($lang);
360
            
361
            if (property_exists($this, $set)) {
362
                $this->writeAttribute($set, $value);
363
                
364
                return;
365
            }
366
        }
367
        
368
        parent::__set($property, $value);
369
    }
370
    
371
    /**
372
     * Magic getter to get localed named field automatically using the current locale
373
     * - Allow to get $this->name{Fr|En|Sp|...} from missing name property
374
     *
375
     * @param string $property property name
376
     * @return mixed|null
377
     */
378
    public function __get(string $property)
379
    {
380
        /** @var Locale $locale */
381
        $locale = $this->getDI()->get('locale');
382
        
383
        $lang = $locale->getLocale();
384
        
385
        if (mb_strrpos($property, ucfirst($lang)) !== mb_strlen($property) - 2) {
0 ignored issues
show
Bug introduced by
It seems like $lang can also be of type null; however, parameter $string of ucfirst() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

385
        if (mb_strrpos($property, ucfirst(/** @scrutinizer ignore-type */ $lang)) !== mb_strlen($property) - 2) {
Loading history...
386
            $set = $property . ucfirst($lang);
387
            
388
            if (property_exists($this, $set)) {
389
                return $this->readAttribute($set);
390
            }
391
        }
392
        
393
        return parent::__get($property);
394
    }
395
    
396
    /**
397
     * @TODO Language support for isset as well
398
     */
399
//    public function __isset() {
400
//
401
//    }
402
}
403