88use Tempest \Database \HasMany ;
99use Tempest \Database \QueryStatements \CreateTableStatement ;
1010use Tempest \Database \QueryStatements \DropTableStatement ;
11+
1112use function Tempest \Database \inspect ;
1213use function Tempest \Database \query ;
1314
1415// Test models without pivot tables (traditional HasMany)
1516final class Company
1617{
1718 use \Tempest \Database \IsDatabaseModel;
18-
19+
1920 public string $ name ;
20-
21+
2122 /** @var Employee[] */
2223 #[HasMany]
2324 public array $ employees = [];
@@ -26,9 +27,11 @@ final class Company
2627final class Employee
2728{
2829 use \Tempest \Database \IsDatabaseModel;
29-
30+
3031 public string $ name ;
32+
3133 public ?string $ position = null ;
34+
3235 public ?Company $ company = null ;
3336}
3437
@@ -40,179 +43,182 @@ protected function setUp(): void
4043 $ this ->createTables ();
4144 $ this ->seedData ();
4245 }
43-
46+
4447 protected function tearDown (): void
4548 {
4649 $ this ->dropTables ();
4750 parent ::tearDown ();
4851 }
49-
52+
5053 public function test_has_many_without_pivot_still_works (): void
5154 {
5255 $ companyInspector = inspect (Company::class);
5356 $ employeesRelation = $ companyInspector ->getHasMany ('employees ' );
54-
57+
5558 $ this ->assertInstanceOf (HasMany::class, $ employeesRelation );
5659 $ this ->assertEquals ('employees ' , $ employeesRelation ->name );
57-
60+
5861 // Should not have pivot configuration
5962 $ reflection = new \ReflectionObject ($ employeesRelation );
6063 $ pivotTableProp = $ reflection ->getProperty ('pivotTable ' );
61- $ pivotTableProp ->setAccessible (true );
6264 $ this ->assertNull ($ pivotTableProp ->getValue ($ employeesRelation ));
6365 }
64-
66+
6567 public function test_traditional_has_many_loading (): void
6668 {
6769 $ company = Company::select ()
6870 ->with ('employees ' )
6971 ->where ('name = ? ' , 'Acme Corp ' )
7072 ->first ();
71-
73+
7274 $ this ->assertNotNull ($ company );
7375 $ this ->assertCount (2 , $ company ->employees );
74-
75- $ employeeNames = array_map (fn ($ emp ) => $ emp ->name , $ company ->employees );
76+
77+ $ employeeNames = array_map (fn ($ emp ) => $ emp ->name , $ company ->employees );
7678 $ this ->assertContains ('Alice ' , $ employeeNames );
7779 $ this ->assertContains ('Bob ' , $ employeeNames );
78-
80+
7981 // Employees should not have pivot data
8082 foreach ($ company ->employees as $ employee ) {
8183 $ this ->assertEmpty ($ employee ->pivot ());
8284 }
8385 }
84-
86+
8587 public function test_join_statement_for_traditional_has_many (): void
8688 {
8789 $ companyInspector = inspect (Company::class);
8890 $ employeesRelation = $ companyInspector ->getHasMany ('employees ' );
8991 $ employeesRelation ->property = $ companyInspector ->reflector ->getProperty ('employees ' );
90-
92+
9193 $ joinStatement = $ employeesRelation ->getJoinStatement ();
9294 $ joinString = $ joinStatement ->compile (\Tempest \Database \Config \SQLiteDialect::class);
93-
95+
9496 // Should have single LEFT JOIN (not pivot table joins)
9597 $ this ->assertStringContainsString ('LEFT JOIN employees ' , $ joinString );
9698 $ this ->assertEquals (1 , substr_count ($ joinString , 'LEFT JOIN ' ));
9799 $ this ->assertStringNotContainsString ('pivot ' , $ joinString );
98100 }
99-
101+
100102 public function test_select_fields_without_pivot (): void
101103 {
102104 $ companyInspector = inspect (Company::class);
103105 $ employeesRelation = $ companyInspector ->getHasMany ('employees ' );
104106 $ employeesRelation ->property = $ companyInspector ->reflector ->getProperty ('employees ' );
105-
107+
106108 $ selectFields = $ employeesRelation ->getSelectFields ();
107-
109+
108110 // Should include employee fields but no pivot fields
109111 $ fieldStrings = array_map (
110- fn ($ field ) => $ field ->compile (\Tempest \Database \Config \SQLiteDialect::class),
111- $ selectFields ->toArray ()
112+ fn ($ field ) => $ field ->compile (\Tempest \Database \Config \SQLiteDialect::class),
113+ $ selectFields ->toArray (),
112114 );
113-
115+
114116 $ this ->assertContains ('employees.id AS "employees.id" ' , $ fieldStrings );
115117 $ this ->assertContains ('employees.name AS "employees.name" ' , $ fieldStrings );
116-
118+
117119 // Should not contain pivot fields
118120 foreach ($ fieldStrings as $ field ) {
119121 $ this ->assertStringNotContainsString ('pivot ' , $ field );
120122 }
121123 }
122-
124+
123125 public function test_mixed_relationships_on_same_model (): void
124126 {
125127 // Test that a model can have both traditional HasMany and pivot-based relationships
126128 $ testModel = new class {
127129 use \Tempest \Database \IsDatabaseModel;
128-
130+
129131 public string $ name ;
130-
132+
131133 /** @var Employee[] */
132134 #[HasMany]
133135 public array $ employees = [];
134-
136+
135137 /** @var \Tests\Tempest\Fixtures\Models\ManyToMany\Tag[] */
136138 #[\Tempest \Database \BelongsToMany(pivotFields: ['created_at ' ])]
137139 public array $ tags = [];
138140 };
139-
141+
140142 $ inspector = inspect ($ testModel ::class);
141-
143+
142144 // Traditional HasMany should work
143145 $ employeesRelation = $ inspector ->getHasMany ('employees ' );
144146 $ this ->assertNotNull ($ employeesRelation );
145147 $ this ->assertNull ($ employeesRelation ->pivotTable ?? null );
146-
148+
147149 // BelongsToMany should work
148150 $ tagsRelation = $ inspector ->getBelongsToMany ('tags ' );
149151 $ this ->assertNotNull ($ tagsRelation );
150152 }
151-
153+
152154 public function test_implicit_has_many_detection_still_works (): void
153155 {
154156 // Test model with implicit HasMany (no attribute)
155157 $ testModel = new class {
156158 use \Tempest \Database \IsDatabaseModel;
157-
159+
158160 public string $ name ;
159-
161+
160162 /** @var Employee[] */
161- public array $ employees = []; // No explicit attribute
163+ public array $ employees = []; // No explicit attribute
162164 };
163-
165+
164166 $ inspector = inspect ($ testModel ::class);
165-
167+
166168 // Should still detect as HasMany
167169 $ relation = $ inspector ->getHasMany ('employees ' );
168170 $ this ->assertNotNull ($ relation );
169171 $ this ->assertInstanceOf (HasMany::class, $ relation );
170172 }
171-
173+
172174 public function test_belongs_to_still_works (): void
173175 {
174176 $ employee = Employee::select ()
175177 ->with ('company ' )
176178 ->where ('name = ? ' , 'Alice ' )
177179 ->first ();
178-
180+
179181 $ this ->assertNotNull ($ employee );
180182 $ this ->assertNotNull ($ employee ->company );
181183 $ this ->assertEquals ('Acme Corp ' , $ employee ->company ->name );
182-
184+
183185 // Company should not have pivot data
184186 $ this ->assertEmpty ($ employee ->company ->pivot ());
185187 }
186-
188+
187189 private function createTables (): void
188190 {
189- ( new CreateTableStatement ('companies ' ) )
191+ new CreateTableStatement ('companies ' )
190192 ->primary ()
191193 ->text ('name ' )
192194 ->execute ();
193-
194- ( new CreateTableStatement ('employees ' ) )
195+
196+ new CreateTableStatement ('employees ' )
195197 ->primary ()
196198 ->text ('name ' )
197199 ->text ('position ' , nullable: true )
198200 ->integer ('company_id ' , nullable: true )
199201 ->execute ();
200202 }
201-
203+
202204 private function seedData (): void
203205 {
204206 $ company = Company::create (name: 'Acme Corp ' );
205-
207+
206208 query ('INSERT INTO employees (name, position, company_id) VALUES (?, ?, ?), (?, ?, ?) ' )
207209 ->execute (
208- 'Alice ' , 'Developer ' , $ company ->id ->toInt (),
209- 'Bob ' , 'Manager ' , $ company ->id ->toInt ()
210+ 'Alice ' ,
211+ 'Developer ' ,
212+ $ company ->id ->toInt (),
213+ 'Bob ' ,
214+ 'Manager ' ,
215+ $ company ->id ->toInt (),
210216 );
211217 }
212-
218+
213219 private function dropTables (): void
214220 {
215- ( new DropTableStatement ('employees ' ) )->execute ();
216- ( new DropTableStatement ('companies ' ) )->execute ();
221+ new DropTableStatement ('employees ' )->execute ();
222+ new DropTableStatement ('companies ' )->execute ();
217223 }
218224}
0 commit comments