0
votes

I have the following (simplified) table:

Service:
  product_name
  number_of_clients

After someone selects and saves Service A with 3 clients and Service B with 2 clients I must display a page with details for Service A and input fields (name & email) for the 3 clients then details for Service B and input fields for 2 clients. From this page the informations about the clients are saved in another table in the database (imposed DB structure).

How can I create the random number of forms to display under each service and how can I properly access the data in the submitted form afterwards? All the forms are edited and saved at the same time, not individually.

I went over Advanced Forms but it didn't help much.

2
So, you have "Service has many Clients" schema and you want user to define a number of clients on the first page and input data for each client on the second page, right? - Dziamid

2 Answers

1
votes

Here's an example with this simple schema. You should be able to adjust it to your needs.

//schema.yml

Client:
  columns:
    name: string(255)
    service_id: integer
  relations:
    Service:
      local: service_id
      foreign: id
      foreignAlias: Clients

Service:
  columns:
    name: string(255)

//routing.yml

//i added this route to split 'new' action in 2 steps
service_choose:
  url: /service/choose
  param: {module: service, action: choose}

service:
  class: sfDoctrineRouteCollection
  options:
    model: Service
    module: service

//create a module based on service route collection (there's a task) and add choose action:
  public function executeChoose(sfWebRequest $request)
  {
    $form = new ServiceChooseForm();
    if (($method = $this->request->getMethod()) == 'POST')
    {
      $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));

      if ($form->isValid())
      {
        $total_clients = $form->getValue('total_clients');
        $this->redirect('@service_new?total_clients='.$total_clients);
      }      
    }
    $this->form = $form;
    $this->setTemplate('choose');
  }

You also need to create a custom sfForm form for choose action, where you set a number of clients.

class ServiceChooseForm extends sfForm
{
  public function configure()
  {
    $this->widgetSchema->setNameFormat('service[%s]');

    $this->setWidget('total_clients', new sfWidgetFormInput());
    $this->setValidator('total_clients', new sfValidatorInteger());
  }
}
//ServiceForm embeds your new clients forms based on total_clients option.
class ServiceForm extends BaseServiceForm
{
  public function configure()
  {
    //clients sub form
    $clientsForm = new sfForm();
    for ($i=0; $i<$this->getOption('total_clients',2); $i++)
    {
      $client = new Client();
      $client->Service = $this->getObject();

      $clientForm = new ClientForm($client);
      $clientsForm->embedForm($i, $clientForm);      
    }
    $this->embedForm('newClients', $clientsForm);
  }
}

Some more tweaks:

//chooseSuccess template, poinst a form to the same action:
<h1>New Service</h1>

<form action="<?php echo url_for('service_choose') ?>" method="post">
  <table>
    <tfoot>
      <tr>
        <td colspan="2">
          <?php echo $form->renderHiddenFields(false) ?>
          <input type="submit" value="Continue" />
        </td>
      </tr>
    </tfoot>
    <tbody>
      <?php echo $form ?>
    </tbody>
  </table>
</form>

//_form partial:

<form action="<?php echo url_for('@service_create?total_clients='. $form->getOption('total_clients')) ?>" method="post">

That's it. Hope you can put it all together =)

0
votes

Apologies if I'm giving an over-simple answer to your question, but you can of course name/declare the forms differently on the basis of the same form class:

$form_1 = $form_2 = $form_3 = new someForm();

If the number of forms is unknown, I'd just use some type of dynamic parameter or identifier to create a set of names you can work with when they're posted back by the user.

You can then access the data for each submitted form object separately (maybe through a loop or similar construct), just as you would do with a single form.