我有一个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 (例如BaseController)中扩展此UserController,如下所示:

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本身从BaseController扩展而来:

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'
        ];
    }

}

问题在于Illuminate\Http\Request需要UserFormRequest对象,而我传递了RepositoryFormRequest对象。因此,我得到此错误:
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要求的情况下键入提示注入(inject)UserFormRequest?我不能强制BaseController要求UserFormRequest,因为它应该适用于任何资源。

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

最佳答案

与许多“真正的”面向对象的语言相反,这种覆盖方法中的类型提示设计在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
}

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

使用服务类

下面的解决方案利用了额外的服务类。对于您的情况,这可能是过大的选择。但是,如果您向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);
    }

}

使用特征

您还可以使用一个特征,但是必须重命名该特征的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()方法添加额外的功能,则只需对特性进行use即可,而无需重命名,也不必编写额外的store()方法。

使用继承

我认为,继承不适用于您在这里需要的那种代码重用,至少在PHP中不是如此。但是,如果您只想使用继承来解决此代码重用问题,请给store()中的BaseController方法另一个名称,确保所有类都具有自己的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);
}

10-06 05:05