如何自定义Laravel的Database\Query\Builder(制作更好的子查询)

Lê *_*ung 9 php subquery laravel eloquent

我正在研究Laravel 4.据我所知,我可以做子查询:

Project::whereIn('project_id', function($q) {
    $q->select('project_id')
        ->from('company')
        ->whereNull('deleted_at');
});
Run Code Online (Sandbox Code Playgroud)

我发现了并发症,我无法在子查询中使用范围并禁用soft_delete让我更改源代码.

我希望它是:

Project::whereIn('project_id', function(&$q) {
    $q = Company::select('project_id')->getQuery();
});
Run Code Online (Sandbox Code Playgroud)

现在,我可以添加范围,轻松禁用soft_delete.

我试过,并找到了一个解决方案,我必须更改Laravel的Database\Query\Builder代码,函数whereInSub,第786行.

call_user_func($callback, $query = $this->newQuery());
Run Code Online (Sandbox Code Playgroud)

至:

$query = $this->newQuery();
call_user_func_array($callback, array(&$query));
Run Code Online (Sandbox Code Playgroud)

修改Laravel框架的供应商是有害的.所以我想问一下如何安全地做到这一点.

抱歉,因为我的英语不好.

谢谢你的阅读.

Unn*_*wut 13

哦!这是一个非常棘手的问题,因为你的模型会延伸Eloquent,然后Eloquent使用Illuminate\Database\Query\Builder.

但我注意到的是,Eloquent它实际上是app/config/app.php文件中的别名.所以你可以做的是遵循这些步骤.

  1. 扩展Illuminate\Database\Query\BuilderMyQueryBuilder您的自定义whereInSub().
  2. 延伸Illuminate\Database\Eloquent\ModelMyModel并成为use你的MyQueryBuilder.
  3. Eloquent别名设置app/config/app.php为新MyModel类.

像这样的东西:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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
{

}
Run Code Online (Sandbox Code Playgroud)

应用程序/配置/ app.php:

'aliases' => array(
    ...
    'Eloquent'        => 'MyModel',
    ...
);
Run Code Online (Sandbox Code Playgroud)

请注意,我use在那里放了很长的列表,因为"use"关键字不会被继承.我也没把MyQueryBuilderMyModel为简单起见命名空间.use根据我们使用的Laravel版本,我的列表可能与您的列表不同,所以请检查用途.

  • 我编辑了我的问题,因为我找到了解决方案.需要在新类MyModel中覆盖受保护的函数newBaseQueryBuilder() (4认同)

小智 5

您可以创建自定义查询构建器并像这样使用它。

我的自定义查询生成器:

use Illuminate\Database\Query\Builder as QueryBuilder;

class MyCustomBuilder extends QueryBuilder{

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在任何模型中覆盖方法 newBaseQueryBuilder() 并返回您自己的查询构建器的实例

class MyModel extends Model{
   protected function newBaseQueryBuilder()
   {
      $connection = $this->getConnection();

      return new MyQueryBuilder(
           $connection, $connection->getQueryGrammar(), 
              $connection->getPostProcessor()
    );
}
}
Run Code Online (Sandbox Code Playgroud)