如何在 Laravel 的服务容器中使用标记?

Moh*_*zam 4 php tagging laravel laravel-service-container

我需要知道使用服务容器标记的目的是什么以及如何通过示例使用它,这是我到目前为止所尝试的。

class MemoryReport
{
}

class SpeedReport
{
}

class ReportAggregator
{
    public function __construct(MemoryReport $memory, SpeedReport $speed)
    {
    }
}
  
App::bind('MemoryReport', function () {
    return new MemoryReport;
});

App::bind('SpeedReport', function () {
    return new SpeedReport;
});

App::tag(['MemoryReport', 'SpeedReport'], 'reports');


App::bind('ReportAggregator', function ($app) {
    return new ReportAggregator($app->tagged('reports'));
});

$reportAggregator = resolve('ReportAggregator');

dd($reportAggregator);
Run Code Online (Sandbox Code Playgroud)

这是我得到的错误。

传递给 ReportAggregator::__construct() 的参数 1 必须是 MemoryReport 的实例,给出的 Illuminate\Container\RewindableGenerator 的实例,在 /media/mazzam/9068A9DC68A9C0F81/M.azzam/Learning/laravel/00 Tutorial/tut/routes/ 中调用web.php 第 80 行

Ahm*_*mad 6

根据文档,标记用于解析绑定的某个“类别”。

我将通过向您展示我们一个项目中的一些代码来解释这意味着什么。

我们使用多个 OCR 系统来扫描上传的文档:

  • App\Support\OCR\GoogleVision
  • App\Support\OCR\AmazonTextract
  • App\Support\OCR\Tesseract ...

所有这些类都实现该App\Contracts\OCR接口:

interface OCR
{
    public function scan(File $file): ScannedFile;
}
Run Code Online (Sandbox Code Playgroud)

我们将所有 OCR 分组到一个名为 的标签中ocrs

// AppServiceProvider -> register method

$this->app->tag([GoogleVision::class, AmazonTextract::class, Tesseract::class], 'ocrs');
Run Code Online (Sandbox Code Playgroud)

然后我们将ocrs标签注入到对象中,Scan如下所示:

$this->app->bind(Scan::class, function() {
    return new Scan(...$this->app->tagged('ocrs'));
});
Run Code Online (Sandbox Code Playgroud)

您可能已经注意到,我们使用了数组扩展运算符...,它扩展数组元素并将它们单独传递给Scan对象。

让我们看看这个Scan类是什么样子的:

namespace App\Support;

class Scan
{
    private array $ocrs;

    public function __construct(App\Contracts\OCR ...$ocrs)
    {
        $this->ocrs = $ocrs;
    }

    public function scan(File $file)
    {
        foreach ($this->ocrs as $ocr)
        {
            $scannedFile = $ocr->scan($file);

            // ...
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

Scan 的承包商使用参数解包(可变参数),这意味着我们可以传递 N 个实现App\Contracts\OCR.

您可能想知道,为什么不使用类型提示而不是标记?

这是因为我们根据客户的需求不断添加/删除 OCR 系统。

因此,通过使用标签,我们不受特定实现的束缚,因为我们可以根据客户的需求轻松添加/删除类。

我希望,我回答了你的问题。


Nam*_*hek 5

标记允许您使用通用名称对服务进行分组。例如,如果您有多个服务实现相同的接口,并且您需要为每个实现执行其中一个接口方法:

interface Messenger
{
    public function sendMessage(string $recipient, string $message): void;
}

class SlackMessenger implements Messenger
{
    public function sendMessage(string $recipient, string $message): void
    {
        app(Slack::class)->send($recipient, $message);
    }
}

class TwilioMessenger implements Messenger
{
    public function sendMessage(string $recipient, string $message): void
    {
        app(Twilio::class)->sendSMS($recipient, $message);
    }
}

// AppServiceProvider::register()
App::tag([SlackMessenger::class, TwilioMessenger::class], Messenger::class);

// somewhere in your application
$messengers = app()->tagged(Messenger::class);
foreach ($messengers as $messenger) {
    $messenger->sendMessage($recipient, $message);
}
Run Code Online (Sandbox Code Playgroud)

注意:这是一个虚构的测试用例,底层服务可能有所不同。您还需要添加命名空间和use导入。

就您而言,您不需要绑定任何类。如果它们的构建基于服务容器的其他服务,则类型提示就足够了。