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

tempestphp / tempest-framework / 14104093847

27 Mar 2025 10:15AM UTC coverage: 79.581% (+0.2%) from 79.334%
14104093847

Pull #1076

github

web-flow
Merge 90e1ee638 into 6af05d563
Pull Request #1076: refactor(database): remove `DatabaseModel` interface

471 of 480 new or added lines in 34 files covered. (98.13%)

1 existing line in 1 file now uncovered.

10679 of 13419 relevant lines covered (79.58%)

92.77 hits per line

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

97.37
/src/Tempest/Database/src/Builder/QueryBuilders/InsertQueryBuilder.php
1
<?php
2

3
namespace Tempest\Database\Builder\QueryBuilders;
4

5
use Tempest\Database\Builder\ModelDefinition;
6
use Tempest\Database\Builder\TableDefinition;
7
use Tempest\Database\Id;
8
use Tempest\Database\Query;
9
use Tempest\Mapper\SerializerFactory;
10
use Tempest\Reflection\ClassReflector;
11
use Tempest\Support\Arr\ImmutableArray;
12

13
use function Tempest\Support\arr;
14

15
/**
16
 * @template TModelClass of object
17
 */
18
final class InsertQueryBuilder
19
{
20
    public function __construct(
63✔
21
        private string|object $model,
22
        private array $rows,
23
        private SerializerFactory $serializerFactory,
24
    ) {}
63✔
25

NEW
26
    public function execute(...$bindings): Id
×
27
    {
NEW
28
        return $this->build()->execute(...$bindings);
×
29
    }
30

31
    public function build(): Query
63✔
32
    {
33
        $table = $this->resolveTableDefinition();
63✔
34

35
        $columns = $this->resolveColumns();
63✔
36

37
        $values = $this->resolveValues($columns);
63✔
38

39
        $valuesPlaceholders = arr($values)
63✔
40
            ->map(function (array $row) {
63✔
41
                return sprintf(
63✔
42
                    '(%s)',
63✔
43
                    arr($row)->map(fn (mixed $value) => '?')->implode(', '),
63✔
44
                );
63✔
45
            })
63✔
46
            ->implode(', ');
63✔
47

48
        return new Query(
63✔
49
            sprintf(
63✔
50
                <<<SQL
63✔
51
                INSERT INTO %s (%s)
52
                VALUES %s
53
                SQL,
63✔
54
                $table,
63✔
55
                arr($columns)->map(fn (string $column) => "`{$column}`")->implode(', '),
63✔
56
                $valuesPlaceholders,
63✔
57
            ),
63✔
58
            arr($values)->flatten(1)->toArray(),
63✔
59
        );
63✔
60
    }
61

62
    private function resolveColumns(): array
63✔
63
    {
64
        $firstEntry = $this->rows[array_key_first($this->rows)];
63✔
65

66
        if (is_array($firstEntry)) {
63✔
67
            return array_keys($firstEntry);
2✔
68
        }
69

70
        if (! is_object($firstEntry)) {
61✔
71
            // Shouldn't be allowed
72
        }
73

74
        $modelClass = new ClassReflector($firstEntry);
61✔
75

76
        $columns = [];
61✔
77

78
        foreach ($modelClass->getPublicProperties() as $property) {
61✔
79
            if (! $property->isInitialized($firstEntry)) {
61✔
80
                continue;
61✔
81
            }
82

83
            // 1:n relations
84
            if ($property->getIterableType()?->isRelation()) {
61✔
85
                continue;
33✔
86
            }
87

88
            if ($property->getType()->isRelation()) {
61✔
89
                $columns[] = $property->getName() . '_id';
31✔
90
            } else {
91
                $columns[] = $property->getName();
61✔
92
            }
93
        }
94

95
        return $columns;
61✔
96
    }
97

98
    private function resolveValues(array $columns): array
63✔
99
    {
100
        $values = [];
63✔
101

102
        foreach ($this->rows as $model) {
63✔
103
            if (is_array($model)) {
63✔
104
                $values[] = $model;
3✔
105

106
                continue;
3✔
107
            }
108

109
            if (! is_object($model)) {
61✔
110
                // TODO: this should now be allowed
111
            }
112

113
            $modelClass = new ClassReflector($model);
61✔
114

115
            $values[] = arr($columns)
61✔
116
                ->map(function (string $column) use ($modelClass, $model) {
61✔
117
                    // TODO: improve
118
                    $column = str($column)->replaceEnd('_id', '');
61✔
119

120
                    $property = $modelClass->getProperty($column);
61✔
121

122
                    $value = $model->{$column};
61✔
123

124
                    if ($value === null) {
61✔
125
                        return $value;
8✔
126
                    }
127

128
                    if ($property->getType()->isRelation()) {
61✔
129
                        if (isset($value->id)) {
24✔
130
                            $value = $value->id->id;
12✔
131
                        } else {
132
                            $value = new InsertQueryBuilder(
12✔
133
                                $value::class,
12✔
134
                                [$value],
12✔
135
                                $this->serializerFactory,
12✔
136
                            )->build();
12✔
137
                        }
138
                    }
139

140
                    // Check if serializer is available for value serialization
141
                    if (($serializer = $this->serializerFactory->forProperty($property)) !== null) {
61✔
142
                        return $serializer->serialize($value);
61✔
143
                    }
144

145
                    return $value;
24✔
146
                })
61✔
147
                ->toArray();
61✔
148
        }
149

150
        return $values;
63✔
151
    }
152

153
    private function resolveTableDefinition(): TableDefinition
63✔
154
    {
155
        $modelDefinition = ModelDefinition::tryFrom($this->model);
63✔
156

157
        if ($modelDefinition === null) {
63✔
158
            return new TableDefinition($this->model);
2✔
159
        }
160

161
        return $modelDefinition->getTableDefinition();
61✔
162
    }
163

164
    //    private function resolveRelations(object $model): array
165
    //    {
166
    //        $relationColumns = [];
167
    //
168
    //        $modelClass = new ClassReflector($model);
169
    //
170
    //        foreach ($modelClass->getPublicProperties() as $property) {
171
    //            if (! $property->isInitialized($model)) {
172
    //                continue;
173
    //            }
174
    //
175
    //            if (! $property->getType()->isRelation()) {
176
    //                continue;
177
    //            }
178
    //
179
    //            $value = $property->getValue($model);
180
    //
181
    //            $relationColumns[$property->getName()] = $value;
182
    //        }
183
    //
184
    //        return $relationColumns;
185
    //    }
186
}
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