-
Notifications
You must be signed in to change notification settings - Fork 97
Description
Bug Description
Since version 4.2.9 (commit ccda351), the package throws a TypeError when trying to use leftJoinRelationship() or orderByLeftPowerJoins() on queries that use fromRaw() with a subquery or CTE (Common Table Expression).
Error Message
TypeError: preg_match(): Argument #2 ($subject) must be of type string, Illuminate\Database\Query\Expression givenStack trace location:
vendor/kirschbaum-development/eloquent-power-joins/src/Mixins/JoinRelationship.php:113
Root Cause
The issue was introduced in commit ccda351 which added logic to use the alias from the from method.
However, when using fromRaw(), the from property contains an Illuminate\Database\Query\Expression object instead of a string, which causes preg_match() to fail with a TypeError.
Minimal Reproducible Example
1. Define models with a relationship
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class, 'user_id');
}
public function scopeWithCTE($query)
{
return $query->fromRaw("(
WITH active_posts AS (
SELECT * FROM posts WHERE status = 'published'
)
SELECT * FROM active_posts
) as posts");
}
}
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}2. Try to use PowerJoins on a query with fromRaw()
// This will throw TypeError
Post::withCTE()
->leftJoinRelationship('author')
->get();
// OR this will also throw TypeError
Post::withCTE()
->orderByLeftPowerJoins('author.name', 'asc')
->get();3. Result
TypeError: preg_match(): Argument #2 ($subject) must be of type string, Illuminate\Database\Query\Expression given
Expected Behavior
The package should handle Expression objects gracefully by:
- Detecting when
fromis anExpressionand skipping the alias logic - Converting the
Expressionto a string before usingpreg_match() - Falling back to default behavior when unable to extract an alias
Actual Behavior
The package crashes with a TypeError because preg_match() receives an Expression object instead of a string.
Current Workaround
The only workaround is to manually perform the join instead of using PowerJoins:
$query = Post::withCTE();
// Instead of: $query->leftJoinRelationship('author')
// Do this manually:
$query->leftJoin('users', 'users.id', '=', 'posts.user_id');
$results = $query->get();Suggested Fix
Add a type check before using preg_match() in src/Mixins/JoinRelationship.php around line 113:
// Current code (simplified)
if (preg_match('/some_pattern/', $from, $matches)) {
// ... logic
}
// Suggested fix
if ($from instanceof \Illuminate\Database\Query\Expression) {
// Skip alias extraction for Expression objects
$from = null; // or handle appropriately
}
if ($from && preg_match('/some_pattern/', $from, $matches)) {
// ... logic
}Use Cases Affected
This issue affects any query that uses fromRaw(), which is commonly used for:
- CTEs (Common Table Expressions) - Complex recursive or multi-step queries
- Complex subqueries - Performance optimization scenarios
- Raw SQL queries - When Eloquent query builder is insufficient
- Union queries with aliases - Combining multiple result sets
Impact
⚠️ Breaking change introduced in 4.2.9⚠️ Affects production applications using CTEs or complex raw queries⚠️ Forces users to pin package to 4.2.8 or write manual join logic