Injecting form objects

Hey guys, I’m wondering what everyone’s approach is to injecting form objects into Controllers or any other class. And what are pro’s/con’s of those approaches. What do we need to consider about the different approaches.

Case 1 - Constructor

Simple case of injecting the form by a factory into the constructor.

private $loginForm;

public function __construct(LoginForm $loginForm)
{
    $this->loginForm = $loginForm;
}

public function loginAction()
{
    // do stuff with form
}

What if we happen to have multiple forms that need to be injected? Like an additional form as for example: RegistrationForm. We seperate it into different controllers or just inject everything into one and the same controller? What if we’ve got a big controller due to legacy?

Case 2 - Form Element Manager wrapper

Create a wrapper for the Form Element Manager like a plugin. This plugin simply forwards the call to the Form Element Manager instance. Quick example of the plugin:

class FormElementManagerPlugin extends AbstractPlugin
{
    private $formElementManager;
    
    public function __construct(ContainerInterface $formElementManager)
    {
        $this->formElementManager = $formElementManager;
    }
    
    public function __call($method, $args) {
        // Invoke original method on our proxied object
        return call_user_func_array([$this->formElementManager, $method], $args);
    }
}

Makes it easy to fetch or check for form elements and no need for injecting it into controller classes.

So I’m really looking forward to your approaches or like to hear you opinions. If I’m missing any cases please provide and will add them later on.

I’ve added tags for you; after you’ve had a few posts and posted a few comments, you should be able to add your own tags.

Both of the approaches you outline are ones I’d use. I tend to prefer direct injection, in part because if I get a bunch of dependencies, it means I likely need to refactor. That could mean splitting the controller into multiple controllers, which might in turn lead to creating an abstract controller or a trait that contains base functionality. This is a great way to refactor legacy controllers into more manageable pieces.

The second approach you provide is also quite valid: it’s part of the reason plugin managers exist in the first place, to provide a way to retrieve related plugins at-will. You wouldn’t even need to go as far as creating a plugin; just inject the FormElementManager into your controller, and pull the appropriate form. The only drawback to this is that you might end up using forms that are not specific to your controller, which is the only reason I’d continue to recommend the first approach. Regardless, you’d want to refactor until you can inject only the form you need.

Thanks for adding the tags and your response.

Well I pretty much do the same and use both case #1 and #2. But as many developers we are sometimes lazy and once you create the plugin, you pretty much use it everywhere.

So we’ve got three cases for now:

  1. Injecting the Form
  2. Injecting the FormElementManager
  3. Create a controller plugin

So basically in that order is the best way to handle form injections, I concluded from your response.

Injecting the Form straight into the controller so you know which dependencies your class has. If it grows and grows start refactoring your controllers/actions and move them to a seperate controller. As in I’ve got a controller which is handling accounts and might split this into multiple controllers. But the problem I’ve got with this is that in how far are you going with this, like one action per controller? Due to you don’t want to inject dependencies into the class you don’t use. A simple Account\PasswordController for example with two actions: request a password by e-mail and reset password. Both actions have their own form. So would you split this into two controllers or fetch both of them and inject it directly? How far are you willing to go with this?

In case of multiple forms within your controller you could always inject the FormElementManager and fetch them from there. So you’ve got to inject this dependency and go on from there to fetch your form objects. So pretty much each and every controllor using CRUD has the FormElementManager injected to handle those 4 actions of a resource.

But with the plugin you’ve no dependency injections of forms but all capabilities of the FormElementManager, so why not use this at all times? Just like any plugin you call instead of injecting the classes? Yeah this pretty much comes close to questioning why we use controller plugins if it’s all about dependencies of our classes.

Side note: same problem for example with other manager like the Doctrine\EntityManager for example, are you injecting this into every and each of your controllers? So for a CRUD controller you’re pretty much injecting the FormElementManager and the EntityManager at all times.

Yes. :slight_smile:

I even recommend this for CRUD. Adding something requires a slightly different form than updating something, as the latter will often include elements such as an element identifier, a timestamp of update, etc., that are either unavailable or unnecessary during creation. Using this approach keeps the controllers targeted, and helps reduce unrelated changes; for example, you shouldn’t be changing the logic around creation if the primary purpose of a changeset is addressing a bug in updating. When a controller has many concerns, it’s tempting to change more than you need to.

Certainly you can, as the controller plugin manager is always injected. However, it hides the actual dependencies: the forms themselves. I like to be able to look at the constructor to determine what a controller needs, and when you’re using controller plugins to pull other assets, I then have to hunt in the logic of the controller to find those.

Additionally, if you refactor to each control handling exactly one thing, you’ll likely only need one form anyways!

