Symfony - Twig中的一个表单适用于多个地址

4
首先,我想澄清一下,我不是在使用Entities/Doctrine处理我的表单(或其他内容)。我正在使用Web服务来添加/编辑数据库中的数据字段。

我正在开发一个需要编辑具有多个地址的公司信息的项目。第一个地址始终是物理地址,因此我在与公司数据相同的循环中获取该地址。

但是,第二个(和第三个,如果存在)地址放置在单独的数据库条目中。 我能够使用单个表单编辑公司数据和公司地址(物理地址),因为数据都在一个集合中。因此,我只需要一个表单,并且不必使用循环来处理地址。


现在,我想编辑第二个地址。我在twig中循环遍历地址并打印出该公司的所有其他地址。 正如我所知道的那样,Symfony不允许同一表单的多个实例,因为这会干扰ID等。

问题是我应该如何仅呈现特定地址的表单。我希望通过单击按钮来编辑它(使用Bootstrap Collapse隐藏表单,如果不进行编辑)

我的代码如下:

控制器:

public function editcompany(Request $request, $klantnr)
{

    [...]

    $result = [
        'gegevens' => [],
        'addresses' => [],
    ];

    foreach ($company as $row) {
        $dataFields = [
            [...] // The data fields of the company (including the physical address)
        ];
        foreach ($dataFields as $dataField) {
            if (empty($result['gegevens'][$dataField])) {
                $result['gegevens'][$dataField] = $row[$dataField];
            }
        }
        if($row['AdrGid'] != $result['gegevens']['AdrGid']) {
            $result['addresses'][$row['AdrGid']] = [
                [...] // The data fields for the other addresses 
            ];
        }
    }

    // The form creation of the company data form
    $form = $this->createForm(EditcompanyForm::class);

    // The form of the other addresses
    $form_adres = $this->createForm(EditcompanyAdresForm::class, array(
        [...]
    ));
    $form->handleRequest($request);
    $form_adres->handleRequest($request);


    if($form->isSubmitted() && $form->isValid()) {
        [...] // Handling the submitted data for the company data
    }

    // GEKOPPELDE ADRESSEN company
    if($form_adres->isSubmitted() && $form_adres->isValid()) {

        [...]

        $data = array(
            [...] // Setting the data for the other addresses
        );

        [...]

        // Passing the data and other options to the webservices
        $this->get('http')->companyRequest($data, $admincode, $service, $token);

        $request->getSession()
            ->getFlashBag()
            ->add('success', 'Linked address successfully edited');

        return $this->redirect($request->getUri());
    }

    return $this->render('pages/company/edit.html.twig', array(
        'client' => $result,
        'form_company' => $form->createView(),
        'form_company_adres' => $form_adres->createView()
    ));
 }

表单构建器(AbstractType):

class EditcompanyAdresForm extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $factuuradres = $options['NawFactuurAdres'];
        $lands = $options['lands'];

        $data = array();
        $builder
            [...] // Add the necessary fields
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            [...] // setting defaults here
        ));
    }
    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_form_editcompanyadresform';
    }
}

Twig模板(显示地址):

{% for address in client.addresses %}
    <div class="right-cont-grey">
        [...] // showing addresses
    </div>
{% endfor %}

Twig 模板(编辑表单):

{% for address in client.addresses %}
    {{ form_start(form_company_adres, {'  method': 'POST'}) }}
    {{ form_row(form_company_adres._token) }}
        <section class="collapse" id="adres_plus">
            <div id="adres" class="new-adres collapse right-cont-grey">
                <div class="col-sm-12">
                    [...] // Form_widgets with values from database
            </div>
        </section>
    {{ form_end(form_company_adres) }}
{% endfor %}

简而言之:

如何在不干扰的情况下为每个地址使用地址表单?也许可以在twig文件中的循环内创建视图(渲染)? 我的表单预先填充了来自数据库的数据,以便我可以进行编辑。


这听起来很像你可以使用collection字段类型。你试过了吗? - Yoshi
一般情况下,表单不需要任何ORM实体。虽然使用真实的模型类会有很大帮助,但这些模型不一定是实际保存到数据库中的实体。我经常在特殊情况下使用DTO(数据传输对象)来简化表单处理。 - Yoshi
不需要:)我只是想给你一些关于集合类型的参考。目前来看,它似乎完全符合你的要求。 - Yoshi
@Yoshi,你介意为我的情况分享一个例子吗?它看起来仍然有点复杂,我不知道该如何开始。 - Kees Sonnema
@Yoshi,关于添加,我计划添加一个单独的表单操作。目前还没有删除地址的功能,但将来可能会添加。本该如此,但目前网络服务不允许我这样做。 - Kees Sonnema
显示剩余4条评论
2个回答

2

在我们的聊天中讨论后修订的答案:

正如我在评论中所写的那样,我相信您只需要设置一个集合字段。然后整个复杂性基本上就消失了。

一个非常简单的例子是:

控制器

public function editCompanyAction(Request $request)
{
    // get the data anyway you see fit and produce a model (this could also be a dedicated DTO)
    $company = $this->getInput();

    // create the form
    $form = $this
        ->createForm(CompanyWithAddressesType::class, $company)
        ->handleRequest($request)
    ;

    // handle submission
    if ($form->isSubmitted() && $form->isValid()) {
        dump($form->getData()); // === $company
    }

    return $this->render('default/index.html.twig', [
        'form' => $form->createView(),
    ]);
}

