trzczy
trzczy

Reputation: 1481

form_elements key is ignored in module.config.php in ZF3

I cannot understand how factories actually work. I prepared short files to test it for form elements.

Application/config/module.config.php

<?php
namespace Application;

use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
use Application\Route\StaticRoute;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Application\Form\Factory\AbcFormFactory;
use Application\Form\AbcForm;

return [
    'form_elements' => [
        'factories' => [
            AbcForm::class => AbcFormFactory::class,
        ],
    ],
//(...)
];

Application/src/Form/Factory/AbcFormFactory.php

<?php

namespace Application\Form\Factory;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Form\AbcForm;

class AbcFormFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new AbcForm("It works");
    }
}

Application/src/Form/AbcForm.php

<?php

namespace Application\Form;

use Zend\Form\Element;
use Zend\Form\Form;
use Zend\Hydrator\ClassMethods as ClassMethodsHydrator;
use Zend\InputFilter\InputFilter;

class AbcForm extends Form
{

    public function __construct($letter='no factory')
    {
        die($letter);
    }
}

The code above displays no factory, so the factory is ignored. If it had not been ignored it would have displayed It works.

How to create proper factory for form_elements?

UPDATE I call the form in controller:

<?php

namespace Application\Controller;

use Application\Form\AbcForm;
use Zend\Mvc\Controller\AbstractActionController;

class AbcController extends AbstractActionController
{
    public function indexAction()
    {
        $form = new AbcForm();
    }
}

What triggers form factory __invoke method? I think, when the form class is implemented via new the form factory __invoke method should run. Or not?

Upvotes: 0

Views: 295

Answers (1)

Marcel
Marcel

Reputation: 5119

You instantiate your form directly by the new operator. In zend framework 3 there is a service container, which contains all your classes you have noted in your module config. In this case it 's the AbcForm class, which should be created over a factory. In this case your controller class should be also created with a factory. It 's a common practice to use dependency injection for telling your controller, which form to use.

Just have a look at the following example.

The controller factory

namespace Application\Controller\Service;

use Application\Form\AbcForm;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;

class AbcControllerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $form = $container->get('FormElementManager')->get(AbcForm::class);
        $controller = new AbcController($form);
        return $controller;
    }
}

The controller itself

namespace Application\Controller;

use Application\Form\AbcForm;
use Zend\Mvc\Controller\AbstractActionController;

class AbcController extends AbstractActionController
{
    protected $form;

    public function __construct(AbcForm $form)
    {
        $this->form = $form;
    }

    public function indexAction()
    {
        if ($this->form->isValid()) {
            // ... and so on
        }
    }
}

Your module config

return [
    'controllers' => [
        'factories' => [
            AbcController::class => AbcControllerFactory::class,
        ],
    ],
    'form_elements' => [
        'factories' => [
            AbcForm::class => AbcFormFactory::class,
        ],
    ],
    'router' => [
        'routes' => [
            'your-abcform-application' => [
                'type' => Literal::class,
                'options' => [
                    'route' => '/application/abcform/',
                    'defaults' => [
                        'controller' => AbcController::class,
                    ],
                ],
            ],
        ],
    ],
];

You have to hand over your form via dependency injection to the constructor of your controller. Therefore we have created the controller factory, which instantiates your form class and gives it to the controller. In the controller itself you can access your form with the protected form property.

Upvotes: 2

Related Questions