使用 css 助手在子表单中拆分 Symfony2 表单
Symfony2 Form Splitting in sub forms with css helper
我有大表格,我需要在移动设备上将其拆分为多个表格。
桌面 = 1 个大表格
Mobile = 2-3 更小的表格,当我验证 1 表格时,然后是新的页面 2 表格,依此类推..
我想以响应式的方式进行,而不是像 (http://mobile/blah.com)
这样的子域
PS:我想避免第三方包!!
建议、建议、指导任何对我有帮助的东西
我的控制器:
public function ownerRegisterAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$owner = new Owner();
$form = $this->createCreateForm($owner);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($owner->getOwner(), $owner->getOwner()->getPassword());
$owner->getOwner()->setPassword($password);
$owner->getOwner()->setStatus('owner');
$owner->getOwner()->setIsValid(0);
$em->persist($owner);
$em->flush();
// Login users after registration
$this->get('apx_auth_after_register')->authenticateUser($tenant->getTenant());
$response = $this->forward('PagesBundle:SearchOwner:search', ['owner' => $owner]);
return $response;
}
return $this->render('::form/owner-register.html.twig', array(
'form' => $form->createView()
));
}
我的表格类型:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('corporate', CorporateType::class, ['expanded' => true, 'multiple' => false, 'label' => false])
//->add('postcode', NumberType::class,['label' => false])
->add('type', TypeType::class, ['expanded' => true, 'multiple' => false,'label' => false])
->add('room', NbRoomType::class,['expanded' => true, 'multiple' => false,'label' => false])
->add('rent', NumberType::class,['label' => false])
->add('area', NumberType::class,['label' => false])
->add('images', CollectionType::class, [
'entry_type' => ImagesFlatType::class,
'allow_add' => true,
'required' => false,
'allow_delete' => true,
'label' => false,
])
->add('fee', NumberType::class, ['label' => false, 'required' => false])
// to be defined in list by city
->add('transport', TextType::class,['label' => false, 'required' => false])
->add('furnished', FurnishedType::class, ['expanded' => true, 'multiple' => false,'label' => false])
->add('vip', LesVipType::class, ['expanded' => true, 'multiple' => true,'label' => false])
->add('feature', FeatureType::class, ['expanded' => true, 'multiple' => true,'label' => false])
->add('description', TextareaType::class,['label' => false, 'required' => false])
// City ajax call to be fix
->add('location', EntityType::class, ['label' => false,
'class' => 'PagesBundle:City',
'choice_label' => 'zone']);
谢谢大家
妮可
好吧,我认为这不是一项艰巨的任务。实际上这似乎有些明显(至少对我而言)。
您可以使用 TabView 或 Accordion-like 视图之类的东西。所有这些都可以通过使用纯 CSS(也许 Javascript)来实现。
如您所见,它与 Symfony 完全无关。通过使用 CSS + 媒体查询,我相信你可以完成所需的 UI.
如果您想使用响应方式拆分表单,您应该在视图中格式化表单输出(例如 TWIG 文件)。将表单部件放在单独的容器中,然后使用 CSS 媒体查询和 JS。祝你好运!
几年前我做了一些事情,其中将一个 'step' var 传递到 formType 中,我在每个 post.
之后递增它
我在 formType 中使用了 if 语句来根据步骤值构建表单。
没有移动子域,只有 css 帮助
当您使用 FormBuilder::add()
时,您创建了一个 FormType 实例,它可以是:
- 整个表格,
- 一个字段,
- 一个子表单。
所以他们的行为都是一样的。
然后您可以轻松地将 FormType 拆分为 3 个 SubFormTypes :
namespace AppBundle\Form;
/*
* When forms get heavy, it becomes handy to use aliases
* on import use statements since it eases the reading in code
* and reduces the list of imports.
*/
use Type as AppType;
use Symfony\Bridge\Doctrine\Form\Type as OrmType;
use Symfony\Component\Form\Extension\Core\Type as CoreType;
// An abstract class which all sub form will extend.
abstract class AbstractSubFormType extends AbstractType
{
/*
* We need to add a submit button on the sub form
* which will be only visible on mobile screens.
*
* Then to get help of css class :
* use ".submit-sub_owner { display: 'none'; }" for desktop screens,
* and ".submit-sub_owner { display: 'block'; }" for mobile screens.
*
* Plus we need dynamic hiding of next sub forms from controller :
* use ".sub_owner-next { display: 'none'; }" for mobile screens.
*/
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
$builder->add('submit', CoreType\SubmitType::class,
'attr' => array(
// Needed to target sub owner controller.
'formaction' => $options['sub_action'],
// Hides sub owners submit button on big screens.
'class' => 'submit_sub_owner',
)
));
}
public function configureOptions(OptionsResolver $resolver)
{
$subNextNormalizer = function (Options $options, $subNext) {
$hideNextSubOwners = isset($options['sub_next']) && $subNext;
// Sets "attr" option of this sub owner type.
$options['attr']['class'] = $hideNextSubOwners ? 'sub_owner-next' : '';
}
// Allows custom options.
$resolver->setDefaults(array('sub_action' => '', 'sub_next' => false));
// Hides sub owners exept the first stage from main controller.
$resolver->setNormalizer('sub_next', $subNextNormalizer);
}
}
// Stage 1
class SubOwnerOne extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
->add('corporate', AppType\CorporateType::class)
->add('type', AppType\TypeType::class)
->add('room', AppType\NbRoomType::class);
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('expanded' => true, 'multiple' => false));
}
public function getName()
{
return 'sub_owner_1';
}
}
// Stage 2
// note that this one is tricky because there is an image that may be a file.
class SubOwnerTwo extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
//->add('postcode')
->add('area')
->add('rent')
->add('images', CoreType\CollectionType::class, array(
'entry_type' => AppType\ImagesFlatType::class,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
));
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', CoreType\NumberType::class);
}
public function getName()
{
return 'sub_owner_2';
}
}
// Last stage
class SubOwnerThree extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
->add('fee', CoreType\NumberType::class, array('required' => false))
// to be defined in list by city
->add('transport', CoreType\TextType::class, array('required' => false))
->add('furnished', AppType\FurnishedType::class) // define options in class
->add('vip', AppType\LesVipType::class) // when belongs to AppType
->add('feature', AppType\FeatureType::class) // ...
->add('description', CoreType\TextareaType::class, array('required' => false))
// City ajax call to be fix
->add('location', OrmType\EntityType::class, array(
'class' => 'PagesBundle:City',
'choice_label' => 'zone',
));
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function getName()
{
return 'sub_owner_3';
}
}
一个 FormType 将它们全部包装起来 :
class OwnerType extends AbstractType
{
public function createForm(FormBuilderInterface $builder, array $options = array())
{
$sub = isset($option['sub_action']) ? $options['sub_action'] : false;
$next = isset($option['sub_next']) ? $options['sub_next'] : false;
$builder
->add('stage_one', AppType\SubOwnerOne::class, array(
'sub_action' => $sub, // get form action from controllers.
))
->add('stage_two', AppType\SubOwnerTwo::class, array(
'sub_action' => $sub,
'sub_next' => $next, // hide sub owners from main controller on mobile screens.
))
->add('final', AppType\SubFormTypeThree::class, array(
'sub_action' => $sub,
'sub_next' => $next,
))
->add('submit', CoreType\SubmitType::class, array(
'attr' => array(
// Allows using ".submit-owner { display: 'none'; }" for mobile screens.
'class' => 'submit-owner',
),
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'label' => false,
// Allows custom options
'sub_action' => '',
'sub_next' => false,
));
}
// ...
}
控制器:
// Holds two controllers, one for owner type and another for sub owner types.
class OwnerController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/*
* Main controller.
*
* This handles initial request
* and renders a view with a form view of OwnerType
*/
public function ownerRegisterAction(Request $request)
{
$owner = new Owner();
// Customizes options
$form = $this->createFormBuilder($owner, array(
// Uses route name of the other controller
'sub_action' => $this->generateUrl('handle_sub_owners'),
// Hides next stages
'sub_next' => true,
))->getForm();
$form->handleRequest($request);
if($form->isSubmitted && $form->isValid()) {
$password = $this->get('security.password_encoder')
->encodePassword($owner->getOwner(), $owner->getOwner()->getPassword());
$owner->getOwner()->setPassword($password);
$owner->getOwner()->setStatus('owner');
$owner->getOwner()->setIsValid(0);
$em = $this->getDoctrine()->getManager();
$em->persist($owner);
$em->flush();
// Login users after registration
$this->get('apx_auth_after_register')->authenticateUser($tenant->getTenant());
// Forwards quiting registration process
return $this->forward('PagesBundle:SearchOwner:search', array('owner' => $owner));
}
// This view should use css rules from above comments
return $this->render('::form/owner-register.html.twig', array(
'form' => $form->createView(),
));
}
/**
* Secondary controller handles sub forms
* and renders one form view among the sub forms.
*
* @Route('/_sub_owners', name="handle_sub_owners")
*/
public function handleSubOwnersAction(Request $request)
{
// Customize sub form action
$action = array('sub_action' => $this->generateUrl('handle_sub_owners'));
// Option "sub_next" will default to false.
$form = $this->createForm(AppType\OwnerType::class, new Owner(), $action);
$form->handleRequest($request);
$subOwner1 = $form->get('stage_one');
$subOwner2 = $form->get('stage_two');
$finalOwner = $form->get('final');
// Last stage is done, reforwards to the main controller.
if ($finalOwner->isSubmitted() && $finalOwner->isValid()) {
// Submits $data to new OwnerType as $form has been submitted by "handleRequest()" call
$owner = $this->createForm(AppType\OwnerType::class, new Owner());
$owner->get('stage_one')->submit(json_decode($finalOwner->get('j_stage_one')->getData()));
$owner->get('stage_two')->submit(json_decode($finalOwner->get('j_stage_two')->getData()));
$owner->get('final')->submit($finalOwner->getData());
// Form in main controller will handle the request again,
// so we need to pass normalized data.
return $this->forward('App:Owner:ownerRegister', array(), $owner->getNormData())
}
// Stage 2 is done
if ($subOwner2->isSubmitted() && $subOwner2->isValid()) {
// Gets back json of stage 1
$finalOwner->add('j_stage_one', 'hidden', array(
// Unmaps this hidden field as it won't match any property of Owner
'mapped' => false,
'data' => $subOwner1->get('j_stage_1')->getData(),
));
// Saves json of stage 2
$finalOwner->add('j_stage_two', 'hidden', array(
'mapped' => false,
'data' => json_encode($subOwner2->getData(), true),
));
// Renders final stage
return $this->render('::form/owner-register.html.twig', array(
'form' => $finalOwner->createView(),
));
}
// Stage 1 is done
if ($subOwner1->isSubmitted() && $subOwner1->isValid()) {
// Save json of $subOwner1
$subOwner2->add('j_stage_one', 'hidden', array(
'mapped' => false,
'data' => json_encode($subOwner1->getData(), true),
));
// Render stage 2
return $this->render('::form/owner-register.html.twig', array(
'form' => $subOwner2->createView(),
));
}
// Else renders stage 1
return $this->render('::form/owner-register.html.twig', array(
'form' => $subOwner1->createView(),
));
}
}
查看
{# ::form/owner-register.html.twig #}
...
<style type="text/css">
/* @media mobile screens */
.sub_owner-next, .submit-owner { display: 'none'; }
.submit-sub_owner { display: 'block'; }
/* @media desktop screens */
.submit-sub_owner { display: 'none'; }
</style>
...
{{ form_start(form) }}
{% form sub_form in form %}
{{ form_start(sub_form) }}
{{ form_widget(sub_form) }}
{{ form_end(sub_form) }}
{% endear %}
{{ form_end(form) }}
...
替代使用移动检测
有很多 php 库可以检测移动浏览器。
例如https://github.com/serbanghita/Mobile-Detect
它为 symfony 提供了一个包:
https://github.com/suncat2000/MobileDetectBundle
因此,您甚至可以在之前的实现中使用助手 :
$mobileDetector = $this->get('mobile_detect.mobile_detector');
$mobileDetector->isMobile();
$mobileDetector->isTablet()
我有大表格,我需要在移动设备上将其拆分为多个表格。
桌面 = 1 个大表格
Mobile = 2-3 更小的表格,当我验证 1 表格时,然后是新的页面 2 表格,依此类推..
我想以响应式的方式进行,而不是像 (http://mobile/blah.com)
这样的子域PS:我想避免第三方包!!
建议、建议、指导任何对我有帮助的东西
我的控制器:
public function ownerRegisterAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$owner = new Owner();
$form = $this->createCreateForm($owner);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($owner->getOwner(), $owner->getOwner()->getPassword());
$owner->getOwner()->setPassword($password);
$owner->getOwner()->setStatus('owner');
$owner->getOwner()->setIsValid(0);
$em->persist($owner);
$em->flush();
// Login users after registration
$this->get('apx_auth_after_register')->authenticateUser($tenant->getTenant());
$response = $this->forward('PagesBundle:SearchOwner:search', ['owner' => $owner]);
return $response;
}
return $this->render('::form/owner-register.html.twig', array(
'form' => $form->createView()
));
}
我的表格类型:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('corporate', CorporateType::class, ['expanded' => true, 'multiple' => false, 'label' => false])
//->add('postcode', NumberType::class,['label' => false])
->add('type', TypeType::class, ['expanded' => true, 'multiple' => false,'label' => false])
->add('room', NbRoomType::class,['expanded' => true, 'multiple' => false,'label' => false])
->add('rent', NumberType::class,['label' => false])
->add('area', NumberType::class,['label' => false])
->add('images', CollectionType::class, [
'entry_type' => ImagesFlatType::class,
'allow_add' => true,
'required' => false,
'allow_delete' => true,
'label' => false,
])
->add('fee', NumberType::class, ['label' => false, 'required' => false])
// to be defined in list by city
->add('transport', TextType::class,['label' => false, 'required' => false])
->add('furnished', FurnishedType::class, ['expanded' => true, 'multiple' => false,'label' => false])
->add('vip', LesVipType::class, ['expanded' => true, 'multiple' => true,'label' => false])
->add('feature', FeatureType::class, ['expanded' => true, 'multiple' => true,'label' => false])
->add('description', TextareaType::class,['label' => false, 'required' => false])
// City ajax call to be fix
->add('location', EntityType::class, ['label' => false,
'class' => 'PagesBundle:City',
'choice_label' => 'zone']);
谢谢大家
妮可
好吧,我认为这不是一项艰巨的任务。实际上这似乎有些明显(至少对我而言)。
您可以使用 TabView 或 Accordion-like 视图之类的东西。所有这些都可以通过使用纯 CSS(也许 Javascript)来实现。
如您所见,它与 Symfony 完全无关。通过使用 CSS + 媒体查询,我相信你可以完成所需的 UI.
如果您想使用响应方式拆分表单,您应该在视图中格式化表单输出(例如 TWIG 文件)。将表单部件放在单独的容器中,然后使用 CSS 媒体查询和 JS。祝你好运!
几年前我做了一些事情,其中将一个 'step' var 传递到 formType 中,我在每个 post.
之后递增它我在 formType 中使用了 if 语句来根据步骤值构建表单。
没有移动子域,只有 css 帮助
当您使用 FormBuilder::add()
时,您创建了一个 FormType 实例,它可以是:
- 整个表格,
- 一个字段,
- 一个子表单。
所以他们的行为都是一样的。
然后您可以轻松地将 FormType 拆分为 3 个 SubFormTypes :
namespace AppBundle\Form;
/*
* When forms get heavy, it becomes handy to use aliases
* on import use statements since it eases the reading in code
* and reduces the list of imports.
*/
use Type as AppType;
use Symfony\Bridge\Doctrine\Form\Type as OrmType;
use Symfony\Component\Form\Extension\Core\Type as CoreType;
// An abstract class which all sub form will extend.
abstract class AbstractSubFormType extends AbstractType
{
/*
* We need to add a submit button on the sub form
* which will be only visible on mobile screens.
*
* Then to get help of css class :
* use ".submit-sub_owner { display: 'none'; }" for desktop screens,
* and ".submit-sub_owner { display: 'block'; }" for mobile screens.
*
* Plus we need dynamic hiding of next sub forms from controller :
* use ".sub_owner-next { display: 'none'; }" for mobile screens.
*/
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
$builder->add('submit', CoreType\SubmitType::class,
'attr' => array(
// Needed to target sub owner controller.
'formaction' => $options['sub_action'],
// Hides sub owners submit button on big screens.
'class' => 'submit_sub_owner',
)
));
}
public function configureOptions(OptionsResolver $resolver)
{
$subNextNormalizer = function (Options $options, $subNext) {
$hideNextSubOwners = isset($options['sub_next']) && $subNext;
// Sets "attr" option of this sub owner type.
$options['attr']['class'] = $hideNextSubOwners ? 'sub_owner-next' : '';
}
// Allows custom options.
$resolver->setDefaults(array('sub_action' => '', 'sub_next' => false));
// Hides sub owners exept the first stage from main controller.
$resolver->setNormalizer('sub_next', $subNextNormalizer);
}
}
// Stage 1
class SubOwnerOne extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
->add('corporate', AppType\CorporateType::class)
->add('type', AppType\TypeType::class)
->add('room', AppType\NbRoomType::class);
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('expanded' => true, 'multiple' => false));
}
public function getName()
{
return 'sub_owner_1';
}
}
// Stage 2
// note that this one is tricky because there is an image that may be a file.
class SubOwnerTwo extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
//->add('postcode')
->add('area')
->add('rent')
->add('images', CoreType\CollectionType::class, array(
'entry_type' => AppType\ImagesFlatType::class,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
));
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', CoreType\NumberType::class);
}
public function getName()
{
return 'sub_owner_2';
}
}
// Last stage
class SubOwnerThree extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
->add('fee', CoreType\NumberType::class, array('required' => false))
// to be defined in list by city
->add('transport', CoreType\TextType::class, array('required' => false))
->add('furnished', AppType\FurnishedType::class) // define options in class
->add('vip', AppType\LesVipType::class) // when belongs to AppType
->add('feature', AppType\FeatureType::class) // ...
->add('description', CoreType\TextareaType::class, array('required' => false))
// City ajax call to be fix
->add('location', OrmType\EntityType::class, array(
'class' => 'PagesBundle:City',
'choice_label' => 'zone',
));
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function getName()
{
return 'sub_owner_3';
}
}
一个 FormType 将它们全部包装起来 :
class OwnerType extends AbstractType
{
public function createForm(FormBuilderInterface $builder, array $options = array())
{
$sub = isset($option['sub_action']) ? $options['sub_action'] : false;
$next = isset($option['sub_next']) ? $options['sub_next'] : false;
$builder
->add('stage_one', AppType\SubOwnerOne::class, array(
'sub_action' => $sub, // get form action from controllers.
))
->add('stage_two', AppType\SubOwnerTwo::class, array(
'sub_action' => $sub,
'sub_next' => $next, // hide sub owners from main controller on mobile screens.
))
->add('final', AppType\SubFormTypeThree::class, array(
'sub_action' => $sub,
'sub_next' => $next,
))
->add('submit', CoreType\SubmitType::class, array(
'attr' => array(
// Allows using ".submit-owner { display: 'none'; }" for mobile screens.
'class' => 'submit-owner',
),
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'label' => false,
// Allows custom options
'sub_action' => '',
'sub_next' => false,
));
}
// ...
}
控制器:
// Holds two controllers, one for owner type and another for sub owner types.
class OwnerController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/*
* Main controller.
*
* This handles initial request
* and renders a view with a form view of OwnerType
*/
public function ownerRegisterAction(Request $request)
{
$owner = new Owner();
// Customizes options
$form = $this->createFormBuilder($owner, array(
// Uses route name of the other controller
'sub_action' => $this->generateUrl('handle_sub_owners'),
// Hides next stages
'sub_next' => true,
))->getForm();
$form->handleRequest($request);
if($form->isSubmitted && $form->isValid()) {
$password = $this->get('security.password_encoder')
->encodePassword($owner->getOwner(), $owner->getOwner()->getPassword());
$owner->getOwner()->setPassword($password);
$owner->getOwner()->setStatus('owner');
$owner->getOwner()->setIsValid(0);
$em = $this->getDoctrine()->getManager();
$em->persist($owner);
$em->flush();
// Login users after registration
$this->get('apx_auth_after_register')->authenticateUser($tenant->getTenant());
// Forwards quiting registration process
return $this->forward('PagesBundle:SearchOwner:search', array('owner' => $owner));
}
// This view should use css rules from above comments
return $this->render('::form/owner-register.html.twig', array(
'form' => $form->createView(),
));
}
/**
* Secondary controller handles sub forms
* and renders one form view among the sub forms.
*
* @Route('/_sub_owners', name="handle_sub_owners")
*/
public function handleSubOwnersAction(Request $request)
{
// Customize sub form action
$action = array('sub_action' => $this->generateUrl('handle_sub_owners'));
// Option "sub_next" will default to false.
$form = $this->createForm(AppType\OwnerType::class, new Owner(), $action);
$form->handleRequest($request);
$subOwner1 = $form->get('stage_one');
$subOwner2 = $form->get('stage_two');
$finalOwner = $form->get('final');
// Last stage is done, reforwards to the main controller.
if ($finalOwner->isSubmitted() && $finalOwner->isValid()) {
// Submits $data to new OwnerType as $form has been submitted by "handleRequest()" call
$owner = $this->createForm(AppType\OwnerType::class, new Owner());
$owner->get('stage_one')->submit(json_decode($finalOwner->get('j_stage_one')->getData()));
$owner->get('stage_two')->submit(json_decode($finalOwner->get('j_stage_two')->getData()));
$owner->get('final')->submit($finalOwner->getData());
// Form in main controller will handle the request again,
// so we need to pass normalized data.
return $this->forward('App:Owner:ownerRegister', array(), $owner->getNormData())
}
// Stage 2 is done
if ($subOwner2->isSubmitted() && $subOwner2->isValid()) {
// Gets back json of stage 1
$finalOwner->add('j_stage_one', 'hidden', array(
// Unmaps this hidden field as it won't match any property of Owner
'mapped' => false,
'data' => $subOwner1->get('j_stage_1')->getData(),
));
// Saves json of stage 2
$finalOwner->add('j_stage_two', 'hidden', array(
'mapped' => false,
'data' => json_encode($subOwner2->getData(), true),
));
// Renders final stage
return $this->render('::form/owner-register.html.twig', array(
'form' => $finalOwner->createView(),
));
}
// Stage 1 is done
if ($subOwner1->isSubmitted() && $subOwner1->isValid()) {
// Save json of $subOwner1
$subOwner2->add('j_stage_one', 'hidden', array(
'mapped' => false,
'data' => json_encode($subOwner1->getData(), true),
));
// Render stage 2
return $this->render('::form/owner-register.html.twig', array(
'form' => $subOwner2->createView(),
));
}
// Else renders stage 1
return $this->render('::form/owner-register.html.twig', array(
'form' => $subOwner1->createView(),
));
}
}
查看
{# ::form/owner-register.html.twig #}
...
<style type="text/css">
/* @media mobile screens */
.sub_owner-next, .submit-owner { display: 'none'; }
.submit-sub_owner { display: 'block'; }
/* @media desktop screens */
.submit-sub_owner { display: 'none'; }
</style>
...
{{ form_start(form) }}
{% form sub_form in form %}
{{ form_start(sub_form) }}
{{ form_widget(sub_form) }}
{{ form_end(sub_form) }}
{% endear %}
{{ form_end(form) }}
...
替代使用移动检测
有很多 php 库可以检测移动浏览器。
例如https://github.com/serbanghita/Mobile-Detect 它为 symfony 提供了一个包: https://github.com/suncat2000/MobileDetectBundle
因此,您甚至可以在之前的实现中使用助手 :
$mobileDetector = $this->get('mobile_detect.mobile_detector');
$mobileDetector->isMobile();
$mobileDetector->isTablet()