Object Framework seems to fail on dependency injection


(Maximilian Schmidt) #1

Hey there guys,

I currently have a problem with a Flow app I am developing.

I created a ViewHelper for rendering a configuration based menu structure.
This ViewHelper has a dependency to another class that is responsible for the rendering of the menu, as I wanted to keep the code reusable.

So we have Vendor\App\MenuViewHelper that depends on Vendor\App\MenuRenderer.
Now the Vendor\App\MenuRenderer itself has 3 further dependencies, e.g. the ConfigurationManager.

When the ViewHelper should be rendered I recieve the following error message:

Too few arguments to function Vendor\App\MenuRenderer::__construct(), 0 passed in …/Packages/Libraries/neos/utility-objecthandling/Classes/ObjectAccess.php on line 452 and exactly 3 expected

Thus I thought I have to stick to another injection-variant, so I removed that constructor and added @Flow\Inject annotations to each of the properties.
Instantiation now works fine but when it comes to rendering of that class it seems as none of the dependencies have been set and all properties are null.

I also tried to retrieve the dependencies from the ObjectManager directly (as it is available within a ViewHelper class) but I stumbled upon the same behaviour.
I don’t really know why this is happening, neither how to debug my situation.

Any help is strongly appreciated.
Thanks in advance, Max.

PS: I’m using the latest stable Flow release (which is 5.2.1 at this time of writing)


(Maximilian Schmidt) #2

Ok it seems that I managed to get it working somehow, even if I don’t know why it didn’t worked for that long.
At the end I retrieved the dependencies directly from the ObjectManager I fetched from Bootstrap::$staticObjectManager.
It doesn’t really seem to me to be the final solution, but for now it’s enough.

Still looking for help on this topic.


(Soren Malling) #3

Hi @mschmidt :slight_smile:

Great with more focus on menu rendering in Flow apps - I created https://github.com/meteko/menu based on a gist from @bwaidelich. I and other users will be happy if you look at that approach and maybe share how yours differ and lets see if we can build a more generic menu tool for everybody :slight_smile:

On your topic, can you share some code from your viewhelper so we can see how the whole initialization is done?


(Maximilian Schmidt) #4

Hi @sorenmalling :slight_smile:

That approach within the repository seems to be nearly the same that I used for my application.
The only big difference is that I registered a custom configuration type for the Menu specifications.
Have a look at my current menu configuration, you will see it’s more or less similar:

'main':
  type: complex
  items:
    'admin':
      type: group
      label: 'Admin'
      icon: 'wrench'
      privilege: 'Vendor.App:Access.Administration'
      items:
        -
          type: link
          label: 'User Management'
          icon: 'users'
          target:
            package: Vendor.App
            controller: Admin\User
            action: index
    'general':
      type: group
      label: 'General'
      icon: 'asterisk'
      expanded: true
      items:
        -
          type: link
          label: 'Homepage'
          icon: 'globe'
          target:
            package: Vendor.App
            controller: Backend
            action: index
        -
          type: link
          label: 'Google'
          icon: 'google'
          target: 'https://www.google.com/'
'user':
  type: dropdown
  class: 'dropdown-menu-right account-actions-menu'
  labelledby: 'userMenuButton'
  items:
    -
      type: link
      label: 'Settings'
      icon: 'cogs'
      target:
        package: Vendor.App
        controller: UserSettings
        action: index
    -
      type: link
      label: 'Logout'
      icon: 'sign-out-alt'
      target:
        package: Vendor.App
        controller: Auth
        action: logout

In addition, I added the type property at root-level of any menu to allow rendering of different menu-types. There you can decide if it shall result in a dropdown menu or alternatively a grouped main-menu (would be possible to add more but the both suffice for my context).
By now it’s not possible to add menu-types dynamically as they are hard-coded within the ViewHelper class, but it would be easily possible to change that behaviour I think.

If anything is still unclear, I will be happy to answer any questions you may have, even though I think that the example configuration shows which features are supported by this implementation.


To return to my original topic, this is the current beginning of the MenuViewHelper class:
(Please don’t blame me for creating such dirty code, it was kind of clean in the beginning before I tried to solve my problem)

/**
* @Flow\Scope("singleton")
*/
class MenuViewHelper extends AbstractViewHelper
{
    const MENU_CONFIG_TYPE = 'Menus';

    /**
    * @var UriBuilder
    */
    protected $uriBuilder;

    /**
    * @var ConfigurationManager
    */
    protected $configurationManager;

    /**
    * @var ComplexMenuRenderer
    */
    protected $complexMenuRenderer;

    /**
    * @var DropdownMenuRenderer
    */
    protected $dropdownMenuRenderer;

    public function __construct()
    {
        $this->objectManager = Bootstrap::$staticObjectManager;

        $this->injectUriBuilder($this->objectManager->get(UriBuilder::class));
        $this->configurationManager = $this->objectManager->get(ConfigurationManager::class);
        $this->dropdownMenuRenderer = $this->objectManager->get(DropdownMenuRenderer::class);
        $this->complexMenuRenderer = $this->objectManager->get(ComplexMenuRenderer::class);

        if ($this->renderingContext instanceof FlowAwareRenderingContextInterface) {
            $this->controllerContext = $renderingContext->getControllerContext();
            $this->complexMenuRenderer->setControllerContext($this->controllerContext);
        }
    }

    /**
    * @param string $menu
    * @return string
    */
    public function render(string $menu)
    {
        $menuConfig = $this->configurationManager->getConfiguration(self::MENU_CONFIG_TYPE);

        Assert::keyExists($menuConfig, $menu, 'No menu named %s could be found!');

        $menuConfig = $menuConfig[$menu];

        switch ($type = $menuConfig['type']) {
            case 'complex':
                return $this->complexMenuRenderer->render($menuConfig);
            case 'dropdown':
                return $this->dropdownMenuRenderer->render($menuConfig);
            default:
                throw new InvalidConfigurationException(
                    "Encountered an invalid menu type: $type!",
                    1545944440
                );
        }
    }

    /**
    * Inject the UriBuilder
    *
    * @param UriBuilder $uriBuilder
    * @return void
    */
    public function injectUriBuilder(UriBuilder $uriBuilder)
    {
        $this->uriBuilder = $uriBuilder;
        $this->uriBuilder->setRequest(
            new ActionRequest(Request::createFromEnvironment())
        );
    }

However, I don’t think that the source code is too meaningful, as I already tried all known variants for dependency injection.
If I specify the dependencies as constructor parameters I get an error that too few arguments were passed to the constructor.
Otherwise, if I want to get the dependencies via injectSomeProperty functions or if I specify the @Flow\Inject annotation, the properties are simply not filled and remain null, which then leads to an error when I want to execute a method on one of these properties.

I don’t know about any other option to inject dependencies, thus I switched to dependency fetching instead of injection for my ViewHelpers.
Actually my code is working like that, and even if it is not a very good solution, I can arrange myself to stay with this variant if no other one works.

Thank you very much for your participation in my problem.