Issues (590)

src/Mapper/Builder/FieldBuilder.php (5 issues)

1
<?php
2
3
namespace Bdf\Prime\Mapper\Builder;
4
5
use ArrayAccess;
6
use ArrayIterator;
7
use Bdf\Prime\Mapper\Metadata;
8
use Bdf\Prime\Types\TypeInterface;
9
use Bdf\Prime\Types\TypesHelperInterface;
10
use Closure;
11
use IteratorAggregate;
12
13
/**
14
 * FieldBuilder
15
 *
16
 * @psalm-type FieldDefinition = array{
17
 *     type: string,
18
 *     default: mixed,
19
 *     primary?: "autoincrement"|"sequence"|true,
20
 *     class?: class-string,
21
 *     embedded?: array<string, array>,
22
 *     class_map?: class-string[],
23
 *     polymorph?: bool,
24
 *     discriminator_field?: string,
25
 *     discriminator_attribute?: string,
26
 *     length?: int,
27
 *     comment?: string,
28
 *     alias?: string,
29
 *     precision?: int,
30
 *     scale?: int,
31
 *     nillable?: bool,
32
 *     unsigned?: bool,
33
 *     unique?: bool|string,
34
 *     fixed?: bool,
35
 *     phpOptions?: array,
36
 *     customSchemaOptions?: array,
37
 *     platformOptions?: array,
38
 *     columnDefinition?: string
39
 * }
40
 *
41
 * @implements IteratorAggregate<string, FieldDefinition>
42
 * @implements ArrayAccess<string, FieldDefinition>
43
 */
