使用 Symfony 自动装配扩展类的正确方法

Svd*_*vdb 3 php symfony

我想知道这是否是通过 Symfony 自动装配扩展和使用类的正确方法。

例如,我有一个 BaseClass 实例化并自动连接实体管理器。

class BaseClass
{
  protected $entityManager;

  public function __construct(EntityManagerInterface $entityManager)
  {
    $this->entityManager = $entityManager;
  }

  protected function someMethodIWantToUse(Entity $something)
  {
    // Do something there
    $this->entityManager->persist($something);
    $this->entityManager->flush();
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个扩展 BaseClass 并需要访问该方法的子类。所以我让它再次自动装配并将它传递给父构造函数。

class SubClass extends BaseClass
{
  private $handler;

  public function __construct(EntityManagerInterface $em, SomeHandler $handler)
  {
    parent::__construct($em);
    $this->handler = $handler;
  }

  public function SubClassMethod()
  {
    // Get some data or do something
    $entity = SomeEntityIGot();
    $this->someMethodIWantToUse($entity);
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我想知道这是否真的是正确的方法,还是我遗漏了什么,父类应该能够自己自动装配实体管理器?

waw*_*awa 7

总结评论,是的,你的方式是正确的。根据您的用例,有替代方案。

以下是您可以采取的方法:

1. 扩展类和使用构造函数注入(你做什么)

    class BaseClass {
        protected $some;

        public function __construct(SomeInterface $some)
        {
            $this->some = $some;
        }
    }

    class SubClass extends BaseClass {
        private $other;

        public function __construct(SomeInterface $some, OtherInterface $other)
        {
            parent::__construct($some);
            $this->other = $other;
        }
    }
Run Code Online (Sandbox Code Playgroud)

2.二传手注入

    class BaseClass {
        protected $some;

        public function __construct(SomeInterface $some)
        {
            $this->some = $some;
        }
    }

    class SubClass extends BaseClass {
        private $other;

        public function setOther(OtherInterface $other)
        {
            $this->other = $other;
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在setOther不会自动调用,您必须通过callsservices.yaml文件中指定属性来“手动”调用它,如下所述:https : //symfony.com/doc/current/service_container/calls.html。这看起来像这样:

// services.yaml
App\SubClass:
    calls:
        - [setOther, ['@other']]
Run Code Online (Sandbox Code Playgroud)

或者

// services.yaml
app.sub_class:
    class: App\SubClass
    calls:
        - [setOther, ['@other']]
Run Code Online (Sandbox Code Playgroud)

假设在服务容器中OtherInterface可以使用的实现@other

如果您使用自动装配,一个更优雅的解决方案是简单地@required向函数添加一个注释,如下所述:https : //symfony.com/doc/current/service_container/autowiring.html#autowiring-calls,如下所示:

/**
 * @required
 */
public function setOther(OtherInterface $other)
{
    $this->other = $other;
}
Run Code Online (Sandbox Code Playgroud)

3.财产注入

    class BaseClass {
        protected $some;

        public function __construct(SomeInterface $some)
        {
            $this->some = $some;
        }
    }

    class SubClass extends BaseClass {
        public $other;
    }
Run Code Online (Sandbox Code Playgroud)

与 Setter 注入一样,您需要告诉 Symfony 填充此属性,方法是在您的services.yaml文件中指定它,如下所示:

// services.yaml
App\SubClass:
    properties:
        other: '@other'
Run Code Online (Sandbox Code Playgroud)

或者

// services.yaml
app.sub_class:
    class: App\SubClass
    properties:
        other: '@other'
Run Code Online (Sandbox Code Playgroud)

假设在服务容器中OtherInterface可以使用的实现@other


结论:
由于有不同的方法可以解决此问题,因此您可以为您的用例确定正确的方法。我个人选择使用注释的选项 1(构造函数注入)或选项 2(Setter 注入)。它们都允许您使用类型提示,从而允许您的 IDE 帮助您编写干净的代码。
在 90% 的情况下,我会选择选项 1,因为这样一来,每个阅读您代码的人都可以一目了然地了解哪些服务可用__constructor
Setter 注入的一个用例是提供所有setXXX功能的基类,但子类不需要所有这些功能。您可以在每个子类中有一个构造函数,请求所需的服务,然后调用setXXX基类的方法。
注意:这是一种边缘情况,您可能不会遇到这种情况。

您可以直接在有关服务容器的 Symfony 文档-> 注入类型中找到每种方法的优缺点列表

  • 从 Php 8 开始,我们还可以使用 `Symfony\Contracts\Service\Attribute\Required` 代替 `@required` 注释。关于这个答案的第二种方法。 (2认同)