表单

重要的一点是,您添加的字段名称必须与提供为数据的数组中的键匹配!

class CompanyWithAddressesType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // combine the company with a collection of addresses:
        $builder
            ->add('gegevens', CompanyType::class)
            ->add('addresses', CollectionType::class, [
                'entry_type' => AddressType::class,
            ])
        ;
    }
}

class CompanyType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // all the fields that are *extra* for companies, address fields are inherited
        // because this type extends the address type (see below getParent)
        $builder
            ->add('NawWebsite', TextType::class)
            ->add('email1', TextType::class)
            ->add('AdrNaam1', TextType::class)
        ;
    }

    /**
     * @return @inheritdoc
     */
    public function getParent()
    {
        return AddressType::class;
    }
}

class AddressType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // all the fields that define an address
        $builder
            ->add('AdrStraat', TextType::class)
            ->add('AdrHuisnummer', TextType::class)
            ->add('AdrPostcode', TextType::class)
            ->add('NawFactuurAdres', CheckboxType::class)
        ;
    }
}

Twig:

仅仅输出表单,任何表单自定义都适用。

{{ form_start(form) }}
    <div>
        <h4>Company</h4>
        {{ form_row(form.gegevens.NawWebsite) }}
        {{ form_row(form.gegevens.email1) }}
        {{ form_row(form.gegevens.AdrNaam1) }}
    </div>

    <div>
        <h4>Main address</h4>
        {{ form_rest(form.gegevens) }}
    </div>

    <div>
        <h4>Extra addresses</h4>
        {% for address in form.addresses %}
            {{ form_errors(address) }}
            {{ form_widget(address) }}
        {% endfor %}
    </div>

    <button>Submit</button>
{{ form_end(form) }}

对我来说很难将这些模型翻译成我的代码,因为我在项目中根本没有使用Symfony模型结构。我拥有的是一个控制器,它获取表单(从我的AbstractType Formbuilder),一个模型类(仅是一个从数据库查询数据并返回给控制器的文件)和一个AbstractType类,在其中构建我的表单。我在Twig模板中填充字段(这部分应该像您一样在控制器中完成)。 - Kees Sonnema
我理解,但我没有看到问题所在。就像我写的那样,在控制器的某个点上,无论它是如何产生的,你都应该有一个代表公司及其地址的单一值。然后这个值将被表单操作。例如,使用一些中间模型(DTO),你可以即时创建实际数据。然后将该DTO传递给表单。在提交时,使用模型中的数据来更新实际数据(例如,使用自定义逻辑)。这与Symfony模型结构无关,你只需要一个控制器和一个表单。 - Yoshi
但是要明确的是,你使用 $foo、$bar、$baz 等变量所做的事情基本上是我使用 $result['addresses'][$row['AdrGid']] = [ [...] ]; 所做的事情的替代方法,例如获取要放入表单字段中的数据(但这是在 Twig 中完成的,而不是在控制器中完成的)。我应该阅读有关数据转换器的资料。 - Kees Sonnema
当我第一次开始使用Symfony时,我也陷入了同样的陷阱。所有文档都使用model/entity,并且似乎到处都是Doctrine的“东西”。因此,让我明确一下,你几乎没有必要使用Doctrine。通常,当谈论“模型”时,它只是指您可以命名的某个数据容器。通常,PHP类是最容易使用的东西,但该类不需要是Doctrine实体。 - Yoshi
让我们在聊天室继续这个讨论:http://chat.stackoverflow.com/rooms/161437/discussion-between-yoshi-and-kees-sonnema。 - Yoshi
显示剩余2条评论

0
我认为您需要在循环之外放置表单,然后有一个隐藏的输入字段来标识需要在提交时保存的地址。因此,当其他字段未显示时,它们不会被提交。 这意味着您需要一些JavaScript逻辑来显示和隐藏地址。

Twig模板(编辑表单):

{{ form_start(form_company_adres, {'  method': 'POST'}) }}
{% for address in client.addresses %}
    <div id="address-{{ loop.index }}" class="address">
        <input type="hidden" name="address-number" value="{{ loop.index }}"/>
        {{ form_row(form_company_adres._token) }}
            <section class="collapse" id="adres_plus">
                <div id="adres" class="new-adres collapse right-cont-grey">
                    <div class="col-sm-12">
                        [...] // Form_widgets with values from database
                </div>
            </section>
    </div>
{% endfor %}
{{ form_end(form_company_adres) }}

好的,我有一个问题。使用这段代码无法确定地址的ID,因此我无法返回正确的数据。我应该加一个第二个隐藏字段吗? - Kees Sonnema
FYI,它总是打印地址-1`。我可能漏掉了什么,但在此之前我无法弄清楚。 - Kees Sonnema
啊,抱歉,请将loop.index替换为地址的ID。然后您应该确定哪个地址已保存。如果这些是来自数据库的自动递增ID,请确保检查用户是否被允许更改地址,以避免用户更改不同用户的地址而导致麻烦。顺便说一句,loop.index不应始终为1,请在此处进行检查https://twig.symfony.com/doc/2.x/tags/for.html#the-loop-variable - Kingson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接