首页 > 解决方案 > 使用 Symfony 自动装配扩展类的正确方法

问题描述

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

例如,我有一个 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();
  }
}

然后我有一个扩展 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);
  }
}

现在我想知道这是否真的是正确的方法,还是我缺少一些东西,父类应该能够自行自动装配实体管理器?

标签: phpsymfony

解决方案


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

这是您可以解决的方法:

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

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

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

// services.yaml
App\SubClass:
    calls:
        - [setOther, ['@other']]

或者

// services.yaml
app.sub_class:
    class: App\SubClass
    calls:
        - [setOther, ['@other']]

假设,在服务容器中 的实现OtherInterface是可用的。@other

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

/**
 * @required
 */
public function setOther(OtherInterface $other)
{
    $this->other = $other;
}

3.属性注入

    class BaseClass {
        protected $some;

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

    class SubClass extends BaseClass {
        public $other;
    }

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

// services.yaml
App\SubClass:
    properties:
        other: '@other'

或者

// services.yaml
app.sub_class:
    class: App\SubClass
    properties:
        other: '@other'

假设,在服务容器中的实现OtherInterface是可用的。@other


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

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


推荐阅读