Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle Symfony form collection with 500+ items

I have form collection which need to handle more than 500 entity instances. After I increased timeout to 60s and increased max_input_vars form work but it is annoying how slow it is. Rendering form is slow but submitting that big form is pain in the ass.

I was considering creating plain HTML form but there is some other drawback suck as validation. So, is there any proper way to handle that big set of data via symfony form ?

CONTROLLER:

public function ratesCardAction() {
    $bannerList = $this->data;

    $em = $this->getDoctrine()->getManager();
    $form = $this->createForm(new AdvertiserRatesType($bannerList));
    if ('POST' == $this->getRequest()->getMethod()) {
        $form->handleRequest($this->getRequest());
        $advertiserCampaign = $form->getData();
        if ($form->isValid()) {
            foreach ($advertiserCampaign['campaignsAdZones'] as $campaignAdZone) {
               $em->persist($campaignAdZone);
            }
            $em->flush();
        }
    }

    return array(
        'form'   => $form->createView()
    );
}



class AdvertiserRatesType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder ->add('campaignsAdZones', 'collection', array(
            'type'   => new AdvertiserRatePerCountryType(),
            'data'   => $this->rates,
            'empty_data'  => null,
            'options'  => array(
                'attr' => array('class' => 'campaignAdZoneItem')
            )
        ))
        ;
    }

}

...

and embedded form looks like:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('class', 'entity', array(
                'class' => 'AcmeCmsBundle:PublisherTypes',
                'property' => 'class',
                'read_only' => true,
                'disabled' => true
            )
        )
        ->add('country', 'entity', array(
                'class' => 'AcmeCmsBundle:Countries',
                'property' => 'name',
            )
        )
        ->add('text1')
        ->add('text2')
    ;
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Acme\CmsBundle\Entity\Rates'
    ));
}
like image 609
Milos Avatar asked Sep 10 '13 12:09

Milos


1 Answers

Indeed using whole entities to collection with over 500 items is an overkill ;)

First: use pure arrays instead of entities, set 'data_class' => null in setDefaultOptions method

Second: You do not want to create over 500 new items on one page form, do You? ;) If it is really needed for some reason and it has to be done like that... well currently $advertiserCampaign = $form->getData(); in Your code will return over 500 objects - HEAVY. Instead of that, $advertiserCampaign = $form->getData(); should return 500 arrays of data and THEN inside foreach You should create new object and bind data to it (e.g. using datatransfomer). Validation still may be handled with no problem - just set validators for each field inside form class.

Third: I hope that $this->rates is not another collection of objects - right? In case it is - use primitive array instead of heavy objects.

Fourth: 500 objects/forms on one page? REALLY? Might it be refactored somehow? Maybe pagination and e.g. 20 forms per page? If You really, really need to save 500 objects from one form in one request, then I suggest, to use some Queue handler like RabbitMQ or Gearman to save all 500 objects in database.

I hope that helps somehow.

like image 117
adam.pilacki Avatar answered Nov 12 '22 17:11

adam.pilacki