需要帮助为全局查询范围编写remove()

时间:2015-06-19 23:54:16

标签: php laravel laravel-5

这是我的范围,apply()方法非常有用。

use Illuminate\Database\Eloquent\ScopeInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;

class JobStatusScope implements ScopeInterface
{

    /**
     * Apply scope on the query.
     *
     * @param \Illuminate\Database\Eloquent\Builder  $builder
     * @param \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder
            ->where('active', true)
            ->whereHas('user', function($q) {
                $q->where('account_type', 'company')
                    ->whereNested(function($r) {
                      $r->where('stripe_active', true)
                        ->orWhereNotNull('subscription_ends_at')
                        ->where('subscription_ends_at', '>', Carbon::now())
                        ->orWhereNotNull('trial_ends_at')
                        ->where('trial_ends_at', '>', Carbon::today());
                });
            });
    }

    /**
     * Remove scope from the query.
     *
     * @param \Illuminate\Database\Eloquent\Builder  $builder
     * @param \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function remove(Builder $builder, Model $model)
    {
        $query = $builder->getQuery();

        //dd($query->wheres);

        $columns = ['stripe_active', 'subscription_ends_at', 'active', 'trial_ends_at'];

        foreach ((array) $query->wheres as $key => $where) {
            if (in_array($where['column'], $columns)) {
                unset($query->wheres[$key]);

                $query->wheres = array_values($query->wheres);
            }
        }
    }

}

我在remove()方法中尝试的内容无效。

我在trait文件中有这个,并附加到我的模型

use App\Cable\Traits\Scopes\JobStatusScope;

trait JobStatusTrait {

    public static function bootJobStatusTrait()
    {
        static::addGlobalScope(new JobStatusScope);
    }

    /**
     * Get the query builder without the scope applied.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public static function withAll()
    {
        return with(new static)->newQueryWithoutScope(new JobStatusScope);
    }
}

当我尝试运行MyModel::withAll()->get()时,它应该返回没有apply()约束的所有记录,但它不会。

当我dd($query->wheres)方法中的remove()并致电->withAll()时,我得到了这个

array:3 [▼
  0 => array:3 [▼
    "type" => "Null"
    "column" => "jobs.deleted_at"
    "boolean" => "and"
  ]
  1 => array:5 [▼
    "type" => "Basic"
    "column" => "active"
    "operator" => "="
    "value" => true
    "boolean" => "and"
  ]
  2 => array:5 [▼
    "type" => "Basic"
    "column" => Expression {#478 ▼
      #value: "(select count(*) from `users` where `jobs`.`user_id` = `users`.`id` and `account_type` = ? and (`stripe_active` = ? or `subscription_ends_at` is not null and `subscription_ends_at` > ? or `trial_ends_at` is not null and `trial_ends_at` > ?) and `users`.`deleted_at` is null)"
    }
    "operator" => ">="
    "value" => Expression {#476 ▶}
    "boolean" => "and"
  ]
]

我似乎无法弄清楚这一点。 Laravel的大部分都很容易理解,它是如此“雄辩”,但这让我感到厌烦。

任何帮助将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:0)

whereHas()限制通过使用子查询来获取相关记录的计数。

dd()输出中可以看出,目标column子句的where不仅仅是一个纯字符串(如'active'),而是一个Expression对象,包含子查询。

您需要编写一些逻辑来确定此Expression是否与whereHas()生成的子查询匹配,因此您可以删除它。

一个简单的例子如下(n.b.没有语法检查或测试):

public function remove(Builder $builder, Model $model)
{
    $query = $builder->getQuery();

    // your plain column names
    $columns = ['active'];

    // columns present in the user relationship subquery
    $userColumns = ['stripe_active', 'subscription_ends_at', 'trial_ends_at'];

    foreach ((array) $query->wheres as $key => $where) {
        // first check if the column is really an Expression
        // if it is not an expression, do the normal check on strings
        if ($where['column'] instanceof \Illuminate\Database\Query\Expression) {
            // get the sql value of the Expression
            $sql = $where['column']->getValue();

            // see if all the user columns are in the sql statement
            $count = 0;
            foreach($userColumns as $name) {
                // increment count if the user column was found
                $count += (stripos($sql, $name) !== false) ? 1 : 0;
            }

            // if all the user columns were found, assume this is the where and remove it
            if ($count == count($userColumns)) {
                unset($query->wheres[$key]);

                $query->wheres = array_values($query->wheres);
            }
        } elseif (in_array($where['column'], $columns)) {
            unset($query->wheres[$key]);

            $query->wheres = array_values($query->wheres);
        }
    }
}

你可能想要提出一些比检查所有列名是否都在SQL中更严格的东西,但这应该让你开始。