请问模型关联关系中如何使用多字段条件

1. 运行环境

1). 当前使用的 Laravel 版本?

10.2.0

2. 问题描述?

用户授权策略 policy 中使用 update 方法参数中注入了目标模型,我如果想在之后的程序中复用该模型,而不想重新 find 查询,有没有解决方案?

3.补充一下代码

比如我有一个 TeamPolicy 采用自发现方式调用。

# 完整代码较长,我只贴关键部分
class TeamPolicy
{
    /**
     * 我想复用 update 方法参数中的 team
     * 因为它在校验用户权限的时候将被首次查询
     */
    public function update(User $user, Team $team): bool
    {
        // 如果是团队的创建者,可以修改
        if ($user->id === $team->owner_id) {
            return  true;
        }

        // 如果有团队修改权限,可以修改
        if (!$user->can('teams.update')) {
            return  false;
        }

        return  true;
    }
}

然后我在 TeamUpdate 类型中想复用它不想重新查询,请问如何绑定这个实例

final class TeamUpdate
{
    readonly public TeamUpdateService $teamUpdateService;
    public function __construct(Team $team)
    {
        # 以下这行虽然可以看到 $team 被传入实例,但其中的数据是空的
        # 请问如何查询到上面授权策略注入的实例。
        info('app', [$team,]);
        $this->teamUpdateService = app(TeamUpdateService::class);
    }

    /**
     * 更新团队
     *
     * @param null $_
     * @param array{} $args
     * @return Team
     * @throws Throwable
     * @throws ValidationException
     */
    public function __invoke($_, array $args): Team
    {
        $input          = data_get($args, 'teamUpdateInput');
        info('app', [app(Team::class,Arr::only($input,['id'])),]);
        $team           = Team::find(data_get($input, 'id'));
        /** @var User $user */
        $user           = auth()->user();
        throw_if(!$team, GraphQLRenderException::class, '团队不存在');
        $data           = Arr::only($input, ['name', 'quota']);
        throw_if(
            isset($data['quota']) && !$user->can('teams.audit'),
            GraphQLRenderException::class,
            '没有权限修改团队额度'
        );

        return  $this->teamUpdateService->handle($team, $data);
    }
}
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
最佳答案

是意味着 inspect 方法最终会调用策略方法吗?

普通的策略方法返回简单的布尔值,所以很难满足一些特殊情况

策略响应可以包含更多信息,这是它的 toArray() 方法

public function toArray()
{
    return [
        // 这个布尔值可以使策略响应像普通策略方法一样被判断
        'allowed' => $this->allowed(),
        'message' => $this->message(),
        'code' => $this->code(),
    ];
}

文档上判断策略响应的示例

$response = Gate::inspect('update', $post);

// 这个返回的就是布尔值
if ($response->allowed()) {
    // The action is authorized...
} else {
    echo $response->message();
}

但这些不能解决你的问题。。。

1年前 评论
讨论数量: 15

如果你想在多个控制器或服务类中需要复用该模型,你可以使用 Laravel 服务容器来注册该模型,并在需要时从容器中获取该模型的实例

1年前 评论

把代码贴出来看看

1年前 评论
sanders (楼主) 1年前
Adachi (作者) 1年前
sanders (楼主) 1年前

策略那里不是注入而是传入吧
策略方法
使用策略授权操作

你的 $team 已经查出来了

$team = Team::find(data_get($input, 'id'));

不应该是这样判断吗

$user->can('update', $team);
1年前 评论
sanders (楼主) 1年前
sanders (楼主) 1年前

file

嗯,抱歉,肤浅了,我没用过 策略方法,我以为你所谓的team是类似管理所有权限的东西,我是在中间件拦截判断的是否有权限curd

1年前 评论
sanders (楼主) 1年前

是意味着 inspect 方法最终会调用策略方法吗?

普通的策略方法返回简单的布尔值,所以很难满足一些特殊情况

策略响应可以包含更多信息,这是它的 toArray() 方法

public function toArray()
{
    return [
        // 这个布尔值可以使策略响应像普通策略方法一样被判断
        'allowed' => $this->allowed(),
        'message' => $this->message(),
        'code' => $this->code(),
    ];
}

文档上判断策略响应的示例

$response = Gate::inspect('update', $post);

// 这个返回的就是布尔值
if ($response->allowed()) {
    // The action is authorized...
} else {
    echo $response->message();
}

但这些不能解决你的问题。。。

1年前 评论

因为我在 lighthouse 的代码里面也没有找到直接对 can 方法的调用,而是在里面看到了对拦截器 inspect 方法的调用,类似这样: $response = $gate->inspect($ability, $arguments); 。总的来说,lighthouse 似乎没有在校验权限时对已查询的模型做缓冲处理,除非我去用自定义的指令去替换它。

我没用过 Lighthouse,按你说的只搜到了这个。

这里的 $model 是传入的,你看能否在这里找到突破口。

file

1年前 评论
sanders

@lddtime

可能我没理解你说的有关inspect方法的问题,于是我还是翻了一下框架的源码,说一下几个问题我自己的结论

Gate::inspect() 方法,会去调用策略

以下是框架默认Gate这方面实现的源码:

    protected function resolveAuthCallback($user, $ability, array $arguments)
    {
        if (isset($arguments[0]) &&
            ! is_null($policy = $this->getPolicyFor($arguments[0])) &&
            $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
            return $callback;
        }

        if (isset($this->stringCallbacks[$ability])) {
            [$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);

            if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
                return $this->abilities[$ability];
            }
        }

        if (isset($this->abilities[$ability]) &&
            $this->canBeCalledWithUser($user, $this->abilities[$ability])) {
            return $this->abilities[$ability];
        }

        return function () {
            //
        };
    }

lighthouse @can 指令的问题

我没用过 Lighthouse,按你说的只搜到了这个。
这里的 $model 是传入的,你看能否在这里找到突破口。

是的,我也是找到这里,但他只是用于权限校验,没有进行缓冲。

确定的是 @can 指令容易产生这种重复查询的缺陷,对此我能想到的有如下两种方案:

1. 封装自己的指令替换 @can 指令

继承 CanDirective 覆盖其 modelsToCheck 方法,将查到的模型缓冲到 GraphQLContext 里面供之后的代码取用。

2. 在解析器里面调用 Gate 的方法

graphql schema 声明中去掉 @can 指令的调用,直接在之后的字段解析器里面调用 Gate 的方法。

1年前 评论

是一次请求的生命周期内,不想对同一条数据查询多次吧,然后又不想用缓存之类的。这个问题我目前的处理方式是写一个上下文类,用单例模式注册到容器里,在第一次查询的时候将模型放到这个上下文对象里,本次生命周期内任意地方可用。

<?php

namespace App\Services;

class ContextService
{
    private array $data;

    public function __construct()
    {
        $this->data = [];
    }

    /**
     * @param $key
     * @param $value
     * @return void
     */
    public function setData($key, $value)
    {
        if ($this->keyExists($key)) {
            return;
        }

        $this->data[$key] = $value;
    }

    /**
     * @param $key
     * @return mixed|null
     */
    public function getData($key): mixed
    {
        return $this->data[$key] ?? null;
    }

    /**
     * @param $key
     * @return bool
     */
    public function keyExists($key): bool
    {
        return isset($this->data[$key]);
    }
}

app(ContextService::class)->setData('user', $user);
app(ContextService::class)->getData('user');

不过这样写也有些问题,目前没得到好的方式解决。 1、团队协作时其他人不知道里面存了哪些东西; 2、key值是写死的后续迭代如何有调整改起来比较麻烦;

3个月前 评论
zzzzzq (作者) 3个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!