The EntityManager is quite a different beast entirely, as it’s a generic instance that may be used anywhere in your application that needs access to persisted entities. A form, however, is almost always specific to a given routed controller (or two, one for initial display, one for re-display on failed validation).

In most of the cases such data is what I add after the form isValid() and is something I never add to the form. As of those are either a hidden field or are of no concern to the user and might be useless to show. Sure we can argue about this to show a disabled field with the identifier or so. Back to my point; so most of the times the create and update are sharing the form. The identifier is something we could work without due to object binding. If we do share the form we could have one controller, but in other modules you might just keep it seperate. So would you stick with the seperation of concerns or share one if there is no real difference in dependencies?

Yes. :sweat_smile: catch myself doing this way too often, while there is no real need to do it in most of the times.

As for the EntityManager within CRUD controllers we sure can inject it, but I’m always like, here we go again! Yes I’m pretty lazy most of the times :grin:. So the EntityManager might not have been the best example, as we can use it all over the application. Was more looking for a way of how to handle classes you use alot in controllers. So yes we could create a plugin that pretty much acts as proxy. Aslong as we use it all over the place, will this still be bothering you if you had to work with an application doing this, like do you feel the need to refactor?

The IsGrantedPlugin for example is something that just forwards to the AuthenticationService::isGranted($permission, $context). So would you inject the AuthenticationService into your controller or would you rather use the plugin even though it hides the dependency to the service. And there are more of those plugins like the identity that pretty much uses the AuthenticationService aswell. If the controller is using both isGranted() and getIdentity(), wouldn’t it be better to inject the Service? As the plugins both need to be fetched from the PluginManager. This way there is no need to fetch any of the plugin instances.

As in your quote it seems like you dislike plugins that are pulling assets, as you can’t see the hidden dependencies the controller has.

Yeah, going a bit off-topic with this from the Form objects to Plugins instances… :innocent: But hope that people who read this thread are seeing other way of approaches or just learn a thing or two of it. It sure did open my eyes about the different approaches.

Certainly prefer approach 1, and I offset the pain of constructing controller factories to match by auto wiring controller dependencies through reflection. I’ve bundled an example of this in my auto wire package:

An option we haven’t talked about yet is the use of Intializers. Adding an initializer to the ControllerManager, which pretty much is the same as how the PluginManager gets injected.

class FormElementManagerInitializer implements InitializerInterface
{
    public function __invoke(ContainerInterface $container, $instance)
    {
        if (!$instance instanceof FormElementManagerAwareInterface) {
            return;
        }

        $instance->setFormElementManager($container->get('FormElementManager'));
    }
}

class IndexController extends AbstractActionController implements FormElementManagerAwareInterface

Too bad there is no way as in lazy loading. If you call the ->getInstance() and the instance is null that it will fetch it from the ServiceManager. The ControllerPluginManager is always loaded, but isn’t always used as for a simple view action. All you need to do is fetch the entity and return an ViewModel. We don’t need to check if the user is logged in or has permissions as the redirect strategy could handle this for us. So bummer :disappointed_relieved:

Thanks for sharing/showing another option/approach!

I’m surprised some of the strongly opinionated folks haven’t jumped in here, with this, so I’ll try.

I think using the managers to pull dependencies in is somewhat discouraged. Sorry, I’m not remembering the source of this - something I’ve internalized from an article or chat somewhere.

As mentioned several times above, it buries dependencies all over the place within the code.

In my opinion, using plugins is similarly problematic - they’re sort of runtime/reflection magic that hides dependencies. Testament to their weirdness is the trouble PHPStorm has in finding their definitions, and also PHPStan. Then there’s testing…

Simplest is to inject all your dependencies in the constructor. That centralizes the calls to the various ZF managers in the factory, and gives your reader one place to look for dependencies.

I’m frighteningly rusty after just a 3 week holiday, so if anything’s ‘off’ about the above, that’s why :slight_smile:

Yes it does. And to help myself remember which plugins are available and help the IDE is to use some type hinting om my own AbstractActionController with @method Controller\Plugin\Example exampe($arg1, $arg2).

I do work on quite large application with one might say, not enough developers to refactor it frequently and rapidly due to new feature requests or improvements. This way we’ve to be a little bit creative and to keep the framework a bit more rapid for development. That’s why we use either initializers or proxy plugins. Yeah I know that it does not take that much time to create a factory and register it propperly. Yeah I know, shame on me! :sweat:

So does this mean that using plugins is bad at all and we should inject those plugins aswell? Or aren’t we heading/leading to such a situation with the framework?

Hope you’ve enjoyed your holidays :grin: And I really do share your opinion about seeing the dependencies. Slowly heading there with the application, so we know exactly what each and every controller needs and doesn’t pull anything strange underwater which can have an impact on the performance.