我想在 whereHas 调用中删除 EmpresaScope 全局范围:
// $tipoTreinamento is a Eloquent model
$tipoTreinamento->with(['funcaoEmpresa' => function($relation){
// Here it works
(new EmpresaScope())->remove($relation->getQuery(), $relation->getRelated());
}])
->whereHas('funcaoEmpresa', function($query) use ($colaborador){
// Here it doesn't
(new EmpresaScope())->remove($query, $query->getModel());
$query->where('id', $colaborador['funcao_empresa_id']);
});
不幸的是,似乎 with 和 whereHas 方法并没有以类似的方式使用它的闭包。
有人知道实现它的方法吗?
这是删除实现
class EmpresaScope implements ScopeInterface
{
...
public function remove(Builder $builder, Model $model)
{
// Reference https://laracasts.com/discuss/channels/eloquent/need-help-writing-remove-for-global-query-scope
$query = $builder->getQuery();
$bindingKey = 0;
foreach ($query->wheres as $i => $where)
{
if ($where['column'] == $model['table'].'.empresa_id')
{
// Remove o where do scope
unset($query->wheres[$i]);
// Remove o valor que entraria no where
$bindings = $query->getBindings();
unset($bindings[$bindingKey]);
$query->setBindings($bindings);
break;
}
if ( ! in_array($where['type'], ['Null', 'NotNull'])) $bindingKey++;
}
}
}
答案 0 :(得分:1)
我能够通过覆盖相关方法来解决这个问题。我已经创造了一个易于使用的特性。
<?php
namespace App;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
trait UnscopedRelations
{
/**
* Define an inverse one-to-one or many relationship.
*
* @param string $related
* @param string $foreignKey
* @param string $otherKey
* @param string $relation
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null, array $removeScopes = [])
{
// If no relation name was given, we will use this debug backtrace to extract
// the calling method's name and use that as the relationship name as most
// of the time this will be what we desire to use for the relationships.
if (is_null($relation)) {
list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$relation = $caller['function'];
}
// If no foreign key was supplied, we can use a backtrace to guess the proper
// foreign key name by using the name of the relationship function, which
// when combined with an "_id" should conventionally match the columns.
if (is_null($foreignKey)) {
$foreignKey = Str::snake($relation).'_id';
}
$instance = new $related;
// Once we have the foreign key names, we'll just create a new Eloquent query
// for the related models and returns the relationship instance which will
// actually be responsible for retrieving and hydrating every relations.
$query = $instance->newQueryWithoutScopes();
foreach ($instance->getGlobalScopes() as $identifier => $scope) {
if(in_array($scope, $removeScopes)) {
continue;
}
$query->withGlobalScope($identifier, $scope);
}
$otherKey = $otherKey ?: $instance->getKeyName();
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
}
/**
* Define a one-to-many relationship.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function hasMany($related, $foreignKey = null, $localKey = null, array $removeScopes = [])
{
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$query = $instance->newQueryWithoutScopes();
foreach ($instance->getGlobalScopes() as $identifier => $scope) {
if(in_array($scope, $removeScopes)) {
continue;
}
$query->withGlobalScope($identifier, $scope);
}
$localKey = $localKey ?: $this->getKeyName();
return new HasMany($query, $this, $instance->getTable().'.'.$foreignKey, $localKey);
}
/**
* Define a many-to-many relationship.
*
* @param string $related
* @param string $table
* @param string $foreignKey
* @param string $otherKey
* @param string $relation
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null, array $removeScopes = [])
{
// If no relationship name was passed, we will pull backtraces to get the
// name of the calling function. We will use that function name as the
// title of this relation since that is a great convention to apply.
if (is_null($relation)) {
$relation = $this->getBelongsToManyCaller();
}
// First, we'll need to determine the foreign key and "other key" for the
// relationship. Once we have determined the keys we'll make the query
// instances as well as the relationship instances we need for this.
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$otherKey = $otherKey ?: $instance->getForeignKey();
// If no table name was provided, we can guess it by concatenating the two
// models using underscores in alphabetical order. The two model names
// are transformed to snake case from their default CamelCase also.
if (is_null($table)) {
$table = $this->joiningTable($related);
}
// Now we're ready to create a new query builder for the related model and
// the relationship instances for the relation. The relations will set
// appropriate query constraint and entirely manages the hydrations.
$query = $instance->newQueryWithoutScopes();
foreach ($instance->getGlobalScopes() as $identifier => $scope) {
if(in_array($scope, $removeScopes)) {
continue;
}
$query->withGlobalScope($identifier, $scope);
}
return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation);
}
}
在模型中,只需在构建关系时包含修改范围所需的特征。
class TipoTreinamento extends Model
{
use UnscopedRelations;
public function FuncaoEmpresa()
{
return $this->hasMany(FuncaoEmpresa::class, null, null, null, [new EmpresaScope]);
}
}
这是如何工作的是您在belongsTo
,hasMany
,hasOne
或belongsToMany
函数的最后一个参数中传递的范围,这些范围将从查询。
我建议您创建其他相关方法,例如FuncaoEmpresaWithoutEmpressaScope()
,然后在需要的with
方法中传递该函数的名称。
我在您的评论中看到您试图删除所有全局范围。如果您希望这样做而不需要传递一系列要移除的范围,那么很容易做出改变。
在特征中的每个函数中,您将看到一个看起来像这样的部分......
$query = $instance->newQueryWithoutScopes();
foreach ($instance->getGlobalScopes() as $identifier => $scope) {
if(in_array($scope, $removeScopes)) {
continue;
}
$query->withGlobalScope($identifier, $scope);
}
您应该能够删除foreach循环部分,并且每次只创建一个没有全局范围的新查询,您将不再需要传递数组。
答案 1 :(得分:1)
你可以这样做:
$tipoTreinamento->with(['funcaoEmpresa' => function($relation){
// Here it works
(new EmpresaScope())->remove($relation->getQuery(), $relation->getRelated());
}])
->whereHas('funcaoEmpresa', function($query) use ($colaborador){
$query->withoutGlobalScope('EmpresaScope')
->where('id', $colaborador['funcao_empresa_id']);
});
$query
中的 whereHas
变量只是一个 queryBuilder
实例,您可以对其执行 withoutGlobalScope
。它有效,因为我必须在我的项目中使用它,我认为它不适用于 OP 案例,因为它是旧版本