Add polymorphic join helpers (joinMorph, leftJoinMorph, rightJoinMorph)
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -191,6 +191,104 @@ class RestrictedEloquentBuilder extends Builder
|
|||||||
return $this->whereIn($column, $values, 'or', true);
|
return $this->whereIn($column, $values, 'or', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==========================================
|
||||||
|
* POLYMORPHIC JOIN HELPERS
|
||||||
|
* ==========================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a polymorphic INNER JOIN to the query
|
||||||
|
*
|
||||||
|
* Joins a table with polymorphic columns (e.g., fileable_type, fileable_id)
|
||||||
|
* to the current model's table, automatically handling type_ref conversion.
|
||||||
|
*
|
||||||
|
* @param string $table The table containing the polymorphic columns
|
||||||
|
* @param string $morphName The morph column prefix (e.g., 'fileable' for fileable_type/fileable_id)
|
||||||
|
* @param string|null $morphClass The class name to match. If null, uses current model's class.
|
||||||
|
* @return $this
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Get contacts with their attachments
|
||||||
|
* Contact_Model::query()
|
||||||
|
* ->joinMorph('file_attachments', 'fileable')
|
||||||
|
* ->get();
|
||||||
|
*
|
||||||
|
* // Explicit class (when joining from a different model)
|
||||||
|
* SomeModel::query()
|
||||||
|
* ->joinMorph('file_attachments', 'fileable', Contact_Model::class)
|
||||||
|
* ->get();
|
||||||
|
*/
|
||||||
|
public function joinMorph(string $table, string $morphName, ?string $morphClass = null): self
|
||||||
|
{
|
||||||
|
return $this->_addMorphJoin('join', $table, $morphName, $morphClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a polymorphic LEFT JOIN to the query
|
||||||
|
*
|
||||||
|
* @param string $table The table containing the polymorphic columns
|
||||||
|
* @param string $morphName The morph column prefix (e.g., 'fileable')
|
||||||
|
* @param string|null $morphClass The class name to match. If null, uses current model's class.
|
||||||
|
* @return $this
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Get all contacts, with attachments if they exist
|
||||||
|
* Contact_Model::query()
|
||||||
|
* ->leftJoinMorph('file_attachments', 'fileable')
|
||||||
|
* ->get();
|
||||||
|
*/
|
||||||
|
public function leftJoinMorph(string $table, string $morphName, ?string $morphClass = null): self
|
||||||
|
{
|
||||||
|
return $this->_addMorphJoin('leftJoin', $table, $morphName, $morphClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a polymorphic RIGHT JOIN to the query
|
||||||
|
*
|
||||||
|
* @param string $table The table containing the polymorphic columns
|
||||||
|
* @param string $morphName The morph column prefix (e.g., 'fileable')
|
||||||
|
* @param string|null $morphClass The class name to match. If null, uses current model's class.
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function rightJoinMorph(string $table, string $morphName, ?string $morphClass = null): self
|
||||||
|
{
|
||||||
|
return $this->_addMorphJoin('rightJoin', $table, $morphName, $morphClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper to add a polymorphic join
|
||||||
|
*
|
||||||
|
* @param string $joinMethod The join method to use (join, leftJoin, rightJoin)
|
||||||
|
* @param string $table The table containing the polymorphic columns
|
||||||
|
* @param string $morphName The morph column prefix
|
||||||
|
* @param string|null $morphClass The class name to match
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function _addMorphJoin(string $joinMethod, string $table, string $morphName, ?string $morphClass): self
|
||||||
|
{
|
||||||
|
// Determine the class name to use
|
||||||
|
if ($morphClass === null) {
|
||||||
|
// Use the current model's simple class name
|
||||||
|
$morphClass = class_basename($this->getModel());
|
||||||
|
} else {
|
||||||
|
// Extract simple class name if FQCN was provided
|
||||||
|
$morphClass = class_basename($morphClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the type_ref ID for the class
|
||||||
|
$typeRefId = Type_Ref_Registry::class_to_id($morphClass);
|
||||||
|
|
||||||
|
// Get the current model's table name
|
||||||
|
$baseTable = $this->getModel()->getTable();
|
||||||
|
|
||||||
|
// Build the join
|
||||||
|
return $this->$joinMethod($table, function ($join) use ($table, $baseTable, $morphName, $typeRefId) {
|
||||||
|
$join->on("{$table}.{$morphName}_id", '=', "{$baseTable}.id")
|
||||||
|
->where("{$table}.{$morphName}_type", '=', $typeRefId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent eager loading via with()
|
* Prevent eager loading via with()
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -108,6 +108,35 @@ File_Attachment_Model::where('fileable_type', 42)->get();
|
|||||||
|
|
||||||
Supported methods: `where()`, `orWhere()`, `whereIn()`, `orWhereIn()`, `whereNotIn()`, `orWhereNotIn()`
|
Supported methods: `where()`, `orWhere()`, `whereIn()`, `orWhereIn()`, `whereNotIn()`, `orWhereNotIn()`
|
||||||
|
|
||||||
|
## Polymorphic Join Helpers
|
||||||
|
|
||||||
|
Join tables with polymorphic columns using dedicated helpers:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Get contacts that have attachments (INNER JOIN)
|
||||||
|
Contact_Model::query()
|
||||||
|
->joinMorph('file_attachments', 'fileable')
|
||||||
|
->select('contacts.*', 'file_attachments.filename')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Get all contacts, with attachments if they exist (LEFT JOIN)
|
||||||
|
Contact_Model::query()
|
||||||
|
->leftJoinMorph('file_attachments', 'fileable')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Explicit class (when querying from a different model)
|
||||||
|
SomeModel::query()
|
||||||
|
->leftJoinMorph('file_attachments', 'fileable', Contact_Model::class)
|
||||||
|
->get();
|
||||||
|
```
|
||||||
|
|
||||||
|
Available methods: `joinMorph()`, `leftJoinMorph()`, `rightJoinMorph()`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `$table` - Table with polymorphic columns (e.g., `'file_attachments'`)
|
||||||
|
- `$morphName` - Column prefix (e.g., `'fileable'` for `fileable_type`/`fileable_id`)
|
||||||
|
- `$morphClass` - Optional class name (defaults to current model)
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
- **Simple Names Only**: Always use simple class names (`Contact_Model`), never FQCNs
|
- **Simple Names Only**: Always use simple class names (`Contact_Model`), never FQCNs
|
||||||
|
|||||||
@@ -130,6 +130,66 @@ LARAVEL MORPH MAP INTEGRATION
|
|||||||
The morph map uses simple class names (e.g., "Contact_Model") not fully
|
The morph map uses simple class names (e.g., "Contact_Model") not fully
|
||||||
qualified names, matching how RSX models work throughout the framework.
|
qualified names, matching how RSX models work throughout the framework.
|
||||||
|
|
||||||
|
QUERY BUILDER INTEGRATION
|
||||||
|
Transparent WHERE Clauses
|
||||||
|
|
||||||
|
The query builder automatically converts type_ref columns in WHERE
|
||||||
|
clauses. You can use class name strings directly:
|
||||||
|
|
||||||
|
// All of these work - class names auto-converted to IDs
|
||||||
|
File_Attachment_Model::where('fileable_type', 'Contact_Model')->get();
|
||||||
|
File_Attachment_Model::where('fileable_type', '=', 'Contact_Model')->get();
|
||||||
|
File_Attachment_Model::where(['fileable_type' => 'Contact_Model'])->get();
|
||||||
|
|
||||||
|
// whereIn also works
|
||||||
|
File_Attachment_Model::whereIn('fileable_type', [
|
||||||
|
'Contact_Model',
|
||||||
|
'Project_Model'
|
||||||
|
])->get();
|
||||||
|
|
||||||
|
// Integer IDs still work (pass-through)
|
||||||
|
File_Attachment_Model::where('fileable_type', 42)->get();
|
||||||
|
|
||||||
|
Supported methods: where(), orWhere(), whereIn(), orWhereIn(),
|
||||||
|
whereNotIn(), orWhereNotIn()
|
||||||
|
|
||||||
|
Polymorphic Join Helpers
|
||||||
|
|
||||||
|
Join tables with polymorphic columns using dedicated helpers:
|
||||||
|
|
||||||
|
// Get contacts that have attachments (INNER JOIN)
|
||||||
|
Contact_Model::query()
|
||||||
|
->joinMorph('file_attachments', 'fileable')
|
||||||
|
->select('contacts.*', 'file_attachments.filename')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Get all contacts, with attachments if they exist (LEFT JOIN)
|
||||||
|
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 class name (defaults to current model)
|
||||||
|
|
||||||
|
Explicit class (when querying from a different model context):
|
||||||
|
|
||||||
|
SomeModel::query()
|
||||||
|
->leftJoinMorph('file_attachments', 'fileable', Contact_Model::class)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
The generated SQL is equivalent to:
|
||||||
|
|
||||||
|
LEFT JOIN file_attachments
|
||||||
|
ON file_attachments.fileable_id = contacts.id
|
||||||
|
AND file_attachments.fileable_type = <type_ref_id>
|
||||||
|
|
||||||
FORM HANDLING
|
FORM HANDLING
|
||||||
Client-Side Format
|
Client-Side Format
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user