如何自定义 Laravel 的 DatabaseQueryBuilder(制作更好的子查询)

2021-12-26 00:00:00 subquery php laravel eloquent

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

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

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

I found complications, that I can't use scope in subquery and disable soft_delete make me change source code so much.

我希望它是:

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

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

Now, I can add scope, disable soft_delete easily.

我尝试并找到了一个解决方案,即我必须更改 Laravel 的 DatabaseQueryBuilder 代码,函数 whereInSub,第 786 行.

I tried, and found a solution, that I must change Laravel's DatabaseQueryBuilder code, function whereInSub, line 786.

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

到:

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

修改 Laravel 框架的供应商是有害的.所以想问一下怎么安全的做.

It's harmful to modify Laravel framework's vendor. So I want to ask how to do it safely.

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

Sorry because my bad English.

感谢您的阅读.

推荐答案

哦!这是一个相当棘手的问题,因为您的模型将扩展 Eloquent,然后 Eloquent 使用 IlluminateDatabaseQueryBuilder.

Oooh! This is quite a tricky one since your model would extend Eloquent, then Eloquent uses IlluminateDatabaseQueryBuilder.

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

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.

  1. 使用自定义的 whereInSub()IlluminateDatabaseQueryBuilder 扩展到 MyQueryBuilder.
  2. IlluminateDatabaseEloquentModel 扩展到 MyModel 并使其 use 你的 MyQueryBuilder.
  3. app/config/app.php 中的 Eloquent 别名设置为新的 MyModel 类.
  1. Extend IlluminateDatabaseQueryBuilder to MyQueryBuilder with your custom whereInSub().
  2. Extend IlluminateDatabaseEloquentModel to MyModel and make it use your MyQueryBuilder.
  3. Set Eloquent alias in app/config/app.php to your new MyModel class.

像这样:

MyQueryBuilder.php:

use Closure;
use IlluminateSupportCollection;
use IlluminateDatabaseConnectionInterface;
use IlluminateDatabaseQueryGrammarsGrammar;
use IlluminateDatabaseQueryProcessorsProcessor;

class MyQueryBuilder extends IlluminateDatabaseQueryBuilder
{
    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 CarbonCarbon;
use LogicException;
use IlluminateEventsDispatcher;
use IlluminateDatabaseEloquentRelationsPivot;
use IlluminateDatabaseEloquentRelationsHasOne;
use IlluminateDatabaseEloquentRelationsHasMany;
use IlluminateDatabaseEloquentRelationsMorphTo;
use IlluminateSupportContractsJsonableInterface;
use IlluminateSupportContractsArrayableInterface;
use IlluminateDatabaseEloquentRelationsRelation;
use IlluminateDatabaseEloquentRelationsMorphOne;
use IlluminateDatabaseEloquentRelationsMorphMany;
use IlluminateDatabaseEloquentRelationsBelongsTo;
// use IlluminateDatabaseQueryBuilder as QueryBuilder;
use IlluminateDatabaseEloquentRelationsMorphToMany;
use IlluminateDatabaseEloquentRelationsBelongsToMany;
use IlluminateDatabaseEloquentRelationsHasManyThrough;
use IlluminateDatabaseConnectionResolverInterface 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 IlluminateDatabaseEloquentModel
{

}

app/config/app.php:

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

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

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.

相关文章