Skip to content

Commit 2157a3d

Browse files
committed
feature: complete lazy loading of foreign keys
1 parent f579c8c commit 2157a3d

1 file changed

Lines changed: 23 additions & 36 deletions

File tree

src/Repository.php

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Gt\SqlBuilder\Condition\Condition;
99
use Gt\SqlBuilder\SelectBuilder;
1010
use ReflectionClass;
11-
use ReflectionObject;
1211
use ReflectionProperty;
1312
use Stringable;
1413

@@ -41,7 +40,7 @@ public function __construct(
4140
public function fetch(
4241
string $className,
4342
int|string|Condition... $match,
44-
):null|object {
43+
) {
4544
$parameters = [];
4645

4746
$primaryKey = $this->getPrimaryKey($className);
@@ -58,7 +57,7 @@ public function fetch(
5857
->select(...$this->getColumnList($className))
5958
->where("id = :id");
6059

61-
$resultSet = $this->database->executeSql($builder, $parameters);
60+
$resultSet = $this->database->executeSql((string)$builder, $parameters);
6261
$row = $resultSet->fetch();
6362

6463
$entity = $this->rowToEntity($row, $className);
@@ -127,12 +126,10 @@ public function getColumnList(string $entityClassName):array {
127126
* @param null|object $instance An existing object reference to hydrate
128127
* @return null|T
129128
*/
130-
protected function rowToEntity(Row $row, string $className, ?object $instance = null):null|object {
129+
protected function rowToEntity(Row $row, string $className, ?object $instance = null) {
131130
$refClass = new ReflectionClass($className);
132131

133-
// Create an instance without constructor if none provided
134-
// This allows setting readonly properties
135-
if ($instance === null) {
132+
if($instance === null) {
136133
$instance = $refClass->newInstanceWithoutConstructor();
137134
}
138135

@@ -155,10 +152,10 @@ protected function rowToEntity(Row $row, string $className, ?object $instance =
155152
}
156153
}
157154

158-
if(!$this->rowContains($row, $propertyName)) {
159-
continue;
160-
}
161-
$rowValues[$propertyName] = $row->get($propertyName);
155+
if(!$this->rowContains($row, $propertyName)) {
156+
continue;
157+
}
158+
$rowValues[$propertyName] = $row->get($propertyName);
162159
}
163160

164161
foreach($refPropertyArray as $refProperty) {
@@ -172,8 +169,7 @@ protected function rowToEntity(Row $row, string $className, ?object $instance =
172169
}
173170
else {
174171
$foreignPropertyType = $refProperty->getType()->getName();
175-
// Skip if not a class type (e.g., string, int, etc.)
176-
if (!class_exists($foreignPropertyType)) {
172+
if(!class_exists($foreignPropertyType)) {
177173
continue;
178174
}
179175

@@ -199,7 +195,7 @@ protected function rowToEntity(Row $row, string $className, ?object $instance =
199195
$foreignPrimaryKey,
200196
);
201197

202-
if (!isset($rowValues[$columnName])) {
198+
if(!isset($rowValues[$columnName])) {
203199
continue;
204200
}
205201

@@ -230,7 +226,6 @@ private function setInstanceProperty(
230226
}
231227
}
232228

233-
// Use reflection directly to set the value regardless of readonly status
234229
$refProperty->setValue($instance, $value);
235230
}
236231

@@ -241,29 +236,24 @@ private function handleLazyInstanceProperty(
241236
null|int|string $foreignPrimaryKeyValue,
242237
):void {
243238
if(is_null($foreignPrimaryKeyValue)) {
244-
// Leave the property unset
245239
return;
246240
}
247241

248242
$refClassForeign = new ReflectionClass($typeName);
249-
250-
// Create the lazy ghost with a callback that will load the entity when accessed
251243
$lazyGhost = $refClassForeign->newLazyGhost(
252244
function(object $ghost) use ($refClassForeign, $typeName, $foreignPrimaryKeyValue) {
253245
$referencedEntity = $this->fetch($typeName, $foreignPrimaryKeyValue);
254246
foreach($refClassForeign->getProperties(ReflectionProperty::IS_PUBLIC) as $refProperty) {
255247
if(!$refProperty->isInitialized($referencedEntity)) {
256248
continue;
257249
}
258-
elseif($refProperty->isInitialized($referencedEntity)) {
259-
$value = $refProperty->getValue($referencedEntity);
260-
$refProperty->setValue($ghost, $value);
261-
}
250+
251+
$value = $refProperty->getValue($referencedEntity);
252+
$refProperty->setValue($ghost, $value);
262253
}
263254
}
264255
);
265256

266-
// Set the ghost object as the property value
267257
$refProperty->setValue($instance, $lazyGhost);
268258
}
269259

@@ -324,19 +314,6 @@ private function buildForeignKey(
324314
]);
325315
}
326316

327-
private function buildJunctionKey(
328-
string $propertyName,
329-
string $foreignTableName,
330-
string $foreignPrimaryKey,
331-
):string {
332-
return implode("_", [
333-
$propertyName,
334-
"junction",
335-
$foreignTableName,
336-
$foreignPrimaryKey,
337-
]);
338-
}
339-
340317
private function buildJunctionPlaceholderKey(string $propertyName):string {
341318
return implode("_", [
342319
$propertyName,
@@ -365,3 +342,13 @@ private function rowToPartialEntity(Row $row, string $className):object {
365342

366343
return $entity;
367344
}
345+
346+
private function rowContains(Row $row, string $propertyName):bool {
347+
try {
348+
return $row->contains($propertyName);
349+
}
350+
catch(\TypeError) {
351+
return false;
352+
}
353+
}
354+
}

0 commit comments

Comments
 (0)