#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, чтобы посмотреть, может ли это помочь.