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);
|
||||
}
|
||||
|
||||
/**
|
||||
* ==========================================
|
||||
* 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()
|
||||
*
|
||||
|
||||
@@ -108,6 +108,35 @@ 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:
|
||||
|
||||
```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
|
||||
|
||||
- **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
|
||||
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
|
||||
Client-Side Format
|
||||
|
||||
|
||||
Reference in New Issue
Block a user