Add form value persistence across cache revalidation re-renders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
237 lines
5.2 KiB
Markdown
Executable File
237 lines
5.2 KiB
Markdown
Executable File
---
|
|
name: polymorphic
|
|
description: RSX polymorphic relationships with type references storing integers instead of class names. Use when implementing morphTo relationships, defining type_ref_columns, handling polymorphic form fields, or using polymorphic join helpers.
|
|
---
|
|
|
|
# RSX Polymorphic Relationships
|
|
|
|
## Overview
|
|
|
|
RSX uses a type reference system that stores **integers** in the database but transparently converts to/from class name strings in PHP.
|
|
|
|
```php
|
|
$activity->eventable_type = 'Contact_Model'; // Stores integer in DB
|
|
echo $activity->eventable_type; // Returns "Contact_Model"
|
|
```
|
|
|
|
**Benefits**:
|
|
- Efficient integer storage (not VARCHAR class names)
|
|
- Automatic type discovery
|
|
- Transparent conversion
|
|
- Laravel morphTo() compatibility
|
|
|
|
---
|
|
|
|
## Defining Type Reference Columns
|
|
|
|
Declare which columns are type references in your model:
|
|
|
|
```php
|
|
class Activity_Model extends Rsx_Model_Abstract
|
|
{
|
|
protected static $type_ref_columns = ['eventable_type'];
|
|
|
|
public function eventable()
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
}
|
|
```
|
|
|
|
The cast is automatically applied - no manual `$casts` needed.
|
|
|
|
---
|
|
|
|
## Database Schema
|
|
|
|
Type reference columns must be **BIGINT**, not VARCHAR:
|
|
|
|
```sql
|
|
CREATE TABLE activities (
|
|
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
eventable_type BIGINT NULL,
|
|
eventable_id BIGINT NULL,
|
|
action VARCHAR(50) NOT NULL,
|
|
INDEX idx_eventable (eventable_type, eventable_id)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Usage
|
|
|
|
### Setting Values
|
|
|
|
```php
|
|
$activity = new Activity_Model();
|
|
$activity->eventable_type = 'Contact_Model'; // Use class name
|
|
$activity->eventable_id = 123;
|
|
$activity->save();
|
|
```
|
|
|
|
### Reading Values
|
|
|
|
```php
|
|
echo $activity->eventable_type; // "Contact_Model" (string)
|
|
$related = $activity->eventable; // Returns Contact_Model instance
|
|
```
|
|
|
|
### Querying
|
|
|
|
Class names are automatically converted to IDs in WHERE clauses:
|
|
|
|
```php
|
|
// All work - class names auto-converted
|
|
Activity_Model::where('eventable_type', 'Contact_Model')->get();
|
|
Activity_Model::whereIn('eventable_type', ['Contact_Model', 'Project_Model'])->get();
|
|
```
|
|
|
|
---
|
|
|
|
## Polymorphic Join Helpers
|
|
|
|
Join tables with polymorphic columns:
|
|
|
|
```php
|
|
// INNER JOIN - contacts that have attachments
|
|
Contact_Model::query()
|
|
->joinMorph('file_attachments', 'fileable')
|
|
->select('contacts.*', 'file_attachments.filename')
|
|
->get();
|
|
|
|
// LEFT JOIN - all contacts, with attachments if they exist
|
|
Contact_Model::query()
|
|
->leftJoinMorph('file_attachments', 'fileable')
|
|
->get();
|
|
|
|
// RIGHT JOIN
|
|
Contact_Model::query()
|
|
->rightJoinMorph('file_attachments', 'fileable')
|
|
->get();
|
|
```
|
|
|
|
**Parameters**:
|
|
- `$table` - Table with polymorphic columns (e.g., 'file_attachments')
|
|
- `$morphName` - Column prefix (e.g., 'fileable' for fileable_type/fileable_id)
|
|
- `$morphClass` - Optional explicit class (defaults to current model)
|
|
|
|
---
|
|
|
|
## Form Handling
|
|
|
|
### Client-Side Format
|
|
|
|
Polymorphic fields submit as JSON:
|
|
|
|
```javascript
|
|
eventable={"model":"Contact_Model","id":123}
|
|
```
|
|
|
|
### Server-Side Parsing
|
|
|
|
```php
|
|
use App\RSpade\Core\Polymorphic_Field_Helper;
|
|
|
|
#[Ajax_Endpoint]
|
|
public static function save(Request $request, array $params = [])
|
|
{
|
|
$eventable = Polymorphic_Field_Helper::parse($params['eventable'], [
|
|
Contact_Model::class,
|
|
Project_Model::class,
|
|
]);
|
|
|
|
// Validate
|
|
if ($error = $eventable->validate('Please select an entity')) {
|
|
return response_error(Ajax::ERROR_VALIDATION, ['eventable' => $error]);
|
|
}
|
|
|
|
// Use
|
|
$activity = new Activity_Model();
|
|
$activity->eventable_type = $eventable->model; // "Contact_Model"
|
|
$activity->eventable_id = $eventable->id; // 123
|
|
$activity->save();
|
|
}
|
|
```
|
|
|
|
**Important**: Always use `Model::class` for the whitelist.
|
|
|
|
---
|
|
|
|
## Auto-Discovery
|
|
|
|
When storing a new class name that isn't in `_type_refs` yet:
|
|
|
|
```php
|
|
$attachment->fileable_type = 'Custom_Model';
|
|
$attachment->save();
|
|
```
|
|
|
|
RSX will:
|
|
1. Verify `Custom_Model` exists and extends `Rsx_Model_Abstract`
|
|
2. Create a new `_type_refs` entry with next available ID
|
|
3. Store that ID in the column
|
|
|
|
Any model can be used without pre-registration.
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### File Attachments to Multiple Models
|
|
|
|
```php
|
|
class File_Attachment_Model extends Rsx_Model_Abstract
|
|
{
|
|
protected static $type_ref_columns = ['fileable_type'];
|
|
|
|
public function fileable()
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
}
|
|
|
|
// Attach to contact
|
|
$attachment->fileable_type = 'Contact_Model';
|
|
$attachment->fileable_id = $contact->id;
|
|
|
|
// Attach to project
|
|
$attachment->fileable_type = 'Project_Model';
|
|
$attachment->fileable_id = $project->id;
|
|
```
|
|
|
|
### Activity Log
|
|
|
|
```php
|
|
class Activity_Model extends Rsx_Model_Abstract
|
|
{
|
|
protected static $type_ref_columns = ['subject_type'];
|
|
|
|
public function subject()
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
}
|
|
|
|
// Log activity for any model
|
|
Activity_Model::log('updated', $contact); // subject_type = 'Contact_Model'
|
|
Activity_Model::log('created', $project); // subject_type = 'Project_Model'
|
|
```
|
|
|
|
---
|
|
|
|
## Simple Names Only
|
|
|
|
Always use simple class names (basename), never FQCNs:
|
|
|
|
```php
|
|
// ✅ Correct
|
|
$activity->eventable_type = 'Contact_Model';
|
|
|
|
// ❌ Wrong - fully qualified
|
|
$activity->eventable_type = 'App\\Models\\Contact_Model';
|
|
```
|
|
|
|
## More Information
|
|
|
|
Details: `php artisan rsx:man polymorphic`
|