Add cross-connection join support#234
Conversation
|
Hey Pablo! This looks pretty interesting. With a feature this big, could you possibly create an example repository and comment a link here that uses this branch to do some hands on testing with? |
|
I also don't see documentation on how to use this, especially clarifications on how the connections need to be on the same server. |
Remove the opt-in qualifyWithDatabaseName() API in favour of automatic database.table qualification for MySQL/MariaDB drivers. SQLite is excluded since it requires ATTACH DATABASE for cross-database access.
…nd coverage. Switch the Author model to the primary connection with a real database name, refactor all assertions to reflect automatic database qualification, and add tests for gaps including scopes, withTrashed BelongsTo, MorphTo same-connection, MorphToMany morph type condition, doesntHave MorphMany, powerJoinHas count threshold, and three-level nested orderByPowerJoins.
|
Ok @nathanheffley! I made some changes and improvements, and also added a section to the README. Now I will provide a project with a POC for this. |
…ss-database joins
|
Hey @Pablo1Gustavo ! I'm interested in getting this working for you but the PR is at almost 2k lines and changes A LOT of stuff. I'm not sure that this method of implementation is going to be desirable to merge. I'd love to see that POC so that I can think through alternative implementations. From all these fixes, I'm having some doubts over this specific PR being the best way forward. With a POC and all these tests, we can probably come up with something a bit easier to maintain. |
|
Hello @nathanheffley |
|
Hi! I built the small POC to test the multi-DB connections branch across MySQL, PostgreSQL, and SQLite with cross-connection JOINs: Setup is in the README (a script switches drivers), and the same tests run on each. Results: Notes (non-MySQL/MariaDB):
The branch already works great from the POC’s perspective. Happy to add you as a collaborator if you want to explore it. |
|
Hey @Pablo1Gustavo, we've been quite busy and want to give this the proper attention for the effort you put in. But it's in our list of things to take a look. |
|
Cross-connection joins are one of the most painful parts of working with distributed databases in Laravel. Usually, you're stuck manually prefixing tables or abandoning Eloquent for raw queries. The fact that this PR makes it 'automatic and plug-and-play' is a massive win for the developer experience. Removing the opt-in API in favor of automatic detection shows a lot of confidence in the implementation. Truly excellent work. |
#34
🔗 Cross-Connection Join Support for eloquent-power-joins
📖 Overview
Added support for joining Eloquent relationships that live on different database connections. Previously, the package always used the base query's connection prefix for all joined tables, producing incorrect SQL when the related model used a different connection (and thus a different table prefix).
Cross-connection qualification is now automatic and plug-and-play — no manual configuration required. The package detects the driver and applies the correct behaviour:
database.table)ATTACH DATABASEand does not support the simpledatabase.tablesyntax)🆕 New File:
src/ConnectionAwareTable.phpA helper class that centralizes all cross-connection awareness logic:
isCrossConnection(Model $owner, $baseQuery)— detects when the related model's connection differs from the base query'stableReference(...)— returns a plain string for same-connection joins (grammar handles prefixing as before), or anExpressionwith the related connection's prefix already baked in for cross-connection joinscolumnReference(...)— same logic fortable.columnreferencestableOrAliasReference(...)/columnOrAliasReference(...)— alias-aware variants that check the static alias cache firstrewriteQualifiedColumn(...)— rewrites a"table.col"string to a properly prefixedExpressionqualifiedDatabaseName(Model $model): ?string— returns the database name to use as a qualifier, ornullwhen not applicable (SQLite driver or empty database name). Used internally and byPowerJoinClause✏️ Modified:
src/Mixins/JoinRelationship.php$tablein thenewPowerJoinClauseclosure to allowExpressionobjects to be passedorderByPowerJoinsto useConnectionAwareTable::columnReference()instead ofsprintf('%s.%s', $table, $column), so theORDER BYcolumn reference carries the correct prefix for cross-connection joins🛠️ Modified:
src/PowerJoinClause.phpMultiple fixes to handle
Expressionobjects flowing through string-manipulation methods:$table; extracts the plain table name from anExpressionto populate$this->tableName$this->tableis anExpression, appends the alias to it as a newExpression; otherwise uses the original string formatExpressionfrom$where['column']before callingStr::contains()on soft-delete checksExpressionfrom$where['first']and$where['second']before string replacementsExpressionfrom$where['column']before string replacementsStr::contains()withis_string($columns)to avoid errors when anExpressionis passed"table.col"string into a fully-qualifiedExpressionusingConnectionAwareTable::qualifiedDatabaseName()🧪 Modified:
tests/JoinRelationshipAcrossConnectionsTest.php54 tests covering all major scenarios.
The test setup uses two named MySQL-driver connections (
primary/secondary) with:primary_db/secondary_db)pri_/sec_)SQLite prefix behaviour is tested via a dedicated
:memory:sub-case that verifies the database name is never emitted as a qualifier.📊 Test Coverage
:memory:database name is never emitted as a qualifier