Laravel 使用基础 class 的依赖注入

Laravel dependency injection with base class

我对 Laravel 比较陌生,我创建了一个中间件和策略来处理我项目中的 'Ownable' Eloquent 个对象。

我使用了一个特征来做到这一点:

trait Ownable
{

    public function user(){
        return $this->belongsTo(User::class, 'created_by');
    }
}

在我的政策中,我只是这样做:

class RightPolicy
{
    use HandlesAuthorization;

    public function update(User $user, Ownable $ownable)
    {
        return $ownable->created_by == $user->id;
    }
}

还有我的中间件,在我的控制器中分配给正确的操作:

class CheckRights
{
    public function __construct(Route $route, Ownable $object) {
        $this->route = $route;
        $this->object = $object;
    }

    public function handle($request, Closure $next)
    {
         // @TODO handle request
    }
}

然后我使用 Ownable 特性创建了一个 class:

class Thread extends Model
{
    use Ownable;
}

但是,当我尝试 运行 具有这种结构的项目时,Laravel 的依赖注入器出现错误:

Target [App\Ownable] is not instantiable while building App\Http\Middleware\CheckRights

有没有办法让依赖注入器知道实例化正确的class(可能使用路由或其他东西)?

如果没有,是否有一种方便的方法可以在没有依赖注入器的情况下做其他事情以确保实例化正确的 class?

我使用 AppServiceProvider 中的 app::bind 方法解决了这个问题。

在服务提供商的注册方法中,我将一个 App\Ownable 接口绑定到一个函数,该函数循环遍历所有传递给路由器的注入 Eloquent 对象。

namespace App\Providers;

use App\Ownable;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Schema::defaultStringLength(191);
    }

    public function register()
    {
        // Binding ownable trait/interface for correctly handling policy
        $this->app->bind('App\Ownable', function() {
            // Get all parameters passed to the current route
            $array = Route::getCurrentRoute()->parameters;

            $return = NULL;
            foreach ($array as $object){
                if ($object instanceof Ownable){
                    // Fetch the last ownable instance found passed to the route
                    $return = $object;
                }
            }

            // return it
            return $return;
        });
    }
}

阅读更多关于:Binding

然后我将 Ownable 特性与一个接口结合起来以实现正确的类型提示,正如其他网站上的论坛所建议的那样:

class Thread extends Model implements \App\Interfaces\Ownable
{
    use Ownable;
}

然后在我的中间件构造函数中,我将 ownable 参数设置为可选(当调用 store 或 create 操作且未提供对象时)并根据操作名称处理我的请求,如果提供了对象

class CheckRights
{
    public function __construct(Route $route, Ownable $object = null) {
        $this->route = $route;
        $this->object = $object;
    }

    public function handle($request, Closure $next)
    {
        // Get the controller's action name
        $action = $this->route->getActionName();
        $action = substr($action, strpos($action, '@') + 1);

        // Check if an object is given in the request
        if ($action != 'store' && $action != 'create' && isset($this->object)){
            // Check if gate allows user to update/delete object
            if ($request->user()->can($action, $this->object)){
                return $next($request);
            }
        }elseif($action == 'store' || $action == 'create'){
            // Check if gate allows user to create object
            if ($request->user()->can($action, Ownable::class)){
                return $next($request);
            }
        }

        return back();
    }
}