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

时间:2014-07-03 13:29:30

标签: php laravel subquery eloquent

我正在研究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框架的供应商是有害的。所以我想问一下如何安全地做到这一点。

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

感谢您的阅读。

2 个答案:

答案 0 :(得分:13)

哦!这非常棘手,因为您的模型会扩展Eloquent,然后Eloquent会使用Illuminate\Database\Query\Builder

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

  1. 使用您的自定义Illuminate\Database\Query\BuilderMyQueryBuilder扩展为whereInSub()
  2. Illuminate\Database\Eloquent\Model扩展为MyModel并将use MyQueryBuilder Eloquent
  3. app/config/app.php中的MyModel别名设置为新的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; } } 课程。
  4. 这样的事情:

    <强> MyQueryBuilder.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
    {
    
    }
    

    <强> MyModel.php:

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

    应用/配置/ app.php:

    use

    请注意,由于"use" keyword does not get inherited,我在其中列出了MyQueryBuilder长列表。另外,为简单起见,我没有将MyModeluse放在命名空间中。根据我们使用的Laravel版本,我的{{1}}列表可能与您的列表不同,因此请检查用途。

答案 1 :(得分: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()
    );
}
}