Форма Symfony2, где объекты данных не совпадают точно с тем, что нужно заполнить - PullRequest
2 голосов
/ 04 октября 2011

У нас есть служба мониторинга, где наши мониторы следят за определенными машинами.

Я создаю форму для регистрации новой машины в Symfony2.

Итак, у нас есть сущность машины :

  • id
  • имя машины
  • идентификатор монитора

И сущность монитора:

  • id
  • serialnumber
  • ...

Для новой машины клиент должен заполнить форма с:

  • имя машины
  • серийный номер подключенного монитора
  • ...

Теперь,если я создаю форму с машинным объектом в качестве основы для данных, у меня нет «поля», в котором можно запросить серийный номер.Symfony не допускает этого, поскольку у объекта поддержки нет поля с именем «серийный номер».

Как я могу:

  • запрашивать серийный номер, пока естьнет такого поля в резервном объекте
  • если я получу серийный номер, как мне связать его с его внутренним идентификатором, чтобы сохранить его с объектом после проверки и привязки данных

Я полагаю, что могу:

  • создать форму без объекта за ней, как описано @weaverryan в этой замечательной статье: http://knplabs.com/en/blog-csi/symfony-validators-standalone - таким образом я делаю упорство самостоятельно, ноМне нужны отдельные ограничения для моей пользовательской формы и для моей машинной сущности, что очень жаль.
  • предоставляют какую-то ссылку, чтобы Symfony знал, где можно получить факт наличия поля serialnumber и его ограниченияявляются.Возможно, определив отношение?Я надеялся избежать отношений в своем коде сущностей, потому что я видел немало проблем, возникающих из-за этого в списке рассылки, но, возможно, у меня нет выбора: -)
  • что-нибудь еще?

Надеюсь, я правильно объясняю это.Я думаю, что многие должны были решить это.Я думаю, что просто смотрю на некоторые очень стандартные функции Symfony, просто потому, что я не уверен, как это называется: -)

Ответы [ 2 ]

5 голосов
/ 04 октября 2011

Во-первых, вы должны использовать отношения - т.е. у машины есть один монитор. Я думаю, что у людей есть проблемы с этим, потому что, когда вы привыкли думать о вещах в терминах «идентификаторов» и внешних ключей, размышления об отношениях полностью объектно-ориентированным способом являются новыми:).

Итак, предполагая, что ваш компьютер связан с монитором, вы можете теперь создать MachineType, который встраивает MonitorType (который будет формой с полем serialnumber в нем). Тогда ваши ограничения для серийного номера вашего класса Monitor будут использованы при отправке составной формы.

По умолчанию, когда вы связываете все это, он создает новый объект Machine с новым объектом Monitor, доступным через $ machine-> getMonitor (). Если вы сохраните, это будет две вставки. Тем не менее, я предполагаю, что вы предпочли бы искать монитор по его серийному номеру и использовать этот существующий монитор, верно? Сделать это легко, просто закоротите вещи после того, как вы связали свою форму.

$form = new MachineType();
$form->bindRequest($request);
if ($form->isValid()) {
    $em = $this->getDoctrine()->getEntityManager();
    $machine = $form->getData();
    $serialNumber = $machine->getMonitor->getSerialNumber();

    $existingMonitor = $em
        ->getRepository('YourBundle:Monitor')
        ->findOneBy(array('serialNumber' => $serialNumber));
    if ($existingMonitory) {
        $machine->setMonitor($existingMonitor);
    }

    $em->persist($machine);
    $em->persist($machine->getMonitor());
    $em->flush();

    // ...
}

Итак, это идея. Самое главное, что, не используя отношения, вы ставите себя в невыгодное положение. Видите ли, объединяя эти две формы вместе, вы действительно do имеете естественный доступ к полю serialnumber.

Удачи!

4 голосов
/ 19 октября 2011

Недавно я решил эту проблему, создав собственный тип формы (в моем случае это называется «селектор»), к которому подключен преобразователь данных, который возвращает монитор по последовательному каналу:

Тип машины:

class MachineType extends AbstractType {
    public function buildForm(FormBuilder $builder, array $options) {
        $builder->add('name');
        $builder->add('monitor', 'monitor_selector_type');
    }

    public function getName() {
        return 'machine;
    }
}

Тип селектора монитора (в конечном итоге это будет просто текстовое поле (родительский тип по умолчанию - «текст»), в котором вы введете серийный номер монитора. В этом поле будет отображаться ошибка, если был введен несуществующий серийный номер.

class MonitorSelectorType extends AbstractType {
    private $doctrine

    public function __construct(Registry $doctrine) {
        $this->doctrine = $doctrine;
    }

    public function buildForm(FormBuilder $builder, array $options) {
        $transformer = new MonitorToSerialTransformer($this->doctrine->getEntityManager());
        $builder->appendClientTransformer($transformer);
    }

    public function getDefaultOptions(array $options) {
        return array(
            'invalid_message'=>'The selected monitor does not exist',
            'error_bubbling'=>false
        );
    }

    public function getName() {
        return 'machine;
    }
}

Этот класс преобразует серийный номер в фактический монитор, а фактический монитор - в серийный

class MonitorToSerialTransformer {
    private $em

    public function __construct(EntityManager $em) {
        $this->em = $em;
    }

    public function transform($val) {
        if (null === $val) {
            return '';
        }
        return $val->getSerial();
    }

    public function reverseTransform($val) {
        if (!$val) {
            return null;
        }
        $monitor = $this->em->getRepository('xBundle:Monitor')->findOneBy(array('serial' => $val));
        if (null === $monitor) {
            throw new TransformationFailedException('A monitor with serial '.$val.' does not exist!');
        }
        return $monitor;
    }
}

наконец, зарегистрируйте MonitorSelectorType в вашем сервисном контейнере с идентификатором "monitor_selector_type" и тегом "form.type" и вставьте в него доктрину.

Теперь в любом произвольном месте вашего приложения будет очень легко использовать этот тип селектора для выбора монитора по идентификатору. Никакая логика не должна быть добавлена ​​к вашему контроллеру вообще.

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

С уважением, Bart

...