laravel 中合并控制器的多个表单请求为一个的解决方案

laravel 中一个动作(action)方法需要对应编写一个表单请求(FormRequest)类。因此经常懒的去编写表单请求,而是直接在动作中编写验证规则。所以我扩展了表单请求基类,可方便的使一个控制器共用同一个表单请求类。

扩展之后的表单请求基类(app/Http/Requests/FormRequest)

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Contracts\Validation\Validator;
use InvalidArgumentException;

class FormRequest extends \Illuminate\Foundation\Http\FormRequest
{
    /**
     * 指示验证是否应在第一个规则失败后停止。
     *
     * @var bool
     */
    protected $stopOnFirstFailure = true;

    public function validationData()
    {
        return $this->call(__FUNCTION__, $args = func_get_args(), parent::{__FUNCTION__}(...$args));
    }

    public function authorize(): bool
    {
        return $this->call(__FUNCTION__, func_get_args(), true);
    }

    public function rules(): array
    {
        return $this->call(__FUNCTION__, func_get_args(), []);
    }

    public function messages(): array
    {
        return $this->call(__FUNCTION__, $args = func_get_args(), parent::{__FUNCTION__}(...$args));
    }

    public function attributes(): array
    {
        return $this->call(__FUNCTION__, $args = func_get_args(), parent::{__FUNCTION__}(...$args));
    }

    protected function failedValidation(Validator $validator)
    {
        return $this->call(__FUNCTION__, $args = func_get_args(), parent::{__FUNCTION__}(...$args));
    }

    protected function failedAuthorization()
    {
        return $this->call(__FUNCTION__, $args = func_get_args(), parent::{__FUNCTION__}(...$args));
    }

    public function validator(ValidationFactory $factory): Validator
    {
        return $this->call(__FUNCTION__, func_get_args(), $this->createDefaultValidator($factory));
    }

    protected function withValidator(Validator $validator): Validator
    {
        return $this->call(__FUNCTION__, func_get_args(), $validator);
    }

    /**
     * @return callable|string
     */
    protected function after()
    {
        return $this->call(
            __FUNCTION__,
            func_get_args(),
            /**
             * @return mixed
             *
             * @throws \Throwable
             */
            static function (Validator $validator) {
                return $validator;
            }
        );
    }

    protected function call(string $method, array $args = [], $defaultReturn = null)
    {
        $actionMethod = transform($method, function (string $method) {
            if (! in_array(
                $method,
                [
                    'validationData',
                    'authorize',
                    'rules',
                    'messages',
                    'attributes',
                    'failedValidation',
                    'failedAuthorization',
                    'validator',
                    'withValidator',
                    'after',
                ],
                true
            )) {
                throw new InvalidArgumentException("Can't call the method[$method].");
            }

            return $this->route()?->getActionMethod().ucfirst($method);
        });

        if (method_exists($this, $actionMethod)) {
            return $this->$actionMethod(...$args);
        }

        if (method_exists(parent::class, $method)) {
            return parent::$method(...$args);
        }

        return $defaultReturn;
    }
}

使用示例

认证表单请求

<?php

namespace App\Http\Requests\Api;

use App\Http\Requests\FormRequest;

class AuthRequest extends FormRequest
{
    public function registerRules()
    {
        return [
            'email' => 'required|email|unique:App\Models\JWTUser,email',
            'password' => 'required|string|min:8|confirmed',
            'password_confirmation' => 'required|same:password',
        ];
    }

    public function loginRules()
    {
        return [
            'email' => 'required|email',
            'password' => 'required|string|min:8|confirmed',
        ];
    }
}

认证控制器

<?php

namespace App\Http\Controllers\Api;

use App\Http\Requests\Api\AuthRequest;

class AuthController extends Controller
{
    public function register(AuthRequest $request)
    {
        $validated = $request->validated();
        ...
    }

    public function login(AuthRequest $request)
    {
        $validated = $request->validated();
        ...
    }
}

原文链接

本作品采用《CC 协议》,转载必须注明作者和本文链接
No practice, no gain in one's wit. 我的 Gitub
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 9

麻烦的很,首先你可以写个命令自动生成请求、控制器,然后每个控制器还要自己手动验证??$validated = $request->validated();

11个月前 评论
guanguans (楼主) 11个月前

如果是懒得创建文件得话,推荐使用 Attribute:

Controller:


class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    protected array $validated;

    public function callAction($method, $parameters)
    {
        $this->doValidate($method);
        return parent::callAction($method, $parameters);
    }


    private function doValidate(string $method)
    {
        $reflectionMethod = new \ReflectionMethod($this, $method);

        $attributes = $reflectionMethod->getAttributes(Validation::class);

        if (empty($attributes[0])) {
            return;
        }

        /** @var Validation $validation */
        $validation = $attributes[0]->newInstance();

        try {
            $this->validated = $this->validate(request(), $validation->getRules(), $validation->getMessages());
        } catch (ValidationException $e) {
            dd($e->errors());
        }
    }
}

Validation:

#[\Attribute(\Attribute::TARGET_METHOD)]
class Validation
{
    public function __construct(protected array $rules, protected array $messages)
    {
    }

    /**
     * @return array
     */
    public function getRules(): array
    {
        return $this->rules;
    }

    /**
     * @return array
     */
    public function getMessages(): array
    {
        return $this->messages;
    }
}

业务控制器:

class TestController extends Controller
{

    #[Validation(
        rules: [
            'name' => 'required',
            'id' => 'required'
        ],
        messages: [
            'name.required' => '请输入名称'
        ]
    )]
    public function test()
    {
        dd($this->validated);
    }
}
11个月前 评论
guanguans (楼主) 11个月前

这个能做到指定场景吗?就比如 register 方法里面使用 login 路由对应的规则

7个月前 评论
guanguans (楼主) 7个月前
kbdxbt (作者) 7个月前

还发现个问题,如果没有路由方法的时候,就会出现死循环的情况。

7个月前 评论
guanguans (楼主) 7个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
55
粉丝
126
喜欢
971
收藏
1332
排名:46
访问:15.2 万
私信
所有博文
社区赞助商