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

时间:2019-08-22 04:30:23

标签: laravel service tagging

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

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);

这是我得到的错误。

  

传递给ReportAggregator :: __ construct()的参数1必须为   MemoryReport的实例,   给定的Illuminate \ Container \ RewindableGenerator,称为   /media/mazzam/9068A9DC68A9C0F81/M.azzam/Learning/laravel/00   第80行的Tutorial / tut / routes / web.php

2 个答案:

答案 0 :(得分:1)

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

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);
}

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

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

答案 1 :(得分:0)

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

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

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

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

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

interface OCR
{
    public function scan(File $file): ScannedFile;
}

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

// AppServiceProvider -> register method

$this->app->tag([GoogleVision::class, AmazonTextract::class, Tesseract::class], 'ocrs');

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

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

您可能已经注意到,我们使用了数组扩展运算符 ...,它扩展数组元素并将它们单独传递给 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);

            // ...
        }
    }
}

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

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

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

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

希望我回答了你的问题。