zf2/zf3 Hydrate Multiple Fieldsets with Objects

1.5k Views Asked by At

Issue: multiple fieldsets in form are not populated/hydrated when using $form->bind($object). How do you populate 2 different fieldsets in a form vai 2 different entity objects? I have 2 fieldsets: FieldsetA, FieldsetB

A form RegisterFrom calls these in its init() method

class RegisterForm extends Form
{
    public function init(){
        $this->add(array(
                'name' => 'service_provider_fieldset',
                'type' => ServiceProviderFieldset::class,   // this is one model/entity
        ));

        $this->add(array(
                'name' => 'location_fieldset',
                'type' => LocationFieldset::class,   // this is a separate model/entity
        ));
}
}

Creating the fieldsets: (note commented out attempts at hydration)

class ServiceProviderFieldset extends Fieldset
{
    public function init(){
        //parent::__construct($name);
        /*
        $this
        ->setHydrator(new ClassMethodsHydrator(false))
        ->setObject(new ServiceProvider())
        ;
        */

        /*
        $this
        ->setHydrator(new ReflectionHydrator(false))
        ->setObject(new ServiceProvider())
        ;
        */

        $this->add(array(
                'type'=>'Hidden',
                'name'=>'id',
                'options'=>array(
                        'label' => 'Service Provider Id'
                ),
                'attributes'=>array(
                        'id'=>'providerId'
                )
        ));
}
}

Controller:

    $provider = $this->findServiceProviderById($providerId); // this is set from DB call and correctly creates a Provider() object with populated values.
    $location = $this->findServiceProviderLocationById($locId);
    $form = $formManager->get(RegisterForm::class);
    $form->bind($provider);
    $form->bin($location);
// $form->get('service_provider_fieldset')->bindValues(...);

View:

$formElement = $form->get('service_provider_fieldset')->get('email');
etc...

The form renders in the view correctly BUT without the populated data.

NOTE: NOT using Doctrine but I retrieve the data from the DB OK. NOTE: IF I set this flag 'use_as_base_fieldset' => true, then 1 of the Objects (ServiceProvider) populates, visa-versa if I set the location fields to 'true' then that populates. I've been searching for a couple of hours, trial and error with no success and I'm hoping it's just my fatigue which has missed a simple setup/config step to get this to work.

Summary: How do you populate 2 or more Fieldsets with 2 or more entities within a form? Bind(), fieldset->bindValues()?,

Tried:

$form->get('service_provider_fieldset')->allowObjectBinding(true);
        $form->get('service_provider_fieldset')->allowedObjectBindingClass(\Provider\Form\ServiceProviderFieldset::class);

These are some links that are close but still cannot populate both field sets via separate entities. ZF2 Form Hydration with multiple objects and fieldsets https://framework.zend.com/manual/2.4/en/modules/zend.form.collections.html hydrating multiple objects from fieldsets ZF2

The collections (product/brand/category) example implies a 'single' collection using the 'use_as_base_fieldset' => true, is used to bind()...?

2

There are 2 best solutions below

0
On

On your webpage, check the form's elements names that relate to your fieldsets. They should be something like this: yourFieldsetName[yourElementName]. If you just see yourElementName, that most likely means that forgot to prepare() your form in the view script.

This is exactly what happened to me, and after I prepare()ed the form, all the objects got hydrated without a problem.

UPDATE = answer to comment's questions: Not resolved as such. Is this bad design? Note: I am using prepare() on in the view.

If everything works fine, your 2 objects should hydrate. use_as_base_fieldset flag is used for basically saying, 'hey that's me (the fieldset) you should only hydrate object with data/extract data from object'. So what you get with one object being hydrated and the other not, and vice versa is predictable. It's quite difficult to say what's going wrong without looking at your complete code. I'm afraid posting too much will also take time for the answerer's to grasp, and my experience is that such questions are usually left unanswered. What I usually do in situations like yours is that I go step by step in the Zend Form's and Fieldsets methods used in hydration/extraction. I use \Zend\Debug\Debug::dump($somethingThatYouWantToCheck); die();. That's not the best method I presume, but it works.

If I were you, I would also do the following.

  1. From your post, it's not clear why you use form's init() method. The init() method is used when you want, for example, some elements in your form be filled from DB (like <select>). The Form runs init() method when some things that aren't available in in the __construct() method yet, but only after the instance of the form is created (not 100% sure about this, double-check this).

  2. Don't worry about good/bad design. Design is a very good thing, but if you have a small or middle system, the design considerations won't affect the performance/complexity of the system. But rather you'll spend really a lot of time doing everything right than just doing it and if it works ok, forgetting about it.

  3. If you don't wanna go with \Zend\Debug\Debug::dump($somethingThatYouWantToCheck); die(); (that could be quite tedious, I know), create one fieldset and attach to it your desired 2 fieldsets. Then include this fieldset in the form and use use_as_base_fieldset = true on this fieldset (of course you'll also need to create object corresponding to this fieldset with 2 nested objects that are attached to your current fieldsets, and attach the object to the fieldset).

Hope this helps at least a little.

0
On


To work with several objects you need:

1. Create a form with fieldset for each object as you have done.
You have to specify a name for each fieldset (e.g. in constructor).

2. In each fieldset we need to specify hydrator
e.g.: $this->setHydrator(new ClassMethods());

Zend\Hydrator\ClassMethods for using getter functions or
Zend\Hydrator\ArraySerializable for using getArrayCopy method.

and allow object class:

$this->setAllowedObjectBindingClass(YourClassObject::class);

You can do it in init method in fieldset.


3. Set hydrator for main form:

$this->setHydrator(new ArraySerializable());


4. Now in controller method you can create object of Zend\Stdlib\ArrayObject:

$obj = new ArrayObject();

then add your objects with a key equals fieldset name:

$obj->offsetSet("fieldset_name", $your_object);

and then you can bind $obj to your form:

$form->bind($obj);


I hope this helps. And don't forget about prepare method:

return new ViewModel(["form" => $form->prepare()]);