0
votes

today i started reading myself into the features of Zend\Form. I found a great tutorial from Michael Gallego in which he explains how to use some new cool features.

The example works fine so far if we're handling 1-1 Relationships. Doctrine covers them fine.

What i want to do is

  • Instead of having textarea for the related value, I'd like a select box
  • The select box should have valid options, depending on what's at the database
  • For editing purpose later, the currently selected value needs to be selected
  • Doctrine should not add new rows to the One-Table

As you can see at my github sources i made use of the example in the tutorial, but shortened it to "Product" and "Brand". Brands - in my example - is a DB-Table with predefined Brands (Nike, Adidas, Puma, whatever) and when you create a new Product from the form you get those Brands as a select menu.

Right now, the way i add the options isn't working. I know i can manually set the options with an array like

$form->get('product')->get('brand')->setAttribute('options', array('Nike'=>'1', 'Adidas'=>'2', etc);

But i strongly assume that there is a more automated way to do this. I simply do not understand all this Hydrator classes provided with Zend.

The Problem is, even if i manually define the array as described above, the mapping of Product and Brand is not working correctly. The dump of $product right now looks like this

object(Application\Entity\Product)[210]
  protected 'id' => null
  protected 'name' => string 'asdasd' (length=6)
  protected 'price' => string '123123' (length=6)
  protected 'brand' => 
    object(Application\Entity\Brand)[215]
      protected 'id' => null
      protected 'name' => string '1' (length=1)

Obviously the brand is mapped completely wrong (for what i want to achieve, zend probably sees this as right, since the VALUE of my select is 1).

Question How do i tell my Form to map the select-value to the mapped object ID? Though maybe the way i set up my product-model is wrong in that case.

Any help will be greatly appreciated :)

3
Any update on how you got this work with ZF2 hydrators? I'm implementing a hack which manually constructs an array from an Entity and sets it with setValueOptions. How do you link an associated entity with a select dropdown? E.g.: a user can have multiple clients (ManyToMany), so when I edit a user I need a <select> dropdown of client names. - hohner
@hohner Simply check out the Docs of DoctrineModule (docs folder on github), Bakura wrote an astonishing readme ;) - Sam

3 Answers

1
votes

this is code from my form object I hope it will help

class ProductForm extends Form
{
public function __construct($em)
{
    parent::__construct();

    $this->add(array(
        'name' => 'productGroupId',
        'attributes' => array(
            'type'  => 'select',
            'label' => 'Category',
            'options' => array(),
        ),
    ));

$this->setProductGropus($em->getRepository('Project\Entity\ProductGroup')->findAll());

public function setProductGropus($groups)
{
  $groupsForm = array('--Select--'=>'');
  foreach ($groups as $group) {
        $groupsForm[$group->name] = (string) $group->productGroupId;
    }
    $this->get('productGroupId')->setAttribute('options',$groupsForm);  
}
}
}
1
votes

Looking at your BrandFieldSet you have only specified name to the InputFilterProvider thus the id will never be passed along.

Secondly im going to recommend you to remove the Registry. Classes created by using the ServiceManager can/should implement ServiceManagareAwareInterface if they need access to anything else if not specified by using constructors.

So in your controller instead of using your registry you access the service manager

$this->getServiceLocator()
     ->get('FQCN_OR_ALIAS');

There are some great examples written by contributors of the framework and ill list a few of there github repos here.

https://github.com/ZF-Commons And https://github.com/EvanDotPro (Can't post any more since i lack reputation)

Come join us on #zftalk.2 on irc.freenode.org if you have any further questions

0
votes

Although this is an old question, thought I'd answer anyway. The previous answers don't use the ObjectSelect of Doctrine.

You say to have a OneToOne relationship and do not want records added to the "One-table"; I'm assuming here you have a Uni-directional OneToOne relationship.

However, if you got "Product" and "Brand" as entities a OneToMany Bi-directional relationship might be more suitable ;)

Going however with OneToOne, your entities should look like this:

class Brand {
    /**
     * @var int
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @var string
     * @ORM\Column(name="name", type="string", nullable=false, length=128)
     */
    protected $name;

    //Getters/Setters
}

class Product {
    /**
     * @var int
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @var string
     * @ORM\Column(name="name", type="string", nullable=false, length=128)
     */
    protected $name;

    //Change below "OneToOne" to "ManyToOne" for proper product + brand relationship. Just this change will leave it as uni-directional.
    /**
     * @var Brand
     * @ORM\OneToOne(targetEntity="Brand", fetch="EAGER")
     * @ORM\JoinColumn(name="brand", referencedColumnName="id")
     */
    protected $brand;

    //Getters/Setters
}

Assuming your entities are correct, you should then use the ObjectSelect build into Doctrine.

class ProductForm
{
    /** @var  ObjectManager */
    protected $objectManager;

    public function __construct($name = 'product-form', $options = [])
    {
        parent::__construct($name, $options);
    }

    public function init()
    {
        $this->add([
            'type' => 'DoctrineModule\\Form\\Element\\ObjectSelect',
            'name' => 'brand',
            'required' => true,
            'attributes' => [
                'id' => 'selectBrand',
                'multiple' => false,
                'value' => null,
            ],
            'options' => [
                'label' => 'Select brand',
                'object_manager' => $this->getObjectManager(),
                'target_class' => Brand::class,
                'property' => 'id',
                'is_method' => true,
                'find_method' => [
                    'name' => 'findBy',
                    'params' => [
                        'criteria' => [],
                        'orderBy' => ['name' => 'ASC'],
                    ],
                ],
                'empty_option' => '--- Select Brand ---',
                'label_generator' => function (Brand $entity) {
                    return $entity->getName();
                }
            ],
        ]);
    }

    /**
     * @return ObjectManager
     */
    public function getObjectManager()
    {
        return $this->objectManager;
    }

    /**
     * @param ObjectManager $objectManager
     */
    public function setObjectManager(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
    }
}

Make sure to setup the Module.php to be able to load this form. Add the getServiceConfig() function to it.

public function getServiceConfig()
    {
        /** @var ServiceManager $sm */
        return [
            'factories' => [
                'product_form' => function ($sm)
                {
                    $form = new ProductForm();
                    $form->setInputFilter(new ProductInputFilter());

                    /** @var EntityManager $entityManager */
                    $entityManager = $sm->get('doctrine.entitymanager.orm_default');

                    //Set Doctrine ObjectManager
                    $form->setObjectManager($entityManager);
                    //Set Doctrine Object as Hydrator
                    $form->setHydrator(new DoctrineObject($entityManager, Product::class));
                    //Set Doctrine Entity
                    $form->setObject(new Product());
                    //Initialize elements onto form
                    $form->init();

                    return $form;
                },
            ],
        ];
    }
}

Next, load the form in a Controller.

$form = $this->getServiceLocator()->get('product_form');

===========================

Note: This works up until Zend Framework 2.5.2