Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 73 additions & 81 deletions src/Phaseolies/Database/Entity/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -1008,96 +1008,88 @@ public function reload(string $relation)
*/
public function __get($name)
{
try {
if (array_key_exists($name, $this->attributes)) {
return $this->castForGet($name, $this->attributes[$name]);
}
if (array_key_exists($name, $this->attributes)) {
return $this->castForGet($name, $this->attributes[$name]);
}

if (array_key_exists($name, $this->relations)) {
return $this->relations[$name];
}
if (array_key_exists($name, $this->relations)) {
return $this->relations[$name];
}

if ($this->isComputed($name)) {
return $this->resolveComputed($name);
}
if ($this->isComputed($name)) {
return $this->resolveComputed($name);
}

if (method_exists($this, $name)) {
$relation = $this->$name();

if ($relation instanceof Builder) {
$relationType = $this->getLastRelationType();

switch ($relationType) {
case 'linkOne':
$result = $relation->first();
$this->setRelation($name, $result);
return $result;

case 'bindTo':
$result = $relation->first();
$this->setRelation($name, $result);
return $result;

case 'linkMany':
$results = $relation->get();
$this->setRelation($name, $results);
return $results;

case 'bindToMany':
$relatedModel = app($this->getLastRelatedModel());
$relatedModelClass = get_class($relatedModel);
$pivotColumns = app('db')->getTableColumns($this->getLastPivotTable());
$pivotTable = $this->getLastPivotTable();
$pivotSelects = array_map(function ($column) use ($pivotTable) {
return "{$pivotTable}.{$column} as pivot_{$column}";
}, $pivotColumns);

$query = $relatedModel->query()
->select(array_merge(
["{$relatedModel->getTable()}.*"],
$pivotSelects
))
->join(
$this->getLastPivotTable(),
"{$this->getLastPivotTable()}.{$this->getLastRelatedKey()}",
'=',
"{$relatedModel->getTable()}.{$relatedModel->getKeyName()}"
)
->where("{$this->getLastPivotTable()}.{$this->getLastForeignKey()}", '=', $this->getKey());

$results = $query->get();
$grouped = [];
foreach ($results as $result) {
$pivot = [];
foreach ($pivotColumns as $column) {
$pivot[$column] = $result["pivot_{$column}"];
unset($result["pivot_{$column}"]);
}
$pivotObj = (object) $pivot;
$result->pivot = $pivotObj;
$grouped[$pivot[$this->getLastForeignKey()]][] = $result;
if (method_exists($this, $name)) {
$relation = $this->$name();

if ($relation instanceof Builder) {
$relationType = $this->getLastRelationType();

switch ($relationType) {
case 'linkOne':
$result = $relation->first();
$this->setRelation($name, $result);
return $result;

case 'bindTo':
$result = $relation->first();
$this->setRelation($name, $result);
return $result;

case 'linkMany':
$results = $relation->get();
$this->setRelation($name, $results);
return $results;

case 'bindToMany':
$relatedModel = app($this->getLastRelatedModel());
$relatedModelClass = get_class($relatedModel);
$pivotColumns = app('db')->getTableColumns($this->getLastPivotTable());
$pivotTable = $this->getLastPivotTable();
$pivotSelects = array_map(function ($column) use ($pivotTable) {
return "{$pivotTable}.{$column} as pivot_{$column}";
}, $pivotColumns);

$query = $relatedModel->query()
->select(array_merge(
["{$relatedModel->getTable()}.*"],
$pivotSelects
))
->join(
$this->getLastPivotTable(),
"{$this->getLastPivotTable()}.{$this->getLastRelatedKey()}",
'=',
"{$relatedModel->getTable()}.{$relatedModel->getKeyName()}"
)
->where("{$this->getLastPivotTable()}.{$this->getLastForeignKey()}", '=', $this->getKey());

$results = $query->get();
$grouped = [];
foreach ($results as $result) {
$pivot = [];
foreach ($pivotColumns as $column) {
$pivot[$column] = $result["pivot_{$column}"];
unset($result["pivot_{$column}"]);
}
$pivotObj = (object) $pivot;
$result->pivot = $pivotObj;
$grouped[$pivot[$this->getLastForeignKey()]][] = $result;
}

$this->setRelation(
$name,
new Collection($relatedModelClass, $grouped[$this->getKey()] ?? [])
);

$this->setRelation(
$name,
new Collection($relatedModelClass, $grouped[$this->getKey()] ?? [])
);

return $results;
}
return $results;
}

return $relation;
}

if (!isset($this->attributes[$name])) {
throw new \Exception("Property or relation '$name' does not exist on " . static::class);
}

return $this->attributes[$name];
} catch (\Throwable) {
return;
return $relation;
}

return null;
}

/**
Expand Down
1 change: 1 addition & 0 deletions tests/API/Presenter/SQLitePresenterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ protected function setUp(): void
$container = new Container();
$container->bind('request', fn() => new Request());
$container->bind('url', fn() => UrlGenerator::class);
$container->bind('db', fn() => new Database('default'));

$this->pdo = new PDO('sqlite::memory:');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Expand Down
12 changes: 11 additions & 1 deletion tests/Model/CastSystemTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -603,11 +603,21 @@ public function testEnumSetCastSerializesUnitEnum(): void

public function testInvalidEnumValueThrows(): void
{
// __get swallows all Throwables, so test the handler directly
$this->expectException(\ValueError::class);

CastManager::resolve(CastTestColor::class)->get('Purple');
}

public function testInvalidEnumValuePropagatesThroughModelGetter(): void
{
// Model::__get() must let real errors (not just "property missing")
// propagate, instead of silently returning null.
$model = new MockEnumCastModel(['color' => 'Purple']);

$this->expectException(\ValueError::class);

$model->color;
}
public function testCustomCastGetUppercases(): void
{
$model = new MockCustomCastModel(['name' => 'doppar']);
Expand Down
Loading