Symfony2动态地将attr分配给表单字段

时间:2022-10-16 18:16:05

I'm in a situation where I want to be able to dynamically set the required=true/false option or the array(...other stuff..., 'class' => ' hidden') of a form field.

我希望能够动态设置表单字段的required = true / false选项或数组(...其他东西......,'class'=>'hidden')。

The context is the following: there is a "Project" entity with some fields. When I create other entities with forms I want to check some attributes of the Project entity and make decisions on the visibility/requiredness of certain fields of the entity I'm creating.

上下文如下:有一个带有一些字段的“Project”实体。当我使用表单创建其他实体时,我想检查Project实体的某些属性,并对我正在创建的实体的某些字段的可见性/必要性做出决策。

For example if a Project is with attribute "timePressure=high" then a field of a given entity is not required (but is visible). If there are other conditions it becomes invisible etc...

例如,如果项目的属性为“timePressure = high”,则不需要给定实体的字段(但是可见)。如果还有其他条件,它会变得不可见等......

So basically I was hoping to call a function inside each ->add() of the form builder to spit out the relevant portions (e.g. that function would return a string with "required=true" or the other related to the hidden class). The thing is that the function should take as arguments: the projectID (ok, this can be passed as options of the form builder), the class and the field we are talking about to decide. I was envisioning something like:

所以基本上我希望在每个 - > add()表单构建器中调用一个函数来吐出相关部分(例如,该函数将返回一个字符串,其中“required = true”或另一个与隐藏类相关)。问题是该函数应该作为参数:projectID(好的,这可以作为表单构建器的选项传递),我们正在讨论的类和字段。我想象的是:

->add('background', 'textarea', array('attr' => array('rows' => 4, functionToDecideIfInvisible($projectId)), functionToDecideRequiredness($projectId)))

The two function would return the string 'class' => ' hidden' and required=true (or false)

这两个函数将返回字符串'class'=>'hidden'和required = true(或false)

I'd like to avoid to having to specify the field name (in this case background) to avoid code repetition.

我想避免必须指定字段名称(在本例中为后台)以避免代码重复。

Can this be done?

可以这样做吗?

Other suggestions on how to solve the thing?

关于如何解决这个问题的其他建议?

Thank you!

SN

2 个解决方案

#1


What you need are Form Events: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-underlying-data

您需要的是表格事件:http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-underlying-data

They allow you to modify your form based on your data.

它们允许您根据数据修改表单。

You create you project form in the controller:

您在控制器中创建项目表单:

$form = $this->createForm(new ProjectType(), $project, array(
    'action' => $this->generateUrl('project.edit'),
    'method' => 'POST',
));

Then you add the FormEvents::PRE_SET_DATA listener:

然后添加FormEvents :: PRE_SET_DATA侦听器:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $project = $event->getData();
        $form = $event->getForm();

        // check timePressure on the Project object
        if ('high' === $project->timePressure) {
            $form->add('timePressure', 'text', array(
                'required' => false
            );
        }
    });
}

#2


I found a way to do it.

我找到了办法。

My add would be like:

我的补充就像:

->add('background', 'textarea', array_merge($this->vr->fieldReq(get_class($this), 
'background', $projectID), array('attr' => array_merge(array('rows' => 4, ),
$this->vr->fieldCssClass(get_class($this), 'background', $projectID) ) )))

To do that I had to define the form as service, plus I created another class as service (the one which holds the two methods I need).

为此,我必须将表单定义为服务,另外我创建了另一个类作为服务(保存我需要的两个方法的类)。

This is the class:

这是班级:

class FieldReqAndCssClass
{
public function fieldReq($parentEntity, $field, $projectID)
    {
    $required = array();
    $required['required'] = false; //do stuff on the database instead of setting it to false hardcoded
    return $required;
    }

public function fieldCssClass($parentEntity, $field, $projectID)
    {
    $cssClass= array();
    //do stuff on the database instead of setting the class as hidden hardcoded
    //$cssClass['class'] = ' hidden';
    $cssClass['class'] = '';
    return $cssClass;
    }
}

Of course in my form I had to:

当然,在我的形式中,我必须:

public function __construct(\AppBundle\Util\FieldReqAndCssClass $fieldReqAndCssClass)
{
$this->vr = $fieldReqAndCssClass; // vr stands for visibility and requiredness
}

And these are the two services:

这些是两项服务:

app_bundle.field_req_and_css_class:
    class: AppBundle\Util\FieldReqAndCssClass
    arguments: []

