diff --git a/app/RSpade/Core/Database/Models/Rsx_Model_Abstract.php b/app/RSpade/Core/Database/Models/Rsx_Model_Abstract.php index bfb206011..fed1a6486 100644 --- a/app/RSpade/Core/Database/Models/Rsx_Model_Abstract.php +++ b/app/RSpade/Core/Database/Models/Rsx_Model_Abstract.php @@ -425,6 +425,40 @@ abstract class Rsx_Model_Abstract extends Model */ protected static $type_ref_columns = []; + /** + * Get a polymorphic parent using type_ref_columns + * + * Laravel's morphTo() reads raw attributes, bypassing accessors. + * Since type_ref_columns stores integers but accessors return class names, + * morphTo() crashes. Use this helper instead. + * + * Usage in model: + * public function getParentAttribute() { + * return $this->get_polymorphic_parent('parent_type', 'parent_id'); + * } + * + * @param string $type_column Column storing the type (e.g., 'parent_type') + * @param string $id_column Column storing the ID (e.g., 'parent_id') + * @return \Illuminate\Database\Eloquent\Model|null + */ + protected function get_polymorphic_parent(string $type_column, string $id_column) + { + $type = $this->$type_column; // Uses accessor - gets class name + $id = $this->$id_column; + + if (!$type || !$id) { + return null; + } + + // Resolve through morph map (e.g., 'Task_Model' → 'Rsx\Models\Task_Model') + $class = \Illuminate\Database\Eloquent\Relations\Relation::getMorphedModel($type); + if (!$class) { + return null; + } + + return $class::find($id); + } + /** * Get the casts array with automatic type casting based on database schema * diff --git a/docs/CLAUDE.dist.md b/docs/CLAUDE.dist.md index f5171fdc6..979ff6b74 100644 --- a/docs/CLAUDE.dist.md +++ b/docs/CLAUDE.dist.md @@ -828,18 +828,27 @@ Pattern recognition: ## POLYMORPHIC TYPE REFERENCES -Polymorphic `*_type` columns (fileable_type, taskable_type, etc.) store BIGINT integers in the database for efficiency. The framework maps between integer IDs and class names transparently. +Polymorphic `*_type` columns store BIGINT integers. Framework maps to class names transparently. ```php -// Model: Declare type ref columns protected static $type_ref_columns = ['fileable_type']; -// Usage: Code works with class name strings - conversion is automatic -$attachment->fileable_type = 'Contact_Model'; // Stored as integer in DB +$attachment->fileable_type = 'Contact_Model'; // Stored as integer echo $attachment->fileable_type; // Reads as 'Contact_Model' ``` -**Simple Names Only**: Always use `class_basename($model)`, never `get_class($model)` (FQCNs) +**NEVER USE morphTo()**: Laravel's `morphTo()` bypasses accessors, receives integers, crashes. Use `get_polymorphic_parent()`: +```php +// ❌ WRONG +public function parent() { return $this->morphTo(...); } + +// ✅ CORRECT +public function getParentAttribute() { + return $this->get_polymorphic_parent('parent_type', 'parent_id'); +} +``` + +**Simple Names Only**: Use `class_basename($model)`, never `get_class($model)` Details: `php artisan rsx:man polymorphic`