jjzjj

php - 拉维尔 5 : Type-hinting a FormRequest class inside a controller that extends from BaseController

coder 2023-06-13 原文

我有一个 BaseController,它为我的 API 服务器提供了大多数 HTTP 方法的基础,例如store 方法:

BaseController.php

/**
 * Store a newly created resource in storage.
 *
 * @return Response
 */
public function store(Request $request)
{
    $result = $this->repo->create($request);

    return response()->json($result, 200);
}

然后我在更具体的 Controller (例如 UserController)中扩展此 BaseController,如下所示:

UserController.php

class UserController extends BaseController {

    public function __construct(UserRepository $repo)
    {
        $this->repo = $repo;
    }

}

这很好用。但是,我现在想扩展 UserController 以注入(inject) Laravel 5 的新 FormRequest 类,它负责处理 User 资源的验证和身份验证等事情.我想这样做,通过覆盖 store 方法并使用 Laravel 的类型提示依赖注入(inject)为其 Form Request 类。

UserController.php

public function store(UserFormRequest $request)
{
    return parent::store($request);
}

UserFormRequestRequest 扩展而来,后者本身从 FormRequest 扩展而来:

UserFormRequest.php

class UserFormRequest extends Request {

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name'  => 'required',
            'email' => 'required'
        ];
    }

}

问题是 BaseController 需要一个 Illuminate\Http\Request 对象,而我传递了一个 UserFormRequest 对象。因此我得到这个错误:

in UserController.php line 6
at HandleExceptions->handleError('2048', 'Declaration of Bloomon\Bloomapi3\Repositories\User\UserController::store() should be compatible with Bloomon\Bloomapi3\Http\Controllers\BaseController::store(Illuminate\Http\Request $request)', '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php', '6', array('file' => '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php')) in UserController.php line 6

那么,我如何在仍然遵守 BaseController 的 Request 要求的情况下键入 hint 来注入(inject) UserFormRequest?我不能强制 BaseController 要求 UserFormRequest,因为它应该适用于任何资源。

我可以在 BaseControllerUserController 中使用类似 RepositoryFormRequest 的接口(interface),但问题是 Laravel 不再注入(inject)UserFormController 通过其类型提示依赖注入(inject)。

最佳答案

与许多“真正的”面向对象语言相比,这种覆盖方法中的类型提示设计在 PHP 中是不可能的,请参阅:

class X {}
class Y extends X {}

class A {
    function a(X $x) {}
}

class B extends A {
    function a(Y $y) {} // error! Methods with the same name must be compatible with the parent method, this includes the typehints
}

这会产生与您的代码相同类型的错误。我只是不会在您的 BaseController 中放置 store() 方法。如果您觉得自己在重复代码,请考虑引入例如服务类或特征。

使用服务类

下面是使用额外服务类的解决方案。对于您的情况,这可能有点矫枉过正。但是,如果您向 StoringServicestore() 方法添加更多功能(如验证),它可能会很有用。您还可以向 StoringService 添加更多方法,例如 destroy()update()create()、但是您可能希望以不同的方式命名服务。

class StoringService {

    private $repo;

    public function __construct(Repository $repo)
    {
        $this->repo = $repo;
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        $result = $this->repo->create($request);

        return response()->json($result, 200);
    }
}

class UserController {

    // ... other code (including member variable $repo)

    public function store(UserRequest $request)
    {
        $service = new StoringService($this->repo); // Or put this in your BaseController's constructor and make $service a member variable
        return $service->store($request);
    }

}

使用特征

你也可以使用 trait,但是你必须重命名 trait 的 store() 方法:

trait StoringTrait {

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        $result = $this->repo->create($request);

        return response()->json($result, 200);
    }
}

class UserController {

    use {
        StoringTrait::store as baseStore;
    }

    // ... other code (including member variable $repo)

    public function store(UserRequest $request)
    {
        return $this->baseStore($request);
    }

}

此解决方案的优点是,如果您不必为 store() 方法添加额外的功能,您可以只 使用 特征而不重命名,然后您不必编写额外的 store() 方法。

使用继承

在我看来,继承不太适合您在这里需要的那种代码重用,至少在 PHP 中不适合。但是如果你只想对这个代码重用问题使用继承,给你的 BaseController 中的 store() 方法起一个名字,确保所有的类都有自己的 store() 方法并调用 BaseController 中的方法。像这样的:

BaseController.php

/**
 * Store a newly created resource in storage.
 *
 * @return Response
 */
protected function createResource(Request $request)
{
    $result = $this->repo->create($request);

    return response()->json($result, 200);
}

UserController.php

public function store(UserFormRequest $request)
{
    return $this->createResource($request);
}

关于php - 拉维尔 5 : Type-hinting a FormRequest class inside a controller that extends from BaseController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29795944/

有关php - 拉维尔 5 : Type-hinting a FormRequest class inside a controller that extends from BaseController的更多相关文章

随机推荐