Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Populate Form with Related Table Data

Hi all!

I've been doing a lot of searching for a solution to this problem, but I'm not finding any solutions; people seem to just give up and/or find workarounds that are suitable for them. However, I'm hoping there's a more elegant way of solving this.

The problem is I have a two related tables in my db: a customer("name") and an address("street", "'street2", "street3", "city", "county", "postcode", "country_id") table. Obviously the customer references the address id when it pulls the data.

I have created a single form that has the fields for the customer, and I have included the fields for the address as follows:

    class CustomerForm extends Form
    {
    public function initialize($entity = null, $options = null)
    {
        if ($options['edit'] === true)
        {
            $this->add(new Hidden('id'));
            $params = [];
        }
        else
        {
            $params = [];
        }

        $field = new Text('name', array_merge($params, [
            'class' => 'form-control',
            'required' => 'required',
            'placeholder' => '{customer_name}'
        ]));
        $field->setLabel('{customer_name}');
        $this->add($field);

    ...

    $pre = 'Address.';
      for ($i=1; $i < 4; $i++) {
            $field = new Text($pre . 'street_' . $i, array_merge($params, [
                'class' => 'form-control',
                'required' => (($i === 1) ? 'required' : ''),
                'placeholder' => '{address} '. $i
            ]));
            $field->setLabel('{address}' . $i);
            $this->add($field);
        }

        $field = new Text($pre . 'city', array_merge($params, [
            'class' => 'form-control',
            'required' => 'required',
            'placeholder' => '{city}'
        ]));
        $field->setLabel('{city}');
        $this->add($field);

        $field = new Text($pre . 'county', array_merge($params, [
            'class' => 'form-control',
            'required' => 'required',
            'placeholder' => '{county}'
        ]));
        $field->setLabel('{county}');
        $this->add($field);

        $field = new Text($pre . 'postcode', array_merge($params, [
         'class' => 'form-control',
         'required' => 'required',
         'placeholder' => '{postcode}'
       ]));
        $field->setLabel('{postcode}');
        $this->add($field);

      $field = new Select($pre . 'country_id', Countries::find(array("order" => "printable_name")), array_merge($params, [
         'class' => 'form-control select2',
         'required' => 'required',
         'useEmpty' => true,
         'emptyText' => '{please_select}',
         'using' => ['id', 'printable_name']
      ]));
      $field->setLabel('{country}');
      $this->add($field);
    }

Now if in my controller I use:

    $customer = Customers::findFirstById($id);
    $form = new CustomersForm($customer, ['edit' => true]);

It will populate the fields from the root customer table, but I'm having issues populating the fields for the related table Addresses.

Does anyone have any bright ideas? Is this even possible?

I've used the $this->tag->setDefault() method previuosly, but i'm trying to keep away from having to write out every tag every time. I've also considered nesting the forms within each other, but I've been unable to do this.

Any help would be greatly appreciated.

Update 1:

So I decided to tackle the problem from the bottom up again and think about things more logically and perhaps a little more OOP.

As such I though that the better solution would be that the two tables would have two separate forms that cover just the fields from their respective model. Then in the controller I could call them both and assign them using the $this->view->form1 = new CustomerForm(); $this->view->form2 = new AddressForm(); and so on. Then in the view file I can use the $form1->render(); and $form2->render(); commands wherever i need them to build the form exactly how I want it to look.

And this works.... most of the time...

If I have a relationship where a->b, then this is fine i.e. profile & user. However, with my Customer table I can have 2 addresses: head office and a separate billing address (which may or may not be the same). "No problem!" I thought, "I'll just send a prefix option to the constructor of the form and then prefix the fields."

This does not work, as the data being passed is the entity data which is non-prefixed, but the form field names are prefixed. As such they do not match, therefore the fields do not pre-populate when you load the page. However, if I don't prefix them, then I will have two fields with the same name.

Anyone have any thoughts on how to get around this specific issue? i.e. how to prefix the data field names and the form field names?



9.0k
Accepted
answer

Hi an non documented functions on forms is getCustomValue() with this method you can handle the related records in the form

I hope it helps you. Good luck

@emiliodeg thanks for the tip. This worked like a dream!

What I've now got is a a definition for the getCustomValue() function in my form, that takes the $name passed to it and then strips out the prefix that has been defined in the form definition.

Then in the view, I can use $form->render('prefix_field_name'); and it will look up the entity field name field_name.

Much better way of doing this and always for the reuse of the forms within different sections of my application. Should mean that if I ever change the model for the form, I can update one form definition.