【问题标题】:How to customize Laravel's Database\Query\Builder (make better subquery)如何自定义 Laravel Database\Query\Builder(制作更好的子查询)
【发布时间】:2014-08-24 15:43:55
【问题描述】:

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

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

我发现了一些复杂情况,即我不能在子查询中使用范围并禁用 soft_delete 使我对源代码进行了如此多的更改。

我希望是:

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

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

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

call_user_func($callback, $query = $this->newQuery());

到:

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

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

对不起,我的英语不好。

感谢您的阅读。

【问题讨论】:

    标签: php laravel subquery eloquent


    【解决方案1】:

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

    但我注意到Eloquent 实际上是app/config/app.php 文件中的别名。因此,您可以按照以下步骤操作。

    1. 使用您的自定义whereInSub()Illuminate\Database\Query\Builder 扩展到MyQueryBuilder
    2. Illuminate\Database\Eloquent\Model 扩展为MyModel,并将use 设为您的MyQueryBuilder
    3. app/config/app.php 中的Eloquent 别名设置为新的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;
        }
    }
    

    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',
        ...
    );
    

    请注意,我在上面放了很长的use 列表,因为"use" keyword does not get inherited。另外,为了简单起见,我没有将 MyQueryBuilderMyModel 放在命名空间中。我的use 列表也可能与你的不同,这取决于我们使用的 Laravel 版本,所以请也检查一下用途。

    【讨论】:

    • 我编辑了我的问题,因为我找到了解决方案。需要在新类 MyModel 中覆盖受保护的函数 newBaseQueryBuilder()
    • 不需要包含“使用”列表,除非您实际上是在......在您的子类中使用它们。在这种情况下,您只需要 MyQueryBuilder,正如@LêTrầnTiếnTrung 准确指出的那样,它将在 newBaseQueryBuilder() 的覆盖版本中使用。
    • @Unnawut 你能帮我解决这个问题吗? stackoverflow.com/q/65508697/1830228
    【解决方案2】:

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

    我的自定义查询生成器:

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

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

    class MyModel extends Model{
       protected function newBaseQueryBuilder()
       {
          $connection = $this->getConnection();
    
          return new MyQueryBuilder(
               $connection, $connection->getQueryGrammar(), 
                  $connection->getPostProcessor()
        );
    }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-02
    • 2014-04-25
    • 2018-10-03
    • 2016-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-20
    相关资源
    最近更新 更多