我正在使用 Laravel 8,我想获取所有实现接口(interface) X 的类。
我几个月前用 DI 用 symfony4 做到了:
服务.yml
_instanceof:
App\Calculator\Budget\BudgetCalculatorInterface:
tags: ['app.budget_calculator']
App\Handler\CalculatorBudgetHandler:
arguments: [!tagged app.budget_calculator]
然后在我的类(class) CalculatorBudgetHandler.php 中
private $calculatorList = [];
public function __construct(iterable $calculatorList)
{
$this->calculatorList = $calculatorList;
}
public function __construct(iterable $calculatorList)
{
$this->calculatorList = $calculatorList;
}
public function calculate(array $data): float
{
foreach ($this->calculatorList as $calculator) {
if ($calculator->supports($data)) {
return $calculator->calculate($data);
}
}
}
但我不明白如何用 Laravel 做到这一点。我想我必须在绑定(bind)或标记中传递我所有的类:
$this->app->tag([CpuReport::class, MemoryReport::class], 'reports');
这意味着如果我有一个实现 X 的新类,我必须将它添加到绑定(bind)/标记中? 我想自动执行。
谢谢!
最佳答案
我也需要这个。找了半天,基本上找到了解决办法。这样做的坏处是,在 PHP 中,当您没有使用
类时,它们实际上并没有被声明。因此,您必须扫描整个项目以查找类并测试每个类以找到实现您的接口(interface)的类,或者(更好)您使用 Composer 自动加载类映射。在那里,您可能会将类的搜索范围限制为子 namespace 。
以这种方式工作的小而酷的包是这个:https://gitlab.com/hpierce1102/ClassFinder - 基本上它使用 composer PSR4 classmaps 并且总体上表现良好。
这是我得出的解决方案:
// Add to service provider
private function tagByInterface(string $interfaceName, string $tagName, string $rootNamespace)
{
foreach (ClassFinder::getClassesInNamespace($rootNamespace, ClassFinder::RECURSIVE_MODE) as $className) {
$class = new \ReflectionClass($className);
if ($class->isAbstract() || $class->isInterface()) {
continue;
}
if ($class->implementsInterface($interfaceName)) {
$this->app->tag($className, $tagName);
}
}
}
然后可以在 register()
中像这样使用:
$this->tagByInterface(SomeInterface::class, 'some-tag', 'App\Domain\Something');
$this->app->when(SomeClass::class)->needs('$myServices')->giveTagged('some-tag');
由于类是使用反射加载的,如果您的根命名空间设置不正确或太宽,此操作仍可能需要一些时间。反射很快(据我所知比从缓存中加载信息更快),但您仍然应该考虑为任务使用延迟提供程序,以便仅在实际需要时触发对实现类的搜索。
关于Laravel 标记所有实现接口(interface)的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66947862/