Правильный способ расширения классов с помощью автопроводки Symfony - PullRequest
2 голосов
/ 18 октября 2019

Мне интересно, если это правильный способ расширения и использования классов с автопроводкой Symfonies.

Например, у меня есть 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);
  }
}

Теперь мне интересно, действительно ли это правильный способ сделать это, или что-то мне не хватает, и родительский класс долженбыть в состоянии автоматически подключить диспетчер управления данными?

1 Ответ

2 голосов
/ 19 октября 2019

Подводя итог комментариям, да, ваш путь правильный. В зависимости от вашего варианта использования есть альтернативы.

Вот способы, которыми вы можете сделать это:

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. Свойство Injection

    class BaseClass {
        protected $some;

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

    class SubClass extends BaseClass {
        public $other;
    }

Как и в случае Setter Injection, вы должны указать 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 (Инъекция сеттера). Они оба позволяют вам использовать шрифты и, таким образом, позволяют вашей IDE помогать писать чистый код.
В 90% случаев я бы выбрал вариант 1, поскольку для каждого, кто читает ваш код, понятно, какие услугидоступны одним взглядом на функцию __constructor.
Один вариант использования для Setter Injection - это базовый класс, предлагающий все функции setXXX, но тогда подклассам не нужны все из них. Вы можете иметь конструктор в каждом подклассе, запрашивая необходимые службы и затем вызывая методы setXXX базового класса.
Примечание: это своего рода крайний случай, и вы, вероятно, не столкнетесь сthis.

Список преимуществ и недостатков каждого метода можно найти непосредственно в документации Symfony о Service Container -> Типы инъекций

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...