1

I have problem with ZF2 annotation forms. Values which has "composed Object" doesn't set default. I have attached my code.

Why form doesn't set value in MetaContent Object rows? What I'm doing wrong? In controller when I wrote "echo $category->getMetaContent()->getMetaDescription();" it showed right value

<?php

namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation;

/**
 * BlogCategory
 *
 * @ORM\Table(name="blog_category", indexes={@ORM\Index(name="category_parent_idx", columns={"parent_id"})})
 * @ORM\Entity
 * 
 * @Annotation\Hydrator("Zend\Stdlib\Hydrator\ObjectProperty")
 * @Annotation\Name("Category")
 */
class BlogCategory
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * 
     * @Annotation\Exclude()  
     */
    private $id;
    
     * @Annotation\Filter({"name":"StripTags"})
     * @Annotation\Required({"required":"true"})
     * @Annotation\Attributes({"class":"gui-input"})
     */
    private $name;
    
    
    /**
     * @var \Application\Entity\MetaContent
     *
     * @ORM\ManyToOne(targetEntity="Application\Entity\MetaContent", cascade={"persist"})
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="meta_content_id", referencedColumnName="id")
     * })
     * 
     * 
     * @Annotation\ComposedObject("Application\Entity\MetaContent")
     * @Annotation\Options({"label":"Meta content:", "label_options":{"class":"control-label"}})
     */
    private $metaContent;

<?php

namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation;

/**
 * MetaContent
 *
 * @ORM\Table(name="meta_content")
 * @ORM\Entity
 * 
 * @Annotation\Name("MetaContent")
 */
class MetaContent
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * 
     * @Annotation\Attributes({"type":"hidden"})
     * @Annotation\AllowEmpty(true)  
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="meta_description", type="string", length=255, nullable=true)
     * 
     * @Annotation\Type("Zend\Form\Element\Text")
     * @Annotation\Required({"required":"true"})
     * @Annotation\Options({"label":"Meta description:"})
     * @Annotation\Validator({"name":"StringLength", "options":{"min":"5", "max":"255"}})
     * @Annotation\Filter({"name":"StripTags"})
     * @Annotation\Attributes({"class":"gui-input"})
     */
    private $metaDescription;

    

use Zend\Form\Annotation\AnnotationBuilder;
 use DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity;

class IndexController extends AbstractAdminController { 

public function indexAction() {
        
        $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
        
        $view =  new ViewModel();
        
        $data = $this->getRequest()->getPost();
        $category = $em->getRepository('Application\Entity\BlogCategory')->findOneByName('tests');
        $builder    = new AnnotationBuilder();
        $form       = $builder->createForm($category);
        
        $form->setHydrator(new DoctrineEntity($em, false));
        
        $form->add(array(
            'type' => 'submit',
            'name' => 'save',
            'attributes' => array(
                'value' => 'Submit',
            )
        ));
        
        $form->bind($category);
        $form->prepare();
        if ($this->getRequest()->isPost()){  
            $form->setData($data);
            if ($form->isValid()){               
                echo 'good!';
            }else{                
                echo 'bad';

            }
        }
        
        $view->setVariable('form', $form);
        return $view;

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />        
    </head>
    <body>

        <h1>Test</h1>
        
    {{form().openTag(form)|raw}}
    
    {{formCollection(form)}}
    
    {{form().closeTag()|raw}}

    </body>
</html>

enter image description here

Fge
  • 2,971
  • 4
  • 23
  • 37
Oleksii.B
  • 618
  • 8
  • 16

1 Answers1

0

By default, doctrine relations are lazy-loaded (proxied) - they're only loaded when items in the collection are actually accessed. This happens all behind the scenes, so when you do:

$category->getMetaContent(); 
// Collection is NOT loaded yet, it is a transparent PROXY

count($category->getMetaContent()); 
// In order to cound, we must load, 
// so only NOW the collection is loaded, replacing the PROXY

$category->getMetaContent()->getMetaDescription();
// Same here, only this command actually would load the MetaDescription

$category->getMetaContent()->getMetaDescription();
// Subsequent calls will return the MetaDescription directly, 
// as it already be been loaded

The last one is particulary important. Once the proxy has been replaced by the actual object, the object is always returned right away. See this question for more about proxies (recommended to read!)

Now things become are bit more difficult when using hydrators. During extraction, the reference returned by getMetaContent is the PROXY. But the DoctrineEntity hydrator doesn't resolve the proxy, but simply skips it as un-extractable. That is exactly what you're seeing here.

So why did it work in the first place when you had $category->getMetaContent()->getMetaDescription(); in your controller? Because by this, the controller already made sure the proxy's been replaced by the actual object! Now the hydrator receives the actual object instead of the proxy - which it knows how to extract.

To counter this, you need to make sure that the proxy is actually resolved. You could do this by two simple ways:

  • Tell doctrine to EAGER load the relation. This will always load the MetaDescription object right away. Beware that this has a performance impact.
  • Register a custom strategory with the hydrator that knows how to handle this field (i.e. extract it from a proxy). See this question/answer for the same issue/approach
Community
  • 1
  • 1
Fge
  • 2,971
  • 4
  • 23
  • 37