Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Zend\Form\Fieldset bindValues problem #83

Closed
denchik63 opened this issue Jun 14, 2016 · 6 comments · Fixed by #214
Closed

Zend\Form\Fieldset bindValues problem #83

denchik63 opened this issue Jun 14, 2016 · 6 comments · Fixed by #214

Comments

@denchik63
Copy link

denchik63 commented Jun 14, 2016

Hi.
Have next structure:

/**
 * Class Client 
 *
 *  @ORM\Entity
 *  @ORM\Table(name="client ")
 *  @package Admin\Entity
 **/
class Client extends AbstractEntity {

    ...
    ...

    /**
     *  @var MasterPhotoGallery[]|ArrayCollection $photoGalleries
     *  @ORM\OneToMany(targetEntity="Admin\Entity\MasterPhotoGallery", mappedBy="client")
     *  @ZFA\Required(false)
     *  @ZFA\ComposedObject({
     *      "target_object": "Admin\Entity\MasterPhotoGallery",
     *      "is_collection": true,
     *      "options": {
     *          "count": 8,
     *          "label": "Фотогаллереи"
     *      }
     * })
     *  @ZFA\Type("Zend\Form\Element\Collection")
     **/
    private $photoGalleries;

}

/**
 * Class MasterPhotoGallery
 *
 *  @ORM\Entity
 *  @ORM\Table(name="client_photo_gallery")
 *  @ZFA\Instance("Admin\Entity\MasterPhotoGallery")
 *  @ZFA\Type("Zend\Form\Fieldset")
 **/
class MasterPhotoGallery extends AbstractEntity {

    /**
     * @var string $title
     * @ORM\Column(type="string", nullable=false, length=50)
     * @ZFA\Type("Zend\Form\Element\Text")
     * @ZFA\Required(false)
     * @ZFA\Options({
     *      "label": "Заголовок"
     * })
     **/

    private $title;
    ...
    ...
    ...
}

On validating form, In bindValues method we have name for title in PhotoGallery class like that: "photoGalleries[0][title]". But data array has not that key. He has key title. Array values look like:
['photoGalleries' => [0 => 'title']]
Source code (after update to version 2.8.4) in Zend\Form\Fieldset, row 568-569:

        foreach ($this->iterator as $element) {
            $name = $element->getName(); // photoGalleries[0]['title'] 
            if (!array_key_exists($name, $values)) { // of course key not exists
                if (!($element instanceof Collection)) {
                    continue;
                }
             ...
             ...
             ...
             ...

Source code (version 2.8.3) in Zend\Form\Fieldset:

        foreach ($values as $name => $value) {
            if (!$this->has($name)) {
                continue;
           ...

Here we have correct name because it's taken from array. By the way in method extract we have same problem.

        // Recursively extract and populate values for nested fieldsets
        foreach ($this->fieldsets as $fieldset) {
            **$name = $fieldset->getName();**
@diniciacci
Copy link
Contributor

diniciacci commented Jul 17, 2018

I'm hunting a bug that triggers in the same situation as this one and I tracked the source to exactly the same lines of Zend\Form\FieldSet in bindValues(). Notice that this is imho related to #106 #121 #122 and possibly to #95.

The bug (or group of bugs) give all the same symptoms (object appears not bound) but they are sometimes related to extract() failing to get values or like in this case the actual bounding is not performed, but to final users they appear exactly the same.

In my case I have a entity with a nested collection bound to a form with a fieldset that I am trying to update. Exactly as the case above.

  1. bindValues() gets called with with $values correctly set with the right (new and fresh) data
  2. data is extract() ed from the entity and so $objectData now contains the current object data (old data).
  3. elements of the entity are iterated. We exclude the case of a validationGroup for which the fix fix #106 PR problem (bug #121) #122 might work.
  4. Let's focus on the case the element is an actual element (i.e. Zend\Form\Element\Text ). This case is just ignored cause the $name used in line
    if (! array_key_exists($name, $values)) {
    is the name used in the form meanwhile $values contains keys that are from actual values.
  5. The situation is that $name is "wrong" and $element is not a Collection and so the code hits the continue.

IMHO the problems is that bindValues() is both called with $values in different "formats", one where it has $values from the form (so "names" are form names, e.g. "photoGalleries[0][title]") and the other case where $values is a more boring format with "plain" names (e.g. "title").

The above is somehow approximate, but is someone gives me some pointers and some more explanation I'm willing to dig a little on this. Imho a proper fix should really cover the two different cases (just for that if(), plus two for the above if from #122 ).

Included a screenshot of the debugging on my code
bindvalues_xdebug_noted

@froschdesign
Copy link
Member

@diniciacci
Can you provide an unit test for your problem? This would help to find the affected code. (Please reduce the unit test to a minimum; no annotations etc.)

Thanks!

@diniciacci
Copy link
Contributor

@froschdesign I failed up to now to produce one that fails indeed. I will try again. My customer said that the code was "perfectly working with old version of zf2". Where "old" is likely around 2.3.9 (zendframerowk version before the split).

Will work on that in the following days.

@diniciacci
Copy link
Contributor

I am still working on the unit test. I am failing to reproduce the problem. However I found that InputFilterProviderFieldset->prepareElements()

$elementOrFieldset->setName($name . '[' . $elementOrFieldset->getName() . ']');

ends up calling setName() on the actual element (i.e. \Zend\Form\Element\Text) and so modifying its name to foo[0][bar] format (for nested elements, to avoid clashes).

My theory is that then when binding happens the name have been modified and the $values can't match any of the names apart if the data comes from a form that has the above format. Naturally when calling bind data may come from different sources.

I am progressing slowly due to unknown specifications of the code and some comments or suggestions would be welcome. Meanwhile I keep working on the unit test. I keep the good news for a later time when the test is ready.

Best

@diniciacci
Copy link
Contributor

@froschdesign The PR has a unit test for this bug. The bug is not present in the current version. The bug was due to an interaction of prepare() with bindValues().

Notice that tests for the same interaction are missing for:

  • populateValues()
  • if a validationGroup is present

Thanks

@diniciacci
Copy link
Contributor

As a final note notice that c7867fe is the commit that introduced the fix and a test for it.

Deciding if both tests should stay is beyond my knowledge of the code.

weierophinney added a commit that referenced this issue Dec 11, 2018
Test for #83 bindValues not working after prepare
weierophinney added a commit that referenced this issue Dec 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants