tot*_*che 6 tags bind interface laravel
我正在使用 Laravel 8,我想获取实现 Interface X 的所有类。
几个月前我用 symfony4 和 DI 做到了:
服务.yml
_instanceof:
App\Calculator\Budget\BudgetCalculatorInterface:
tags: ['app.budget_calculator']
App\Handler\CalculatorBudgetHandler:
arguments: [!tagged app.budget_calculator]
Run Code Online (Sandbox Code Playgroud)
然后在我的类 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
但我不明白如何用 Laravel 做到这一点。我想我必须在绑定或标记中传递所有类:
$this->app->tag([CpuReport::class, MemoryReport::class], 'reports');
Run Code Online (Sandbox Code Playgroud)
这意味着如果我有一个实现 X 的新类,我必须将其添加到绑定/标签中?我想自动完成。
谢谢 !
我也需要这个。找了很久,基本找到了解决办法。这样做的坏处是,在 PHP 中,当您没有声明类时,实际上并没有声明use它们。因此,您必须扫描整个项目中的类并测试每个类以查找实现您的接口的类,或者(更好)您使用 Composer 自动加载类映射。在那里,您可能可以将类的搜索范围限制为子命名空间。
以这种方式工作的一个小而酷的包是: https: //gitlab.com/hpierce1102/ClassFinder - 基本上它使用 Composer PSR4 类映射,并且总体上性能良好。
这是我找到的解决方案:
// 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后可以像这样使用register():
$this->tagByInterface(SomeInterface::class, 'some-tag', 'App\Domain\Something');
$this->app->when(SomeClass::class)->needs('$myServices')->giveTagged('some-tag');
Run Code Online (Sandbox Code Playgroud)
由于类是使用反射加载的,如果您的根命名空间设置不正确或太宽,此操作仍然可能需要一些时间。反射很快(据我所知比从缓存加载信息更快),但您仍然应该考虑使用延迟提供程序来执行任务,以便仅在实际需要时触发对实现类的搜索。
该解决方案可行,但如果项目变大,可能会严重消耗性能。我现在正在缓存标记的类。像这样的事情:
use HaydenPierce\ClassFinder\ClassFinder as HPClassFinder;
use Illuminate\Contracts\Cache\Repository;
class InheritanceClassFinder
{
public function __construct(private ?Repository $cache = null)
{
}
public function findClassesImplementingOrExtending(string $interfaceOrClass, string $rootNamespace): array
{
if ($this->cache) {
return $this->cache->rememberForever(
'inheriting-classes-'.$interfaceOrClass,
fn () => $this->findClassesInheriting($interfaceOrClass, $rootNamespace));
}
return $this->findClassesInheriting($interfaceOrClass, $rootNamespace);
}
private function findClassesInheriting(string $interfaceOrClass, string $rootNamespace): array
{
$classes = [];
foreach (HPClassFinder::getClassesInNamespace($rootNamespace, HPClassFinder::RECURSIVE_MODE) as $className) {
if (!is_subclass_of($className, $interfaceOrClass)
|| ($class = new \ReflectionClass($className))->isAbstract() || $class->isInterface()) {
continue;
}
$classes[] = $className;
}
return $classes;
}
}
Run Code Online (Sandbox Code Playgroud)
这意味着只要注入缓存,就会加载一次内容,然后从缓存中取出。我仅在生产中注入缓存,因此在本地它有点慢但始终是最新的。在生产中,我在每次部署时都会丢弃缓存,因此每次部署后我都会获得一次新的加载。
| 归档时间: |
|
| 查看次数: |
1011 次 |
| 最近记录: |