Skip to content

TypeError with fromRaw() when using alias from 'from' method (v4.2.9+) #225

@WHeesters

Description

@WHeesters

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 given

Stack 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 from is an Expression and skipping the alias logic
  • Converting the Expression to a string before using preg_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

Additional Information

  • Works perfectly in version 4.2.8 and earlier
  • Regression introduced by commit ccda351
  • Related to PR #224 - "Using alias from the 'from' method when set"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions