How to customize Laravel's Database\Query\Builder (make better subquery)

eloquent, laravel, php, subquery

Solution

Oooh! This is quite a tricky one since your model would extend `Eloquent`, then `Eloquent` uses `Illuminate\Database\Query\Builder`.

But what I noticed is that `Eloquent` is actually an alias in `app/config/app.php` file. So what you can do is following these steps.

- Extend `Illuminate\Database\Query\Builder` to `MyQueryBuilder` with your custom `whereInSub()`.

- Extend `Illuminate\Database\Eloquent\Model` to `MyModel` and make it `use` your `MyQueryBuilder`.

- Set `Eloquent` alias in `app/config/app.php` to your new `MyModel` class.

Something like this:

MyQueryBuilder.php:

use Closure;
use Illuminate\Support\Collection;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Query\Grammars\Grammar;
use Illuminate\Database\Query\Processors\Processor;

class MyQueryBuilder extends Illuminate\Database\Query\Builder
{
    protected function whereInSub($column, Closure $callback, $boolean, $not)
    {
        $type = $not ? 'NotInSub' : 'InSub';

        $query = $this->newQuery(); // Your changes
        call_user_func_array($callback, array(&$query)); // Your changes

        $this->wheres[] = compact('type', 'column', 'query', 'boolean');

        $this->mergeBindings($query);

        return $this;
    }
}

MyModel.php:

use DateTime;
use ArrayAccess;
use Carbon\Carbon;
use LogicException;
use Illuminate\Events\Dispatcher;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Contracts\JsonableInterface;
use Illuminate\Support\Contracts\ArrayableInterface;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
// use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\ConnectionResolverInterface as Resolver;
use MyQueryBuilder as QueryBuilder; // MyModel should now use your MyQueryBuilder instead of the default which I commented out above

abstract class MyModel extends Illuminate\Database\Eloquent\Model
{

}

app/config/app.php:

'aliases' => array(
    ...
    'Eloquent'        => 'MyModel',
    ...
);

Note that I put long lists of `use` up there because "use" keyword does not get inherited. Also I did not put `MyQueryBuilder` and `MyModel` in a namespace for the sake of simplicity. My `use` list might also be different from yours depending on Laravel versions we use, so please check the uses too.

Problem

I'm working on Laravel 4. As I knew, I can do subquery: ``` Project::whereIn('project_id', function($q) { $q->select('project_id') ->from('company') ->whereNull('deleted_at'); }); ``` I found complications, that I can't use scope in subquery and disable soft_delete make me change source code so much. I wish it was: ``` Project::whereIn('project_id', function(&$q) { $q = Company::select('project_id')->getQuery(); }); ``` Now, I can add scope, disable soft_delete easily. I tried, and found a solution, that I must change Laravel's Database\Query\Builder code, function whereInSub, line 786. ``` call_user_func($callback, $query = $this->newQuery()); ``` to: ``` $query = $this->newQuery(); call_user_func_array($callback, array(&$query)); ``` It's harmful to modify Laravel framework's vendor. So I want to ask how to do it safely. Sorry because my bad English. Thank you for reading.

Original source

Related problems