Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically inject ObjectManager in form elements through FormElementManager #175

Closed
danizord opened this issue Feb 21, 2013 · 31 comments
Closed

Comments

@danizord
Copy link
Contributor

Inject the ObjectManager within my forms and fieldsets because DoctrineModule\Form\Element\ObjectSelect needs an ObjectManager is not very cool :/

And since ZF 2.1 was released, we have an FormElementManager.
I Think that DoctrineORMModule and DoctrineMongoODMModule can register factories for DoctrineModule\Form\Element\* that inject their own ObjectManager instance.

What do you think?

@Ocramius
Copy link
Member

@danizord see doctrine/DoctrineORMModule#142

@bakura10 wouldn't that be problematic for the ODM folks btw?

@bakura10
Copy link
Member

If you implement ObjectManagerProviderInterface or add the traits ProvidesObjectManager it should already do it. (well, when this thing will be merged: #169).

Why would it be problamtic for OMD ?

@danizord
Copy link
Contributor Author

Initializers are bad for performance, why do not factories?

@webdevilopers
Copy link

Since I'm running PHP 5.3. I couldn't implement the traits ProvidesObjectManager.

Alternatively when using ObjectManagerProviderInterface an exception is thrown: no object manager was set.
Where do I set it (globally) for my Zend\Form?

@webdevilopers
Copy link

@bakura10 : I implemented the ObjectManagerAwareInterface successfully. But calling the objectManager returns NULL. Do I have to config my DI elsewhere our should it work from scratch? I am using ZF 2.1:

class CostcenterFieldset extends Fieldset
implements ServiceLocatorAwareInterface, ObjectManagerAwareInterface
{
    protected $serviceLocator;
    protected $objectManager;

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    #public function setServiceLocator(ServiceLocator $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }

    public function getServiceLocator()
    {
        return $this->serviceLocator;
    }

    public function setObjectManager(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
    }

    public function getObjectManager()
    {
        return $this->objectManager;
    }

    public function init()
    {
        $om = $this->getObjectManager();var_dump($om); // returns NULL
    }

@danizord
Copy link
Contributor Author

danizord commented Mar 1, 2013

@webdevilopers currently we do not inject the ObjectManager automatically for you, this is a proposal that has not been merged yet. As a temporary workaround, you can put it in your Module.php:

use DoctrineModule\Persistence\ObjectManagerAwareInterface;
...
public function getFormElementConfig()
{
    return array(
        'initializers' => array(
            'ObjectManagerInitializer' => function ($element, $formElements) {
                if ($element instanceof ObjectManagerAwareInterface) {
                    $services      = $formElements->getServiceLocator();
                    $entityManager = $services->get('Doctrine\ORM\EntityManager');

                    $element->setObjectManager($entityManager);
                }
            },
        ),
    );
}

@webdevilopers
Copy link

@danizord : I tried but the initializers are not called. Do I have to change the key 'ObjectManagerInitializer' or anything else to make it work with Fieldsets?

@danizord
Copy link
Contributor Author

danizord commented Mar 1, 2013

@webdevilopers Sure you're using the FormElementManager to get this fieldset?

@webdevilopers
Copy link

@danizord : But how to? When using the FormElementManager to get my parent form I add my fieldset in the constructor. As far as I know the serviceLocator is only loaded in the init() method.
But my init() method in my parent form is never called. But in my fieldset this works.
What is the reason the init() method is not called and if Ican fix it should I the add the fieldset after having called it via serviceLocator (which will be an instance of FormElementManager then)?

@danizord
Copy link
Contributor Author

danizord commented Mar 4, 2013

@webdevilopers Make sure you are doing the following in your controller:

$forms = $this->getServiceLocator()->get('FormElementManager');
$myForm = $forms->get('Application\Form\MyForm');

then add your fieldset inside the init() of your Form.

This should work (:

@webdevilopers
Copy link

@danizord That is what I tried. But the init function in my Form is never called. :/

@danizord
Copy link
Contributor Author

danizord commented Mar 4, 2013

@webdevilopers Please paste your form here.

@webdevilopers
Copy link

Controller:

$formManager = $this->serviceLocator->get('FormElementManager');
$form    = $formManager->get('Application\Form\CreateCostcenter');

Form:

<?php
namespace Application\Form;

use Zend\Form\Form;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class CreateCostcenter extends Form
implements ServiceLocatorAwareInterface, ObjectManagerAwareInterface
{
    protected $serviceLocator;
    protected $objectManager;

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    #public function setServiceLocator(ServiceLocator $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }

    public function getServiceLocator()
    {
        return $this->serviceLocator;
    }

    public function setObjectManager(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
        #$this->init();
    }

    public function getObjectManager()
    {
        return $this->objectManager;
    }

    public function __construct()
    {
        echo "I'm called.";
    }

    public function init()
    {
        die("I'm never called!");
    }

Results in:

I'm called. 

exception 'Zend\Di\Exception\RuntimeException' with message 'Invalid instantiator of type "NULL" for "Zend\ServiceManager\ServiceLocatorInterface".'

An abstract factory could not create an instance of applicationformcreatecostcenter(alias: Application\Form\CreateCostcenter).

@danizord
Copy link
Contributor Author

danizord commented Mar 4, 2013

@webdevilopers Sounds like a problem with config of FormElementManager, Can you also paste your FormElementManager configuration?

@webdevilopers
Copy link

You mean the configuration you recommended, @danizord (see above)? Or is there another place for configuring?

use DoctrineModule\Persistence\ObjectManagerAwareInterface;
...
public function getFormElementConfig()
{
    return array(
        'initializers' => array(
            'ObjectManagerInitializer' => function ($element, $formElements) {
                if ($element instanceof ObjectManagerAwareInterface) {
                    $services      = $formElements->getServiceLocator();
                    $entityManager = $services->get('Doctrine\ORM\EntityManager');

                    $element->setObjectManager($entityManager);
                }
            },
        ),
    );
}

The getFormElementConfig method indeed gets called but none of the initializers.

@danizord
Copy link
Contributor Author

danizord commented Mar 5, 2013

@webdevilopers Strange, it should work, try adding the following key:

'invokables' => array(
    'CreateCostcenter' => 'Application\Form\CreateCostcenter',
),

And in your controller, get the form like this:

$forms->get('CreateCostcenter');

@Ocramius can you comment on this one?

@Ocramius
Copy link
Member

Ocramius commented Mar 5, 2013

@danizord can't comment now since I didn't follow

@danizord
Copy link
Contributor Author

danizord commented Mar 5, 2013

@Ocramius just read the above comments 😓

@webdevilopers
Copy link

I added the invokables in the getFormElementConfig method, right @danizord ?
Simply calling the form by:

use Application\Form\CreateCostcenter as CreateCostcenterForm;
$form = new CreateCostcenterForm();

will make the error disapear but not call the init() function on the Form.

Where does your "$forms" come from?

A question general regardless objectManager and others:
When correctely configuring a form the init() method is supposed to be called automatically after the it has been successfully constructed?

@danizord
Copy link
Contributor Author

danizord commented Mar 5, 2013

@webdevilopers is the FormElementManager who calls the init(), for it, you must get the form through it, not directly instantiating. Like this:

$forms = $this->getServiceLocator()->get('FormElementManager');
$myForm = $forms->get('CreateCostcenterForm');

Assuming you defined CreateCostcenterForm as invokable in your getFormElementConfig()

@webdevilopers
Copy link

This again leads me to my first error:
exception 'Zend\Di\Exception\RuntimeException' with message 'Invalid instantiator of type "NULL" for "Doctrine\Common\Persistence\ObjectManager".'

@pauloelr
Copy link

pauloelr commented Mar 7, 2013

@webdevilopers I was with the same problem, but i did as explain by @danizord and it works (Muito Obrigado @danizord). @webdevilopers did you update your zend framework version to 2.1.* or you're still using 2.0.? Because as i know this FormElementManager functionality was not present in 2.0.

@danizord
Copy link
Contributor Author

danizord commented Mar 8, 2013

You're welcome, @pauloelr (:

@webdevilopers
Copy link

Thank you guys, I finally got it working! I was using the wrong Alias for my Form inside the invokables.

My final result inside my Module.php now looks like:

    public function getFormElementConfig()
    {
        return array(
            'invokables' => array(
                'CostcenterFieldset' => 'Application\Form\CostcenterFieldset',
            ),
            'initializers' => array(
                'ObjectManagerInitializer' => function ($element, $formElements) {
                    if ($element instanceof ObjectManagerAwareInterface) {

                        $services      = $formElements->getServiceLocator();
                        $entityManager = $services->get('Doctrine\ORM\EntityManager');
                        $element->setObjectManager($entityManager);
                    }
                },
            ),
        );
    }

And nothing else. Furthermore I removed the ObjectManagerAwareInterface from my form and used it for my fieldset since this is the only place I need it.

A) Anyway, if I had a form with several Fieldsets that need the objectManager. Should each Fieldset implement the ObjectManagerAwareInterface? Or is there a good way to inherit the Fieldsets from inside the main form?

B) What is best practice to switch between the ORM entityManager and ODM objectManager?

I hope this doesn't go beyond the scope of this issue.

@danizord
Copy link
Contributor Author

@webdevilopers

A) You must implement ObjectManagerAwareInterface in each fieldset.
B) If you are using Doctrine\Common\Persistence\ObjectManager API. You can anytime switch between ORM and ODM.

And yes, you're going beyond the scope of this issue.

@herodrigues
Copy link

Sorry for delete the comment. I configured the initializer :D

I already had resolved. I'm not sure if was this, but I in my form class I wasn't calling parent::init()

@Ocramius
Copy link
Member

Closing. I also closed the PR for the initializer provided by DoctrineModule since the approach was basically broken. Implementing an initializer is quite trivial, I don't think we should over-complicate things by providing own magic

@danizord
Copy link
Contributor Author

@Ocramius I'm not talking about Initializers, I'm talking about factories in DoctrineORMModule and DoctrineMongoODMModule. Initializers are bad, but factories are good!

@Ocramius
Copy link
Member

Argh, my bad - lost topic because of the entire initializers chatting going on. No, I don't think that would work since we'd have custom names for orm and odm... That would start becoming really messy...

@danizord
Copy link
Contributor Author

@Ocramius We don't need custom names. I'll make a PR in DoctrineORMModule to make more clearer my approach.

@Ocramius
Copy link
Member

@danizord thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants