如何自定义 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
How to customize Laravel's Database\Query\Builder (make better subquery)
提问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 Eloquent
uses Illuminate\Database\Query\Builder
.
哦!这是一个相当棘手的问题,因为您的模型会扩展Eloquent
,然后Eloquent
使用Illuminate\Database\Query\Builder
.
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.
但我注意到它Eloquent
实际上是app/config/app.php
文件中的别名。所以你可以做的是遵循这些步骤。
- Extend
Illuminate\Database\Query\Builder
toMyQueryBuilder
with your customwhereInSub()
. - Extend
Illuminate\Database\Eloquent\Model
toMyModel
and make ituse
yourMyQueryBuilder
. - Set
Eloquent
alias inapp/config/app.php
to your newMyModel
class.
- 扩展
Illuminate\Database\Query\Builder
到MyQueryBuilder
您的自定义whereInSub()
. - 扩展
Illuminate\Database\Eloquent\Model
到MyModel
并使其成为use
您的MyQueryBuilder
. - 为您的新班级设置
Eloquent
别名。app/config/app.php
MyModel
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 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.
请注意,我在上面放了很长的列表,use
因为“use”关键字不会被继承。此外,为了简单起见,我没有将MyQueryBuilder
和MyModel
放在命名空间中。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()
);
}
}