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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24555025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 09:45:46  来源:igfitidea点击:

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

phplaravelsubqueryeloquent

提问by Lê Tr?n Ti?n Trung

I'm working on Laravel 4. As I knew, I can do subquery:

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

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.

我发现了一些复杂情况,即我无法在子查询中使用作用域并禁用 soft_delete 使我对源代码进行了大量更改。

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.

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

I tried, and found a solution, that I must change Laravel's Database\Query\Builder code, function whereInSub, line 786.

我尝试并找到了一个解决方案,即我必须更改 Laravel 的 Database\Query\Builder 代码,函数 whereInSub,第 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.

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

Sorry because my bad English.

对不起,因为我的英语不好。

Thank you for reading.

感谢您的阅读。

回答by Unnawut

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

哦!这是一个相当棘手的问题,因为您的模型会扩展Eloquent,然后Eloquent使用Illuminate\Database\Query\Builder.

But what I noticed is that Eloquentis actually an alias in app/config/app.phpfile. So what you can do is following these steps.

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

  1. Extend Illuminate\Database\Query\Builderto MyQueryBuilderwith your custom whereInSub().
  2. Extend Illuminate\Database\Eloquent\Modelto MyModeland make it useyour MyQueryBuilder.
  3. Set Eloquentalias in app/config/app.phpto your new MyModelclass.
  1. 扩展Illuminate\Database\Query\BuilderMyQueryBuilder您的自定义whereInSub().
  2. 扩展Illuminate\Database\Eloquent\ModelMyModel并使其成为use您的MyQueryBuilder.
  3. 为您的新班级设置Eloquent别名。app/config/app.phpMyModel

Something like this:

像这样的东西:

MyQueryBuilder.php:

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:

我的模型.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:

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

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

Note that I put long lists of useup there because "use" keyword does not get inherited. Also I did not put MyQueryBuilderand MyModelin a namespace for the sake of simplicity. My uselist might also be different from yours depending on Laravel versions we use, so please check the uses too.

请注意,我在上面放了很长的列表,use因为“use”关键字不会被继承。此外,为了简单起见,我没有将MyQueryBuilderMyModel放在命名空间中。use根据我们使用的 Laravel 版本,我的列表也可能与您的不同,因此请检查用途。

回答by Jetmir Haxhisefa

You can create a custom query builder and use it like this.

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

My Custom Query Builder:

我的自定义查询生成器:

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;
    }
}

In any Model overwrite the method newBaseQueryBuilder() and return an instance of your own query builder

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

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

      return new MyQueryBuilder(
           $connection, $connection->getQueryGrammar(), 
              $connection->getPostProcessor()
    );
}
}