44
class FieldBuilder implements IteratorAggregate, ArrayAccess, TypesHelperInterface
45
{
46
    /**
47
     * Array of fields definition
48
     *
49
     * @var array<string, FieldDefinition>
50
     */
51
    protected $fields = [];
52
53
    /**
54
     * The name of the current field
55
     *
56
     * @var string
57
     */
58
    protected $current;
59
60
61
    /**
62
     * Get all defined fields
63
     *
64
     * @return array<string, FieldDefinition>
65
     */
66 421
    public function fields(): array
67
    {
68 421
        return $this->fields;
69
    }
70
71
    /**
72
     * Specify the autoincrement key for the table.
73
     *
74
     * @return $this  This builder instance
75
     */
76 415
    public function autoincrement()
77
    {
78 415
        return $this->primary(Metadata::PK_AUTOINCREMENT);
0 ignored issues
show
Bdf\Prime\Mapper\Metadata::PK_AUTOINCREMENT of type string is incompatible with the type Bdf\Prime\Mapper\Metadata expected by parameter $type of Bdf\Prime\Mapper\Builder\FieldBuilder::primary(). ( Ignorable by Annotation )

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

78
        return $this->primary(/** @scrutinizer ignore-type */ Metadata::PK_AUTOINCREMENT);
Loading history...
79
    }
80
81
    /**
82
     * Specify the sequence key(s) for the table.
83
     *
84
     * @return $this  This builder instance
85
     */
86 255
    public function sequence()
87
    {
88 255
        return $this->primary(Metadata::PK_SEQUENCE);
0 ignored issues
show
Bdf\Prime\Mapper\Metadata::PK_SEQUENCE of type string is incompatible with the type Bdf\Prime\Mapper\Metadata expected by parameter $type of Bdf\Prime\Mapper\Builder\FieldBuilder::primary(). ( Ignorable by Annotation )

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

88
        return $this->primary(/** @scrutinizer ignore-type */ Metadata::PK_SEQUENCE);
Loading history...
89
    }
90
91
    /**
92
     * Specify the primary key(s) for the table.
93
     *
94
     * @param Metadata::PK_* $type Type of sequence
95
     *
96
     * @return $this
97
     */
98 505
    public function primary($type = Metadata::PK_AUTO)
99
    {
100 505
        $this->fields[$this->current]['primary'] = $type;
101
102 505
        return $this;
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108 515
    public function string(string $name, int $length = 255, ?string $default = null)
109
    {
110 515
        $this->add($name, TypeInterface::STRING, $default);
111
112 515
        return $this->length($length);
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 96
    public function text(string $name, ?string $default = null)
119
    {
120 96
        return $this->add($name, TypeInterface::TEXT, $default);
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 359
    public function integer(string $name, ?int $default = null)
127
    {
128 359
        return $this->add($name, TypeInterface::INTEGER, $default);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 339
    public function bigint(string $name, $default = null)
135
    {
136 339
        return $this->add($name, TypeInterface::BIGINT, $default);
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 1
    public function smallint(string $name, ?int $default = null)
143
    {
144 1
        return $this->add($name, TypeInterface::SMALLINT, $default);
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 1
    public function tinyint(string $name, ?int $default = null)
151
    {
152 1
        return $this->add($name, TypeInterface::TINYINT, $default);
153
    }
154
155
    /**
156
     * Alias of @see self::double()
157
     *
158
     * {@inheritdoc}
159
     */
160 1
    public function float(string $name, ?float $default = null)
161
    {
162 1
        return $this->double($name, $default);
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 2
    public function double(string $name, ?float $default = null)
169
    {
170 2
        return $this->add($name, TypeInterface::DOUBLE, $default);
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176 1
    public function decimal(string $name, $default = null)
177
    {
178 1
        return $this->add($name, TypeInterface::DECIMAL, $default);
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184 118
    public function boolean(string $name, ?bool $default = null)
185
    {
186 118
        return $this->add($name, TypeInterface::BOOLEAN, $default);
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 1
    public function date(string $name, $default = null)
193
    {
194 1
        return $this->add($name, TypeInterface::DATE, $default);
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 175
    public function dateTime(string $name, $default = null)
201
    {
202 175
        return $this->add($name, TypeInterface::DATETIME, $default);
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208 1
    public function dateTimeTz(string $name, $default = null)
209
    {
210 1
        return $this->add($name, TypeInterface::DATETIMETZ, $default);
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216 1
    public function time(string $name, $default = null)
217
    {
218 1
        return $this->add($name, TypeInterface::TIME, $default);
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224 1
    public function timestamp(string $name, $default = null)
225
    {
226 1
        return $this->add($name, TypeInterface::TIMESTAMP, $default);
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232 1
    public function binary(string $name, ?string $default = null)
233
    {
234 1
        return $this->add($name, TypeInterface::BINARY, $default);
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240 1
    public function blob(string $name, ?string $default = null)
241
    {
242 1
        return $this->add($name, TypeInterface::BLOB, $default);
243
    }
244
245
    /**
246
     * {@inheritdoc}
247
     */
248 1
    public function guid(string $name, $default = null)
249
    {
250 1
        return $this->add($name, TypeInterface::GUID, $default);
251
    }
252
253
    /**
254
     * {@inheritdoc}
255
     */
256 1
    public function json(string $name, $default = null)
257
    {
258 1
        return $this->add($name, TypeInterface::JSON, $default);
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 1
    public function simpleArray(string $name, ?array $default = null)
265
    {
266 1
        return $this->add($name, TypeInterface::TARRAY, $default);
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272 1
    public function object(string $name, $default = null)
273
    {
274 1
        return $this->add($name, TypeInterface::OBJECT, $default);
275
    }
276
277
    /**
278
     * {@inheritdoc}
279
     */
280 1
    public function arrayObject(string $name, ?array $default = null)
281
    {
282 1
        return $this->add($name, TypeInterface::ARRAY_OBJECT, $default);
283
    }
284
285
    /**
286
     * {@inheritdoc}
287
     */
288 216
    public function searchableArray(string $name, ?array $default = null)
289
    {
290 216
        return $this->add($name, TypeInterface::TARRAY, $default);
291
    }
292
293
    /**
294
     * {@inheritdoc}
295
     */
296 6
    public function arrayOf(string $name, string $type, ?array $default = null)
297
    {
298 6
        return $this->add($name, $type.'[]', $default);
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304 2
    public function arrayOfInt(string $name, ?array $default = null)
305
    {
306 2
        return $this->arrayOf($name, TypeInterface::INTEGER, $default);
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     */
312 2
    public function arrayOfDouble(string $name, ?array $default = null)
313
    {
314 2
        return $this->arrayOf($name, TypeInterface::DOUBLE, $default);
315
    }
316
317
    /**
318
     * {@inheritdoc}
319
     */
320 2
    public function arrayOfDateTime(string $name, ?array $default = null)
321
    {
322 2
        return $this->arrayOf($name, TypeInterface::DATETIME, $default);
323
    }
324
325
    /**
326
     * Add a field
327
     *
328
     * @param string $name      The property name
329
     * @param string $type      The repository type
330
     * @param mixed  $default   The default value for repository
331
     *
332
     * @return $this             This builder instance
333
     */
334 561
    public function add(string $name, string $type = TypeInterface::STRING, $default = null)
335
    {
336 561
        $this->current = $name;
337
338 561
        $this->fields[$this->current] = [
339 561
            'type'      => $type,
340 561
            'default'   => $default,
341 561
        ];
342
343 561
        return $this;
344
    }
345
346
    /**
347
     * Add an embedded field
348
     *
349
     * @param string $name The property name
350
     * @param class-string $classname The embedded class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
351
     * @param Closure(static):void $resolver Configure the meta for this embedded
352
     *
353
     * @return $this This builder instance
354
     */
355 389
    public function embedded(string $name, string $classname, Closure $resolver)
356
    {
357 389
        $builder = new static();
358 389
        $resolver($builder);
359
360 389
        $this->fields[$name] = [
361 389
            'class'     => $classname,
362 389
            'embedded'  => $builder->fields(),
363 389
        ];
364
365 389
        return $this;
366
    }
367
368
    /**
369
     * Add an embedded polymorph field
370
     *
371
     * /!\ A discriminator field must be provided, and must not be nillable
372
     *
373
     * <code>
374
     * $fields->polymorph(
375
     *     'subentity',
376
     *     [
377
     *         'user'  => User::class,
378
     *         'admin' => Admin::class,
379
     *     ],
380
     *     function (PolymorphBuilder $builder) {
381
     *         $builder
382
     *             ->string('type')->discriminator()
383
     *             ->string('name')
384
     *         ;
385
     *     }
386
     * );
387
     * </code>
388
     *
389
     * @param string $name The property name
390
     * @param array<string, class-string> $classMap The class mapping, in form [ discriminatorValue => ClassName ]
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, class-string> at position 4 could not be parsed: Unknown type name 'class-string' at position 4 in array<string, class-string>.
Loading history...
391
     * @param Closure(PolymorphBuilder):void $resolver Configure the meta for this embedded
392
     *
393
     * @return $this              This builder instance
394
     */
395 3
    public function polymorph(string $name, array $classMap, Closure $resolver)
396
    {
397 3
        $builder = new PolymorphBuilder();
398 3
        $resolver($builder);
399
400 3
        $this->fields[$name] = [
401 3
            'class_map'               => $classMap,
402 3
            'polymorph'               => true,
403 3
            'embedded'                => $builder->fields(),
404 3
            'discriminator_field'     => $builder->getDiscriminatorField(),
405 3
            'discriminator_attribute' => $builder->getDiscriminatorAttribute(),
406 3
        ];
407
408 3
        return $this;
409
    }
410
411
    /**
412
     * Set length of current string field
413
     *
414
     * @param int|null $length        The length of the value
415
     *
416
     * @return $this             This builder instance
417
     */
418 515
    public function length(?int $length)
419
    {
420 515
        $this->fields[$this->current]['length'] = $length;
421
422 515
        return $this;
423
    }
424
425
    /**
426
     * Set comment on current field
427
     *
428
     * @param string|null $comment    The comment
429
     *
430
     * @return $this             This builder instance
431
     */
432 1
    public function comment(?string $comment)
433
    {
434 1
        $this->fields[$this->current]['comment'] = $comment;
435
436 1
        return $this;
437
    }
438
439
    /**
440
     * Set alias of current field
441
     *
442
     * @param string $alias      The repository name
443
     *
444
     * @return $this             This builder instance
445
     */
446 447
    public function alias(string $alias)
447
    {
448 447
        $this->fields[$this->current]['alias'] = $alias;
449
450 447
        return $this;
451
    }
452
453
    /**
454
     * Set the default value of current field
455
     *
456
     * @param mixed $value       The repository name
457
     *
458
     * @return $this             This builder instance
459
     */
460 1
    public function setDefault($value)
461
    {
462 1
        $this->fields[$this->current]['default'] = $value;
463
464 1
        return $this;
465
    }
466
467
    /**
468
     * Set the precision and scale of a digit
469
     *
470
     * @param int $precision     The number of significant digits that are stored for values
471
     * @param int $scale         The number of digits that can be stored following the decimal point
472
     *
473
     * @return $this             This builder instance
474
     */
475 1
    public function precision(int $precision, int $scale = 0)
476
    {
477 1
        $this->fields[$this->current]['precision'] = $precision;
478 1
        $this->fields[$this->current]['scale'] = $scale;
479
480 1
        return $this;
481
    }
482
483
    /**
484
     * Set nillable flag of current field
485
     *
486
     * @param bool $flag         Activate/Deactivate nillable
487
     *
488
     * @return $this             This builder instance
489
     */
490 426
    public function nillable(bool $flag = true)
491
    {
492 426
        $this->fields[$this->current]['nillable'] = $flag;
493
494 426
        return $this;
495
    }
496
497
    /**
498
     * Set unsigned flag of current field
499
     *
500
     * @param bool $flag         Activate/Deactivate unsigned
501
     *
502
     * @return $this             This builder instance
503
     */
504 1
    public function unsigned(bool $flag = true)
505
    {
506 1
        $this->fields[$this->current]['unsigned'] = $flag;
507
508 1
        return $this;
509
    }
510
511
    /**
512
     * Set unique flag of current field
513
     *
514
     * @param bool|string $index The index name. True to generate one
515
     *
516
     * @return $this             This builder instance
517
     */
518 10
    public function unique($index = true)
519
    {
520 10
        $this->fields[$this->current]['unique'] = $index;
521
522 10
        return $this;
523
    }
524
525
    /**
526
     * Set fixed flag of current field.
527
     *
528
     * Fix length of a string
529
     *
530
     * @param bool $flag         Activate/Deactivate unique
531
     *
532
     * @return $this             This builder instance
533
     */
534 1
    public function fixed(bool $flag = true)
535
    {
536 1
        $this->fields[$this->current]['fixed'] = $flag;
537
538 1
        return $this;
539
    }
540
541
    /**
542
     * Set php class name of the attribute.
543
     *
544
     * @param class-string $className  The php class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
545
     *
546
     * @return $this             This builder instance
547
     */
548 61
    public function phpClass(string $className)
549
    {
550 61
        return $this->phpOptions('className', $className);
551
    }
552
553
    /**
554
     * Set date timezone.
555
     *
556
     * @param string $timezone   The date timezone
557
     *
558
     * @return $this             This builder instance
559
     */
560 59
    public function timezone(string $timezone)
561
    {
562 59
        return $this->phpOptions('timezone', $timezone);
563
    }
564
565
    /**
566
     * Set a php options use by type when database value is transformed to php.
567
     *
568
     * @param string $key The key option
569
     * @param mixed $value The value
570
     *
571
     * @return $this             This builder instance
572
     */
573 64
    public function phpOptions(string $key, $value)
574
    {
575 64
        $this->fields[$this->current]['phpOptions'][$key] = $value;
576
577 64
        return $this;
578
    }
579
580
    //---- methods for schema
581
582
    /**
583
     * Set schema options.
584
     *
585
     * {@see \Doctrine\DBAL\Schema\Table} for the detail
586
     *
587
     * @param array $options     The array of options
588
     *
589
     * @return $this             This builder instance
590
     */
591 1
    public function schemaOptions(array $options)
592
    {
593 1
        $this->fields[$this->current]['customSchemaOptions'] = $options;
594
595 1
        return $this;
596
    }
597
598
    /**
599
     * Set platform options.
600
     *
601
     * {@see \Doctrine\DBAL\Schema\Table} for the detail
602
     *
603
     * @param array $options     The array of options
604
     *
605
     * @return $this             This builder instance
606
     */
607 1
    public function platformOptions(array $options)
608
    {
609 1
        $this->fields[$this->current]['platformOptions'] = $options;
610
611 1
        return $this;
612
    }
613
614
    /**
615
     * Set a custom definition for the schema of this field
616
     *
617
     * @param string $definition  The schema definition
618
     *
619
     * @return $this              This builder instance
620
     */
621 1
    public function definition($definition)
622
    {
623 1
        $this->fields[$this->current]['columnDefinition'] = $definition;
624
625 1
        return $this;
626
    }
627
628
    /**
629
     * Change current field
630
     *
631
     * @param string $name
632
     *
633
     * @return $this
634
     */
635 1
    public function field(string $name)
636
    {
637 1
        if (!isset($this->fields[$name])) {
638
            throw new \RuntimeException('Field ' . $name . ' not found');
639
        }
640
641 1
        $this->current = $name;
642
643 1
        return $this;
644
    }
645
646
    /**
647
     * Replace fields by another builder's fields
648
     *
649
     * @param FieldBuilder|iterable<string, FieldDefinition> $fields
650
     *
651
     * @return $this
652
     */
653 1
    public function fill(iterable $fields)
654
    {
655 1
        if ($fields instanceof FieldBuilder) {
656 1
            $this->fields = $fields->fields;
657
        } elseif (is_array($fields)) {
658
            $this->fields = $fields;
659
        } else {
660
            $this->fields = iterator_to_array($fields);
661
        }
662
663 1
        return $this;
664
    }
665
666
    //---- interator interface
667
668
    /**
669
     * {@inheritdoc}
670
     */
671 512
    public function getIterator(): \Iterator
672
    {
673 512
        return new ArrayIterator($this->fields);
674
    }
675
676
    //---- array access interface
677
678
    /**
679
     * {@inheritdoc}
680
     */
681 8
    public function offsetExists($offset): bool
682
    {
683 8
        return isset($this->fields[$offset]);
684
    }
685
686
    /**
687
     * {@inheritdoc}
688
     */
689
    #[\ReturnTypeWillChange]
690 22
    public function offsetGet($key)
691
    {
692 22
        return $this->fields[$key];
693
    }
694
695
    /**
696
     * {@inheritdoc}
697
     */
698 1
    public function offsetSet($key, $value): void
699
    {
700
        // not allowed
701 1
    }
702
703
    /**
704
     * {@inheritdoc}
705
     */
706 1
    public function offsetUnset($offset): void
707
    {
708
        // not allowed
709 1
    }
710
}
711