因此,您希望避免订单和履行资源控制器的代码重复,并且有点枯燥。好的
无法输入特征
作为马修
stated
,您无法键入提示特征,这就是您出现绑定解析错误的原因。除此之外,即使它是可类型化的,容器也会混淆它应该实例化哪个模型,因为有两个模型
Commentable
型号可用。但是,我们稍后再谈。
通常,有一个界面来伴随一个特征是一种很好的做法。除了界面可以类型化之外,您还坚持
Interface Segregation
“如果需要”的原则是一种良好的做法。
interface Commentable
{
public function comments();
}
class Order extends Model implements Commentable
{
use Commentable;
// ...
}
现在它是可打印的。让我们来谈谈容器混淆问题。
语境结合
contextual binding
. 这是明确告诉它何时以及如何将抽象解析为具体的能力。
# AppServiceProvider::register()
$this->app
->when(CommentController::class)
->needs(Commentable::class)
->give(function ($container, $params) {
// Since you're probably utilizing Laravel's route model binding,
// we need to resolve the model associated with the passed ID using
// the `findOrFail`, instead of just newing up an empty instance.
// Assuming this route pattern: "order|fullfilment/{id}/comment/{id}"
$id = (int) $this->app->request->segment(2);
return $this->app->request->segment(1) === 'order'
? Order::findOrFail($id)
: Fulfillment::findOrFail($id);
});
你基本上是告诉容器
CommentController
需要
实例,首先检查路由,然后实例化正确的可注释模型。
# AppServiceProvider::register()
$this->app->bind(Commentable::class, function ($container, $params) {
$id = (int) $this->app->request->segment(2);
return $this->app->request->segment(1) === 'order'
? Order::findOrFail($id)
: Fulfillment::findOrFail($id);
});
错误的工具
我们刚刚通过引入不必要的复杂性消除了重复的控制器代码,这种复杂性甚至更糟糕。
遗产
消除这些问题的正确工具就是继承。引入一个抽象的基注释控制器类,并从中扩展了两个浅层注释控制器类。
# App\Http\Controllers\CommentController
abstract class CommentController extends Controller
{
public function store(CreateCommentRequest $request, Commentable $commentable) {
// ...
}
// All other common methods here...
}
# App\Http\Controllers\OrderCommentController
class OrderCommentController extends CommentController
{
public function store(CreateCommentRequest $request, Order $commentable) {
return parent::store($commentable);
}
}
# App\Http\Controllers\FulfillmentCommentController
class FulfillmentCommentController extends CommentController
{
public function store(CreateCommentRequest $request, Fulfillment $commentable) {
return parent::store($commentable);
}
}
# Routes
Route::resource('order.comment', 'OrderCommentController');
Route::resource('fulfillments.comment', 'FulfillCommentController');
啊,错误的语言
没那么快:
声明
.
尽管重写方法参数在构造函数中工作得很好,但它对其他方法根本不起作用!构造函数是
special cases
.
好吧,也许在一个更好的世界里。
更新:请参阅
PHP 7.4
的类型差异支持
那我们该怎么办?
如果我们明确告诉路由器如何加载
可评论的
注释控制器
explicit model binding
通过映射路线占位符(例如。
{order}
)建模类或自定义解析逻辑。所以,当我们使用我们的单曲
我们可以根据订单和履行的路径占位符使用单独的模型或解析逻辑。因此,我们放弃typehint并依赖占位符。
对于资源控制器,占位符名称取决于传递给
Route::resource
artisan route:list
找出答案。
# App\Providers\RouteServiceProvider::boot()
public function boot()
{
// Map `{order}` route placeholder to the \App\Order model
$this->app->router->model('order', \App\Order::class);
// Map `{fulfillment}` to the \App\Fulfilment model
$this->app->router->model('fulfillment', \App\Fulfilment::class);
parent::boot();
}
# App\Http\Controllers\CommentController
class CommentController extends Controller
{
// Note that we have dropped the typehint here:
public function store(CreateCommentRequest $request, $commentable) {
// $commentable is either an \App\Order or a \App\Fulfillment
}
// Drop the typehint from other methods as well.
}
路线定义保持不变。
它比第一种解决方案更好,因为它不依赖于易于更改的URL段,而依赖于很少更改的路由占位符。它也是通用的
s将被解决为
\App\Order
模型和所有
{fulfillment}
App\Fulfillment
.
我们可以改变第一种解决方案,利用路由参数而不是URL段。但是,当拉雷维尔提供给我们时,没有理由手动执行。
是的,我知道,我也感觉不太好。