Zend Framework 3, почему я не могу привязать сущности доктрины к формам на фабрике?

#php #zend-framework #doctrine-orm #zend-framework3

Вопрос:

За эти годы несколько раз сталкивался с любопытным поведением, всегда хотел спросить об этом.

Это связано с поведением, которое я не понимаю, связанным с привязкой объектов к формам в Zend Framework.

Рассмотрим эту фабрику, которая создает форму, загружает объект доктрины из базы данных и пытается привязать его к форме (чтобы значения отображались при визуализации).:

 class TermsConfigFormFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $form = new TermsConfigForm('terms_config_form', $options);
        $form->setInputFilter($container->get('InputFilterManager')->get(TermsConfigInputFilter::class, $options));
        $form->setHydrator(new DoctrineHydrator($container->get('doctrine.entitymanager.orm_default'), false));

        if (!is_string($options['locale'])) {   
            throw new InvalidArgumentException("An illegal locale variable was received by the terms configuration factory");
        }

        $termsConfig = $container->get(TermsConfigMapper::class)->get($options['locale']);
        if (!$termsConfig) {
            $termsConfig = new TermsConfig($options['locale']);
        }

        // A. If we do just this, the form doesn't print data
        $form->bind($termsConfig);

        return $form;
    }
}
 

Форма хорошо подключена через form_elements и так далее. Затем мы попытаемся использовать его внутри контроллера следующим образом:

 $termsForm = $this->formElementManager->get(TermsConfigForm::class, ['locale' => $this->locale()]);
$viewModel->setVariable('termsForm', $termsForm);
 

Интересно, что мы обнаруживаем, что объект не будет отображать связанные данные в модели представления. Теперь, что еще более любопытно, если мы удалим вызов «привязки» на заводе и сделаем это вместо этого в контроллере, значения будут отображаться правильно!!

 $termsForm = $this->formElementManager->get(TermsConfigForm::class, ['locale' => $this->locale()]);

// B. You have to do this in the controller, here, for it to print data!
$termsForm->bind($termsForm->getObject());
$viewModel->setVariable('termsForm', $termsForm);
 

Почему это не работает на заводе?

С этой точки зрения вызов привязки в контроллере аналогичен вызову привязки на заводе. Я бы хотел сохранить этот материал на фабрике, но, похоже, я не могу!

Ответ №1:

Похоже, сложность заключается в FormElementManager, который упорядочивает фабрику и цикл инициализации по порядку. Другими словами, init не вызывается до тех пор, пока фабрика не завершит свою работу.

Перефразируя, вызов «привязки» внутри фабрики формы не демонстрирует такого же поведения, как вызов «привязки» после того, как фабрика возвращает форму, потому что фабрика вызывает инициализацию (автоматически) между ними.

Будьте осторожны с этой ловушкой «порядка операций».

Ответ №2:

Если вы посмотрите на заводской шаблон, вы заметите, что задача заводского шаблона состоит в том, чтобы возвращать только нужный объект. Это не имеет никакого отношения к манипулированию объектом. Итак, с учетом сказанного все, что вы хотите сделать с желаемым объектом, должно быть сделано в объекте модели. Поскольку объект модели может быть настолько толстым, насколько это возможно. Поэтому я бы предложил перенести привязку нужного объекта с завода и перенести его в модель.

Я процитирую кое-что, что я видел в видео, и Марко Пиветта показал что-то очень абстрактно, и это было что-то вроде ниже:

 // The below code is my understanding of Marco Pivetta explaining in a youtube video.

use PsrContainerContainerInterface;

class TermsConfigFormFactory implements FactoryInterface
{
  $form = new TermsConfigForm('terms_config_form', $options);
  $form->setInputFilter($container->get('InputFilterManager')->get(TermsConfigInputFilter::class, $options));
  $form->setHydrator(new DoctrineHydrator($container->get('doctrine.entitymanager.orm_default'), false));

  /*
  This should have been the first line in the method, as it looks like. Because it seems you want locale to be given.
  if (!is_string($options['locale'])) {
    throw new InvalidArgumentException("An illegal locale variable was received by the terms configuration factory");
   }
  //From here till binding should be moved to a model.
 
  $termsConfig = $container->get(TermsConfigMapper::class)->get($options['locale']);
  if (!$termsConfig) {
    $termsConfig = new TermsConfig($options['locale']);
  }
  
  // A. If we do just this, the form doesn't print data
  $form->bind($termsConfig);
  */
 return $form;
}


 

Это то, что, по моему мнению, сделал бы Марко Пиветта в соответствии с его видео и тем, что я абстрагировал.

 class TermsConfigFormFactory implements FactoryInterface
{

  if (!is_string($options['locale'])) {
    throw new InvalidArgumentException("An illegal locale variable was received by the terms configuration factory");
  }
$form = new TermsConfigForm('terms_config_form', $options);
  $form->setInputFilter($container->get('InputFilterManager')->get(TermsConfigInputFilter::class, $options));
  $form->setHydrator(new DoctrineHydrator($container->get('doctrine.entitymanager.orm_default'), false));

  return $form;  
}

// Some model

namespace MynacespaceModel;

class SomeModel extends /* I don't remember the actual FQCN*/DoctirneRepository
{
    public function dosomething(Form $form ){
      $request = $this->getServiceContainer()->getRequest(); 
      $data = $request->getPost();
      $form->bind($data);
      // do more of your work here.

      // This array returning was not mentioned in Marco video.
      return ['status' => 200, 'message' => 'Success', 'data' => 'form' => $form];
    }
}

 

Комментарии:

1. Привет, Алтамаш. У меня нет проблем с фактическим заводским шаблоном, и я абсолютно сторонник внедрения очень строгой проверки. Проблема здесь, однако, заключается не в заводской стратегии, а в том факте, что ->bind() ведет себя двумя способами, молча, с очень разными результатами. Чтобы сослаться на Окрамиуса, он также любит использовать термин «пока-иго». Это совсем не пока-иго.

2. Привет @Saeven, если он ведет себя по-другому, значит, вы только что обнаружили ошибку и можете сообщить об этом, но в случае с TTD. Это облегчило бы жизнь такому человеку, как я. Кроме того, я приношу извинения за то, что не понял вашу точку зрения. Я дал ответ в соответствии только с моим пониманием. Спасибо!