• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ICanBoogie / ActiveRecord / 4525423125

pending completion
4525423125

push

github

Olivier Laviale
Build schema from attributes

191 of 191 new or added lines in 20 files covered. (100.0%)

1499 of 1875 relevant lines covered (79.95%)

31.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.38
/lib/ActiveRecord/SchemaBuilder.php
1
<?php
2

3
namespace ICanBoogie\ActiveRecord;
4

5
use ICanBoogie\ActiveRecord\Attribute;
6
use ICanBoogie\ActiveRecord\Attribute\SchemaAttribute;
7
use LogicException;
8

9
use function array_filter;
10
use function in_array;
11
use function is_string;
12

13
final class SchemaBuilder
14
{
15
    public const SIZE_TINY = 'TINY';
16
    public const SIZE_SMALL = 'SMALL';
17
    public const SIZE_MEDIUM = 'MEDIUM';
18
    public const SIZE_BIG = 'BIG';
19

20
    public const NOW = 'NOW';
21
    public const CURRENT_TIMESTAMP = 'CURRENT_TIMESTAMP';
22

23
    /**
24
     * @var array<string, SchemaColumn>
25
     */
26
    private array $columns = [];
27

28
    /**
29
     * @var array<array{ string|array<string>, bool, ?string }>
30
     */
31
    private array $indexes = [];
32

33
    public function build(): Schema
34
    {
35
        $schema = new Schema($this->columns);
107✔
36

37
        foreach ($this->indexes as $index) {
107✔
38
            $schema->index(...$index);
4✔
39
        }
40

41
        return $schema;
107✔
42
    }
43

44
    public function add_column(
45
        string $col_name,
46
        string $type,
47
        string|int|null $size = null,
48
        bool $unsigned = false,
49
        bool $null = false,
50
        mixed $default = null,
51
        bool $auto_increment = false,
52
        bool $unique = false,
53
        bool $primary = false,
54
        ?string $comment = null,
55
        ?string $collate = null,
56
    ): self {
57
        $this->columns[$col_name] = new SchemaColumn(
×
58
            type: $type,
×
59
            size: $size,
×
60
            unsigned: $unsigned,
×
61
            null: $null,
×
62
            default: $default,
×
63
            auto_increment: $auto_increment,
×
64
            unique: $unique,
×
65
            primary: $primary,
×
66
            comment: $comment,
×
67
            collate: $collate,
×
68
        );
×
69

70
        return $this;
×
71
    }
72

73
    public function add_boolean(
74
        string $col_name,
75
        bool $null = false,
76
        /** @obsolete */
77
        bool $unique = false,
78
    ): self {
79
        $this->columns[$col_name] = SchemaColumn::boolean(
2✔
80
            null: $null,
2✔
81
            unique: $unique,
2✔
82
        );
2✔
83

84
        return $this;
2✔
85
    }
86

87
    public function add_integer(
88
        string $col_name,
89
        int|string|null $size = null,
90
        bool $unsigned = false,
91
        bool $null = false,
92
        bool $unique = false,
93
    ): self {
94
        $this->columns[$col_name] = SchemaColumn::int(
87✔
95
            size: $size,
87✔
96
            unsigned: $unsigned,
87✔
97
            null: $null,
87✔
98
            unique: $unique
87✔
99
        );
87✔
100

101
        return $this;
87✔
102
    }
103

104
    public function add_decimal(
105
        string $col_name,
106
        ?int $precision = null,
107
        bool $unsigned = false,
108
        bool $null = false,
109
    ): self {
110
        $this->columns[$col_name] = SchemaColumn::float(
1✔
111
            precision: $precision,
1✔
112
            unsigned: $unsigned,
1✔
113
            null: $null
1✔
114
        );
1✔
115

116
        return $this;
1✔
117
    }
118

119
    public function add_serial(
120
        string $col_name,
121
        bool $primary = false,
122
    ): self {
123
        $this->columns[$col_name] = SchemaColumn::serial(
106✔
124
            primary: $primary,
106✔
125
        );
106✔
126

127
        return $this;
106✔
128
    }
129

130
    public function add_foreign(
131
        string $col_name,
132
        bool $null = false,
133
        bool $unique = false,
134
        bool $primary = false,
135
    ): self {
136
        $this->columns[$col_name] = SchemaColumn::foreign(
91✔
137
            null: $null,
91✔
138
            unique: $unique,
91✔
139
            primary: $primary,
91✔
140
        );
91✔
141

142
        return $this;
91✔
143
    }
144

145
    public function add_date(
146
        string $col_name,
147
        bool $null = false,
148
        ?string $default = null,
149
    ): self {
150
        $this->columns[$col_name] = SchemaColumn::date(
8✔
151
            null: $null,
8✔
152
            default: $default,
8✔
153
        );
8✔
154

155
        return $this;
8✔
156
    }
157

158
    public function add_datetime(
159
        string $col_name,
160
        bool $null = false,
161
        ?string $default = null,
162
    ): self {
163
        $this->columns[$col_name] = SchemaColumn::datetime(
88✔
164
            null: $null,
88✔
165
            default: $default,
88✔
166
        );
88✔
167

168
        return $this;
88✔
169
    }
170

171
    public function add_timestamp(
172
        string $col_name,
173
        bool $null = false,
174
        ?string $default = null,
175
    ): self {
176
        $this->columns[$col_name] = SchemaColumn::timestamp(
2✔
177
            null: $null,
2✔
178
            default: $default,
2✔
179
        );
2✔
180

181
        return $this;
2✔
182
    }
183

184
    public function add_char(
185
        string $col_name,
186
        int $size = 255,
187
        bool $null = false,
188
        bool $unique = false,
189
        bool $primary = false,
190
        ?string $comment = null,
191
        ?string $collate = null,
192
    ): self {
193
        $this->columns[$col_name] = SchemaColumn::char(
13✔
194
            size: $size,
13✔
195
            null: $null,
13✔
196
            unique: $unique,
13✔
197
            primary: $primary,
13✔
198
            comment: $comment,
13✔
199
            collate: $collate,
13✔
200
        );
13✔
201

202
        return $this;
13✔
203
    }
204

205
    public function add_varchar(
206
        string $col_name,
207
        int $size = 255,
208
        bool $null = false,
209
        bool $unique = false,
210
        bool $primary = false,
211
        ?string $comment = null,
212
        ?string $collate = null,
213
    ): self {
214
        $this->columns[$col_name] = SchemaColumn::varchar(
107✔
215
            size: $size,
107✔
216
            null: $null,
107✔
217
            unique: $unique,
107✔
218
            primary: $primary,
107✔
219
            comment: $comment,
107✔
220
            collate: $collate,
107✔
221
        );
107✔
222

223
        return $this;
107✔
224
    }
225

226
    public function add_binary(
227
        string $col_name,
228
        int $size = 255,
229
        bool $null = false,
230
        bool $unique = false,
231
        bool $primary = false,
232
        ?string $comment = null,
233
    ): self {
234
        $this->columns[$col_name] = new SchemaColumn(
1✔
235
            type: SchemaColumn::TYPE_BINARY,
1✔
236
            size: $size,
1✔
237
            null: $null,
1✔
238
            unique: $unique,
1✔
239
            primary: $primary,
1✔
240
            comment: $comment,
1✔
241
        );
1✔
242

243
        return $this;
1✔
244
    }
245

246
    public function add_varbinary(
247
        string $col_name,
248
        int $size = 255,
249
        bool $null = false,
250
        bool $unique = false,
251
        bool $primary = false,
252
        ?string $comment = null,
253
    ): self {
254
        $this->columns[$col_name] = new SchemaColumn(
×
255
            type: SchemaColumn::TYPE_VARBINARY,
×
256
            size: $size,
×
257
            null: $null,
×
258
            unique: $unique,
×
259
            primary: $primary,
×
260
            comment: $comment,
×
261
        );
×
262

263
        return $this;
×
264
    }
265

266
    public function add_blob(
267
        string $col_name,
268
        string|null $size = null,
269
        bool $null = false,
270
        bool $unique = false,
271
        bool $primary = false,
272
        ?string $comment = null,
273
        ?string $collate = null,
274
    ): self {
275
        $this->columns[$col_name] = SchemaColumn::blob(
×
276
            size: $size,
×
277
            null: $null,
×
278
            unique: $unique,
×
279
            primary: $primary,
×
280
            comment: $comment,
×
281
            collate: $collate,
×
282
        );
×
283

284
        return $this;
×
285
    }
286

287
    public function add_text(
288
        string $col_name,
289
        string|null $size = null,
290
        bool $null = false,
291
        bool $unique = false,
292
        bool $primary = false,
293
        ?string $comment = null,
294
        ?string $collate = null,
295
    ): self {
296
        $this->columns[$col_name] = SchemaColumn::text(
75✔
297
            size: $size,
75✔
298
            null: $null,
75✔
299
            unique: $unique,
75✔
300
            primary: $primary,
75✔
301
            comment: $comment,
75✔
302
            collate: $collate,
75✔
303
        );
75✔
304

305
        return $this;
75✔
306
    }
307

308
    /**
309
     * Adds an index on one or multiple columns.
310
     *
311
     * @param string|array<string> $columns
312
     *     Identifiers of the columns making the unique index.
313
     *
314
     * @return $this
315
     *
316
     * @throws LogicException if a column used by the index is not defined.
317
     */
318
    public function add_index(
319
        array|string $columns,
320
        bool $unique = false,
321
        ?string $name = null
322
    ): self {
323
        if (is_string($columns)) {
5✔
324
            $columns = [ $columns ];
4✔
325
        }
326

327
        foreach ($columns as $column) {
5✔
328
            $this->columns[$column] ??
5✔
329
                throw new LogicException("Column used by index is not defined: $column");
1✔
330
        }
331

332
        $this->indexes[] = [ $columns, $unique, $name ];
4✔
333

334
        return $this;
4✔
335
    }
336

337
    /**
338
     * @param array{ SchemaAttribute }[] $class_attributes
339
     * @param array{ SchemaAttribute, string }[] $property_attributes
340
     *
341
     * @return $this
342
     */
343
    public function from_attributes(
344
        array $class_attributes,
345
        array $property_attributes,
346
    ): self {
347
        $ids = [];
1✔
348

349
        $property_attributes = array_filter($property_attributes, function ($ar) use (&$ids) {
1✔
350
            [ $attribute, $property ] = $ar;
1✔
351

352
            if ($attribute instanceof Attribute\Id) {
1✔
353
                $ids[] = $property;
1✔
354

355
                return false;
1✔
356
            }
357

358
            return true;
1✔
359
        });
1✔
360

361
        foreach ($property_attributes as [ $attribute, $name ]) {
1✔
362
            $is_primary = in_array($name, $ids);
1✔
363

364
            match ($attribute::class) {
1✔
365
                // Numeric Data Types
366

367
                Attribute\Boolean::class => $this->add_boolean(
1✔
368
                    col_name: $name,
1✔
369
                    null: $attribute->null,
1✔
370
                ),
1✔
371

372
                Attribute\Integer::class => $this->add_integer(
1✔
373
                    col_name: $name,
1✔
374
                    size: $attribute->size,
1✔
375
                    unsigned: $attribute->unsigned,
1✔
376
                    null: $attribute->null,
1✔
377
                    unique: $attribute->unique,
1✔
378
                ),
1✔
379

380
                Attribute\Serial::class => $this->add_serial(
1✔
381
                    col_name: $name,
1✔
382
                    primary: in_array($name, $ids)
1✔
383
                ),
1✔
384

385
                // Date and Time Data Types
386

387
                Attribute\Date::class => $this->add_date(
1✔
388
                    col_name: $name,
1✔
389
                    null: $attribute->null,
1✔
390
                    default: $attribute->default,
1✔
391
                ),
1✔
392

393
                Attribute\DateTime::class => $this->add_datetime(
1✔
394
                    col_name: $name,
1✔
395
                    null: $attribute->null,
1✔
396
                    default: $attribute->default,
1✔
397
                ),
1✔
398

399
                // String Data Types
400

401
                Attribute\Char::class => $this->add_char(
1✔
402
                    col_name: $name,
1✔
403
                    size: $attribute->size,
1✔
404
                    null: $attribute->null,
1✔
405
                    unique: $attribute->unique,
1✔
406
                    primary: $is_primary,
1✔
407
                    collate: $attribute->collate,
1✔
408
                ),
1✔
409

410
                Attribute\VarChar::class => $this->add_varchar(
1✔
411
                    col_name: $name,
1✔
412
                    size: $attribute->size,
1✔
413
                    null: $attribute->null,
1✔
414
                    unique: $attribute->unique,
1✔
415
                    primary: $is_primary,
1✔
416
                    collate: $attribute->collate,
1✔
417
                ),
1✔
418

419
                Attribute\Binary::class => $this->add_binary(
1✔
420
                    col_name: $name,
1✔
421
                    size: $attribute->size,
1✔
422
                    null: $attribute->null,
1✔
423
                    unique: $attribute->unique,
1✔
424
                    primary: $is_primary,
1✔
425
                ),
1✔
426

427
                Attribute\VarBinary::class => $this->add_varbinary(
1✔
428
                    col_name: $name,
1✔
429
                    size: $attribute->size,
1✔
430
                    null: $attribute->null,
1✔
431
                    unique: $attribute->unique,
1✔
432
                    primary: $is_primary,
1✔
433
                ),
1✔
434

435
                Attribute\Text::class => $this->add_text(
1✔
436
                    col_name: $name,
1✔
437
                    size: $attribute->size,
1✔
438
                    null: $attribute->null,
1✔
439
                    unique: $attribute->unique,
1✔
440
                    primary: $is_primary,
1✔
441
                ),
1✔
442

443
                // Relations
444

445
                Attribute\BelongsTo::class => $this->add_foreign(
1✔
446
                    col_name: $name,
1✔
447
                    null: $attribute->null,
1✔
448
                    unique: $attribute->unique,
1✔
449
                    primary: $is_primary,
1✔
450
                ),
1✔
451

452
                default => throw new LogicException("Don't know what to do with " . $attribute::class)
1✔
453
            };
1✔
454
        }
455

456
        foreach ($class_attributes as [ $attribute ]) {
1✔
457
            match ($attribute::class) {
1✔
458
                Attribute\Index::class => $this->add_index(
1✔
459
                    columns: $attribute->columns,
1✔
460
                    unique: $attribute->unique,
1✔
461
                    name: $attribute->name,
1✔
462
                ),
1✔
463

464
                default => throw new LogicException("Don't know what to do with " . $attribute::class)
1✔
465
            };
1✔
466
        }
467

468
        return $this;
1✔
469
    }
470
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc