Сохраняющиеся сгенерированные объекты Symfony3

#php #mysql #symfony #doctrine

#php #mysql #symfony #доктрина

Вопрос:

Я работаю над проектом, сохраняющим IP-адреса и диапазоны IP. В настоящее время мой объект Range имеет ссылки на два объекта IP-адресов (сетевые и широковещательные объекты) и имеет отношение OneToMany к хостам. Объект IP-адреса имеет отношение ManyToOne к диапазону IP, это необязательно.

Вот мой объект IP-адреса:

 class IPAddress {  
    /**
     * @var int
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    // relations to other tables
    /**
     * @var IPRange
     * @ORMManyToOne(targetEntity="AppBundleEntityIPRange", inversedBy="hosts", cascade={"persist", "remove"})
     * @ORMJoinColumn(name="ip_range_id", referencedColumnName="id", nullable=true)
     */
    private $ipRangeId;

    // entity variables
    /**
     * @var mixed
     * @ORMColumn(name="ip_address", type="binary", length=16, nullable=false, unique=true)
     * @SIAssertIpPacked(version="all")
     */
    private $ipAddress;
    /**
     * @var string
     * @ORMColumn(name="ip_address_text", type="string", length=50, nullable=false)
     */
    private $ipAddressText;
    /**
     * @var int
     * @ORMColumn(name="cidr", type="smallint", nullable=false, options={"unsigned"=true})
     */
    private $cidr;
    /**
     * @var mixed
     * @ORMColumn(name="gateway", type="binary", length=16, nullable=false)
     */
    private $gateway;
    /**
     * @var string
     * @ORMColumn(name="gateway_text", type="string", length=50, nullable=false)
     */
    private $gatewayText;
    /**
     * @var int
     * @ORMColumn(name="type", type="smallint", options={"unsigned"=true})
     */
    private $type;
    /**
     * @var string
     * @ORMColumn(name="description", type="string", length=50)
     */
    private $description;

    // getters and setters not shown
}
  

И мой объект диапазона IP:

 class IPRange {
    /**
     * @var int
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    // relations to other tables
    /**
     * @var IPAddress
     * @ORMOneToOne(targetEntity="AppBundleEntityIPAddress", cascade={"persist", "remove"}, orphanRemoval=true)
     * @ORMJoinColumn(name="network", referencedColumnName="id", nullable=false)
     */
    private $network;
    /**
     * @var IPAddress
     * @ORMOneToOne(targetEntity="AppBundleEntityIPAddress", cascade={"persist", "remove"}, orphanRemoval=true)
     * @ORMJoinColumn(name="broadcast", referencedColumnName="id", nullable=false)
     */
    private $broadcast;
    /**
     * @var array
     * @ORMOneToMany(targetEntity="AppBundleEntityIPAddress", mappedBy="ipRangeId")
     */
    private $hosts;

    // entity variables
    /**
     * @var string
     * @ORMColumn(name="description", type="string", length=50, nullable=false)
     */
    private $description;
    /**
     * @var int
     * @ORMColumn(name="cidr", type="smallint", nullable=false, options={"unsigned"=true})
     */
    private $cidr;
    /**
     * @var string
     * @ORMColumn(name="notes", type="text", length=65535, nullable=true)
     */
    private $notes;

    // getters and setters not shown
}
  

Мой текущий поток при создании нового диапазона IP:

  • сгенерировать новый объект диапазона IP (форма отображения)
  • получить информацию обратно из формы
  • создание объектов IP-адресов с нулевым значением для объекта диапазона IP (диапазон IP еще не сохранен, поскольку объекты сетевых и широковещательных IP-адресов не созданы)
  • сохранение моего объекта диапазона IP
  • получите этот идентификатор и перепишите все мои объекты IP-адресов с допустимым объектом диапазона IP

Должно быть, я что-то упускаю, потому что кажется, что для программного создания объектов требуется много шагов.

Is there a better and more efficient way of doing this?

Edited … added my form types …

Мой IPRangeType:

 public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('description', TextType::class, array(
            'required' => true,
            'label' => 'Description:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('notes', TextareaType::class, array(
            'required' => false,
            'label' => 'Notes:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('cidr', ChoiceType::class, array(
            'required' => true,
            'multiple' => false,
            'expanded' => false,
            'choices' => array (
                'IPv4' => [
                    '/30 (255.255.255.252)' => 30,
                    '/29 (255.255.255.248)' => 29,
                    '/28 (255.255.255.240)' => 28,
                    '/27 (255.255.255.224)' => 27,
                    '/26 (255.255.255.192)' => 26,
                    '/25 (255.255.255.128)' => 25,
                    '/24 (255.255.255.0)' => 24,
                    '/23 (255.255.254.0)' => 23,
                    '/22 (255.255.252.0)' => 22,
                    '/21 (255.255.248.0)' => 21,
                    '/20 (255.255.240.0)' => 20,
                ],
                'IPv6' => [
                    '/64 network' => 64,
                ]
            ),
            'label' => 'CIDR (subnet):',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('network', IPAddressType::class, array(
            'required' => true,
        ))
        ->add('gateway_select', ChoiceType::class, array(
            'mapped' => false,
            'required' => true,
            'multiple' => false,
            'expanded' => false,
            'choices' => array (
                '- enter in valid IP and CIDR -' => 0,
            ),
            'label' => 'Gateway:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('json_data', HiddenType::class, array(
            'mapped' => false,
        ))
    ;
}
  

Мой IPAddressType:

 public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('ipAddress', TextType::class, array(
            'required' => true,
            'label' => 'IP Address:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('cidr', ChoiceType::class, array(
            'required' => true,
            'multiple' => false,
            'expanded' => false,
            'choices' => array (
                'IPv4' => [
                    '/30 (255.255.255.252)' => 30,
                    '/29 (255.255.255.248)' => 29,
                    '/28 (255.255.255.240)' => 28,
                    '/27 (255.255.255.224)' => 27,
                    '/26 (255.255.255.192)' => 26,
                    '/25 (255.255.255.128)' => 25,
                    '/24 (255.255.255.0)' => 24,
                    '/23 (255.255.254.0)' => 23,
                    '/22 (255.255.252.0)' => 22,
                    '/21 (255.255.248.0)' => 21,
                    '/20 (255.255.240.0)' => 20,
                ],
                'IPv6' => [
                    '/64 network' => 64,
                ]
            ),
            'label' => 'CIDR (subnet):',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('gateway', TextType::class, array(
            'required' => true,
            'label' => 'Gateway:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('type', ChoiceType::class, array(
            'required' => true,
            'multiple' => false,
            'expanded' => false,
            'choices' => IPAddress::TYPE,
            'label' => 'Address Type:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
        ->add('description', TextType::class, array(
            'required' => true,
            'label' => 'Description:',
            'label_attr' => array(
                'class' => 'text-right middle',
            ),
        ))
    ;

    $builder->get('ipAddress')
        ->addModelTransformer(new IPToStringTransformer());

    $builder->get('gateway')
        ->addModelTransformer(new IPToStringTransformer());
}
  

Ответ №1:

Звучит очень окольно. Я вкратце расскажу вам, что я бы сделал в качестве общего подхода.

Я бы создал два типа форм, вызвал один IpAddressType, а другой IpRangeType. Поскольку вы разделяете цели в IPAddress, вам должен понадобиться только один тип, это все те же данные.

Таким образом, ваш тип формы будет выглядеть примерно так:

(это синтаксис symfony 2.3 — будут некоторые различия, если у вас более новая версия, но подход тот же)

 class IpRangeType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('description');
        $builder->add('cidr'); // and other single fields

        $builder->add('network', new IpAddressType());
        $builder->add('broadcast', new IpAddressType());
        $builder->add('hosts', 'collection', ['type' => new IpAddressType()]);
    }

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'YourBundleEntityIpRange',
        ));
    }
}
  

Тип вспомогательной формы.

 class IpAddressType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('ipAddress');
        $builder->add('ipAddressText'); // etc etc
    }

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'YourBundleEntityIpAddress',
        ));
    }
}
  

Затем в вашем контроллере:

 $ipRange = new IpRange();
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(new IpRangeType(), $ipRange);
$form->handleRequest($request);
if ($form->isValid()) {
    $em->persist($ipRange);
    $em->flush();
    return $this->redirect($this->generateUrl('your_success_route'));
}
  

Это должно быть так просто.

Примечание: вам нужно будет установить как минимум cascade=persist для свойства IpRange $hosts, иначе оно не будет сохранять элементы IPAddress, добавленные в коллекцию.

Из-за каскадного сохранения свойств IpRange network, broadcast и hosts вам не нужно сохранять отдельные элементы, объекты IPAddress будут автоматически связаны с родительским объектом.

Вам нужно будет следовать этому подходу для добавления записей вашего хоста:

http://symfony.com/doc/current/form/form_collections.html

Также, пожалуйста, ознакомьтесь с документацией по формам в целом, вам никогда не нужно «генерировать» объект, если форма выполнена правильно (кроме особых случаев).

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

Документы для обработки отправленных форм здесь: http://symfony.com/doc/current/forms.html#handling-form-submissions

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

1. Я рассматривал возможность этого, однако, когда я добрался до переменного количества хостов, я понял, что не хочу показывать конечному пользователю до 4096 записей IP-адреса. Я добавил свой код типа выше, и у меня есть сеть в качестве типа IPAddress. Я вернусь к CollectionType, чтобы посмотреть, может ли это помочь.