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

tempestphp / tempest-framework / 14104110817

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

Pull #1076

github

web-flow
Merge 45a3cfa43 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

12
use function Tempest\Support\arr;
13

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

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

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

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

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

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

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

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

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

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

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

75
        $columns = [];
61✔
76

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

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

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

94
        return $columns;
61✔
95
    }
96

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

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

105
                continue;
3✔
106
            }
107

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

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

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

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

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

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

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

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

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

149
        return $values;
63✔
150
    }
151

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

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

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

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