17
17
class Field implements Expressionable
18
18
{
19
19
use DiContainerTrait;
20
+ use Model \FieldPropertiesTrait;
20
21
use Model \JoinLinkTrait;
21
22
use ReadableCaptionTrait;
22
23
use TrackableTrait;
23
24
24
- // {{{ Properties
25
-
26
- /**
27
- * Default value of field.
28
- *
29
- * @var mixed
30
- */
31
- public $ default ;
32
-
33
- /**
34
- * Field type.
35
- *
36
- * Values are: 'string', 'text', 'boolean', 'integer', 'money', 'float',
37
- * 'date', 'datetime', 'time', 'array', 'object'.
38
- * Can also be set to unspecified type for your own custom handling.
39
- *
40
- * @var string
41
- */
42
- public $ type ;
43
-
44
- /**
45
- * For several types enum can provide list of available options. ['blue', 'red'].
46
- *
47
- * @var array|null
48
- */
49
- public $ enum ;
50
-
51
- /**
52
- * For fields that can be selected, values can represent interpretation of the values,
53
- * for instance ['F'=>'Female', 'M'=>'Male'];.
54
- *
55
- * @var array|null
56
- */
57
- public $ values ;
58
-
59
- /**
60
- * If value of this field is defined by a model, this property
61
- * will contain reference link.
62
- *
63
- * @var string|null
64
- */
65
- protected $ referenceLink ;
66
-
67
- /**
68
- * Actual field name.
69
- *
70
- * @var string|null
71
- */
72
- public $ actual ;
73
-
74
- /**
75
- * Is it system field?
76
- * System fields will be always loaded and saved.
77
- *
78
- * @var bool
79
- */
80
- public $ system = false ;
81
-
82
- /**
83
- * Setting this to true will never actually load or store
84
- * the field in the database. It will action as normal,
85
- * but will be skipped by load/iterate/update/insert.
86
- *
87
- * @var bool
88
- */
89
- public $ never_persist = false ;
90
-
91
- /**
92
- * Setting this to true will never actually store
93
- * the field in the database. It will action as normal,
94
- * but will be skipped by update/insert.
95
- *
96
- * @var bool
97
- */
98
- public $ never_save = false ;
99
-
100
- /**
101
- * Is field read only?
102
- * Field value may not be changed. It'll never be saved.
103
- * For example, expressions are read only.
104
- *
105
- * @var bool
106
- */
107
- public $ read_only = false ;
108
-
109
- /**
110
- * Defines a label to go along with this field. Use getCaption() which
111
- * will always return meaningful label (even if caption is null). Set
112
- * this property to any string.
113
- *
114
- * @var string
115
- */
116
- public $ caption ;
117
-
118
- /**
119
- * Array with UI flags like editable, visible and hidden.
120
- *
121
- * @var array
122
- */
123
- public $ ui = [];
124
-
125
- /**
126
- * Mandatory field must not be null. The value must be set, even if
127
- * it's an empty value.
128
- *
129
- * Can contain error message for UI.
130
- *
131
- * @var bool|string
132
- */
133
- public $ mandatory = false ;
134
-
135
- /**
136
- * Required field must have non-empty value. A null value is considered empty too.
137
- *
138
- * Can contain error message for UI.
139
- *
140
- * @var bool|string
141
- */
142
- public $ required = false ;
143
-
144
- /**
145
- * Should we use typecasting when saving/loading data to/from persistence.
146
- *
147
- * Value can be array [$typecast_save_callback, $typecast_load_callback].
148
- *
149
- * @var bool|array|null
150
- */
151
- public $ typecast ;
152
-
153
- /**
154
- * Should we use serialization when saving/loading data to/from persistence.
155
- *
156
- * Value can be array [$encode_callback, $decode_callback].
157
- *
158
- * @var bool|array|string|null
159
- */
160
- public $ serialize ;
161
-
162
- /**
163
- * Persisting format for type = 'date', 'datetime', 'time' fields.
164
- *
165
- * For example, for date it can be 'Y-m-d', for datetime - 'Y-m-d H:i:s.u' etc.
166
- *
167
- * @var string
168
- */
169
- public $ persist_format ;
170
-
171
- /**
172
- * Persisting timezone for type = 'date', 'datetime', 'time' fields.
173
- *
174
- * For example, 'IST', 'UTC', 'Europe/Riga' etc.
175
- *
176
- * @var string
177
- */
178
- public $ persist_timezone = 'UTC ' ;
179
-
180
- /**
181
- * DateTime class used for type = 'data', 'datetime', 'time' fields.
182
- *
183
- * For example, 'DateTime', 'Carbon\Carbon' etc.
184
- *
185
- * @var string
186
- */
187
- public $ dateTimeClass = \DateTime::class;
188
-
189
- /**
190
- * Timezone class used for type = 'data', 'datetime', 'time' fields.
191
- *
192
- * For example, 'DateTimeZone', 'Carbon\CarbonTimeZone' etc.
193
- *
194
- * @var string
195
- */
196
- public $ dateTimeZoneClass = \DateTimeZone::class;
197
-
198
- // }}}
199
-
200
25
// {{{ Core functionality
201
26
202
27
/**
@@ -485,6 +310,25 @@ public function setNull(): self
485
310
return $ this ;
486
311
}
487
312
313
+ /**
314
+ * @param mixed $value
315
+ *
316
+ * @return mixed
317
+ */
318
+ private function typecastSaveField ($ value , bool $ allowDummyPersistence = false )
319
+ {
320
+ $ persistence = $ this ->getOwner ()->persistence ;
321
+ if ($ persistence === null ) {
322
+ if ($ allowDummyPersistence ) {
323
+ $ persistence = (new \ReflectionClass (Persistence \Sql::class))->newInstanceWithoutConstructor ();
324
+ } else {
325
+ $ this ->getOwner ()->checkPersistence ();
326
+ }
327
+ }
328
+
329
+ return $ persistence ->typecastSaveRow ($ this ->getOwner (), [$ this ->short_name => $ value ])[$ this ->getPersistenceName ()];
330
+ }
331
+
488
332
/**
489
333
* Compare new value of the field with existing one without retrieving.
490
334
* In the trivial case it's same as ($value == $model->get($name)) but this method can be used for:
@@ -501,32 +345,17 @@ public function compare($value, $value2 = null): bool
501
345
$ value2 = $ this ->get ();
502
346
}
503
347
504
- // TODO code below is not nice, we want to replace it, the purpose of the code is simply to
505
- // compare if typecasted values are the same using strict comparison (===) or nor
506
- $ typecastFunc = function ($ v ) {
507
- // do not typecast null values, because that implies calling normalize() which tries to validate that value can't be null in case field value is required
508
- if ($ v === null ) {
509
- return $ v ;
510
- }
511
-
512
- if ($ this ->getOwner ()->persistence === null ) {
513
- $ v = $ this ->normalize ($ v );
514
-
515
- // without persistence, we can not do a lot with non-scalar types, but as DateTime
516
- // is used often, fix the compare for them
517
- // TODO probably create and use a default persistence
518
- if (is_scalar ($ v )) {
519
- return (string ) $ v ;
520
- } elseif ($ v instanceof \DateTimeInterface) {
521
- return $ v ->getTimestamp () . '. ' . $ v ->format ('u ' );
522
- }
523
-
524
- return serialize ($ v );
348
+ $ typecastFunc = function ($ value ): ?string {
349
+ // do not typecast null values, because that implies calling normalize()
350
+ // which tries to validate if value is not null in case field value is required
351
+ if ($ value === null ) {
352
+ return null ;
525
353
}
526
354
527
- return (string ) $ this ->getOwner ()-> persistence -> typecastSaveRow ( $ this -> getOwner (), [ $ this -> short_name => $ v ])[ $ this -> getPersistenceName ()] ;
355
+ return (string ) $ this ->typecastSaveField ( $ value , true ) ;
528
356
};
529
357
358
+ // compare if typecasted values are the same using strict comparison
530
359
return $ typecastFunc ($ value ) === $ typecastFunc ($ value2 );
531
360
}
532
361
@@ -562,24 +391,27 @@ public function useAlias(): bool
562
391
*/
563
392
public function getQueryArguments ($ operator , $ value ): array
564
393
{
565
- $ skipValueTypecast = [
394
+ $ typecastField = $ this ;
395
+ $ allowArray = true ;
396
+ if (in_array ($ operator , [
566
397
Scope \Condition::OPERATOR_LIKE ,
567
398
Scope \Condition::OPERATOR_NOT_LIKE ,
568
399
Scope \Condition::OPERATOR_REGEXP ,
569
400
Scope \Condition::OPERATOR_NOT_REGEXP ,
570
- ];
571
-
572
- if (!in_array ($ operator , $ skipValueTypecast , true )) {
573
- if (is_array ($ value )) {
574
- $ value = array_map (function ($ option ) {
575
- return $ this ->getOwner ()->persistence ->typecastSaveField ($ this , $ option );
576
- }, $ value );
577
- } else {
578
- $ value = $ this ->getOwner ()->persistence ->typecastSaveField ($ this , $ value );
579
- }
401
+ ], true )) {
402
+ $ typecastField = new self (['type ' => 'string ' ]);
403
+ $ typecastField ->setOwner (new Model ($ this ->getOwner ()->persistence , ['table ' => false ]));
404
+ $ typecastField ->short_name = $ this ->short_name ;
405
+ $ allowArray = false ;
580
406
}
581
407
582
- return [$ this , $ operator , $ value ];
408
+ return [
409
+ $ this ,
410
+ $ operator ,
411
+ is_array ($ value ) && $ allowArray
412
+ ? array_map (fn ($ value ) => $ typecastField ->typecastSaveField ($ value ), $ value )
413
+ : $ typecastField ->typecastSaveField ($ value ),
414
+ ];
583
415
}
584
416
585
417
// }}}
0 commit comments