app_bundle.form.type.projectdetail:
    class: AppBundle\Form\Type\ProjectDetailFormType
    arguments: [@app_bundle.field_req_and_css_class]
    tags:
        - { name: form.type, alias: ProjectDetail }

Of course here in the first service I'll need to inject the entity manager and add it to the construct, and probably also in the form service, but the basic skeleton is working :)

当然,在第一个服务中,我需要注入实体管理器并将其添加到构造中,也可能在表单服务中,但基本框架正在工作:)

I'm a happy man :)

我是一个快乐的人:)

EDIT The only problem of the above is that it makes hidden the widget but not the label. To fix it:

编辑上面唯一的问题是它隐藏了小部件而不是标签。要解决这个问题:

->add('background', 'textarea', array_merge($vr->fieldReq($myClass,
'background', $project), array('label_attr' => $vr->fieldCssClass($myClass, 
'background', $project),'attr' => array_merge(array('rows' => 4, ), 
$vr->fieldCssClass($myClass, 'background', $project) ) ))) 

Obviously before I have to declare:

显然在我必须申报之前:

$myClass = get_class($this);
$vr = $this->vr;

#1


What you need are Form Events: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-underlying-data

您需要的是表格事件:http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-underlying-data

They allow you to modify your form based on your data.

它们允许您根据数据修改表单。

You create you project form in the controller:

您在控制器中创建项目表单:

$form = $this->createForm(new ProjectType(), $project, array(
    'action' => $this->generateUrl('project.edit'),
    'method' => 'POST',
));

Then you add the FormEvents::PRE_SET_DATA listener:

然后添加FormEvents :: PRE_SET_DATA侦听器:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $project = $event->getData();
        $form = $event->getForm();

        // check timePressure on the Project object
        if ('high' === $project->timePressure) {
            $form->add('timePressure', 'text', array(
                'required' => false
            );
        }
    });
}

#2


I found a way to do it.

我找到了办法。

My add would be like:

我的补充就像:

->add('background', 'textarea', array_merge($this->vr->fieldReq(get_class($this), 
'background', $projectID), array('attr' => array_merge(array('rows' => 4, ),
$this->vr->fieldCssClass(get_class($this), 'background', $projectID) ) )))

To do that I had to define the form as service, plus I created another class as service (the one which holds the two methods I need).

为此,我必须将表单定义为服务,另外我创建了另一个类作为服务(保存我需要的两个方法的类)。

This is the class:

这是班级:

class FieldReqAndCssClass
{
public function fieldReq($parentEntity, $field, $projectID)
    {
    $required = array();
    $required['required'] = false; //do stuff on the database instead of setting it to false hardcoded
    return $required;
    }

public function fieldCssClass($parentEntity, $field, $projectID)
    {
    $cssClass= array();
    //do stuff on the database instead of setting the class as hidden hardcoded
    //$cssClass['class'] = ' hidden';
    $cssClass['class'] = '';
    return $cssClass;
    }
}

Of course in my form I had to:

当然,在我的形式中,我必须:

public function __construct(\AppBundle\Util\FieldReqAndCssClass $fieldReqAndCssClass)
{
$this->vr = $fieldReqAndCssClass; // vr stands for visibility and requiredness
}

And these are the two services:

这些是两项服务:

app_bundle.field_req_and_css_class:
    class: AppBundle\Util\FieldReqAndCssClass
    arguments: []

app_bundle.form.type.projectdetail:
    class: AppBundle\Form\Type\ProjectDetailFormType
    arguments: [@app_bundle.field_req_and_css_class]
    tags:
        - { name: form.type, alias: ProjectDetail }

Of course here in the first service I'll need to inject the entity manager and add it to the construct, and probably also in the form service, but the basic skeleton is working :)

当然,在第一个服务中,我需要注入实体管理器并将其添加到构造中,也可能在表单服务中,但基本框架正在工作:)

I'm a happy man :)

我是一个快乐的人:)

EDIT The only problem of the above is that it makes hidden the widget but not the label. To fix it:

编辑上面唯一的问题是它隐藏了小部件而不是标签。要解决这个问题:

->add('background', 'textarea', array_merge($vr->fieldReq($myClass,
'background', $project), array('label_attr' => $vr->fieldCssClass($myClass, 
'background', $project),'attr' => array_merge(array('rows' => 4, ), 
$vr->fieldCssClass($myClass, 'background', $project) ) ))) 

Obviously before I have to declare:

显然在我必须申报之前:

$myClass = get_class($this);
$vr = $this->vr;