Laminas Form View Helper retrieval consistency

Hi all.

I am trying to understand if the different patterns between some bits of code are intentional, eg:

\Laminas\Form\View\Helper\AbstractHelper::getDoctypeHelper()

    protected function getDoctypeHelper(): Doctype
    {
        if ($this->doctypeHelper) {
            return $this->doctypeHelper;
        }

        if ($this->view !== null && method_exists($this->view, 'plugin')) {
            $this->doctypeHelper = $this->view->plugin('doctype');
        }

        if (! $this->doctypeHelper instanceof Doctype) {
            $this->doctypeHelper = new Doctype();
        }

        return $this->doctypeHelper;
    }

\Laminas\Form\View\Helper\FormSelect::getFormHiddenHelper():

    protected function getFormHiddenHelper(): FormHidden
    {
        if (! $this->formHiddenHelper) {
            if (method_exists($this->view, 'plugin')) {
                $this->formHiddenHelper = $this->view->plugin('formhidden');
            }

            if (! $this->formHiddenHelper instanceof FormHidden) {
                $this->formHiddenHelper = new FormHidden();
            }
        }

        return $this->formHiddenHelper;
    }

\Laminas\Form\View\Helper\AbstractFormDateSelect::getSelectElementHelper():

    protected function getSelectElementHelper(): FormSelect
    {
        if (null !== $this->selectHelper) {
            return $this->selectHelper;
        }

        if (method_exists($this->view, 'plugin')) {
            $selectHelper = $this->view->plugin('formselect');
            assert($selectHelper instanceof FormSelect);
            $this->selectHelper = $selectHelper;
        }
        assert(null !== $this->selectHelper);

        return $this->selectHelper;
    }

AbstractHelper::getDoctypeHelper()

  • will always return a Doctype().

FormSelect::getFormHiddenHelper()

  • will throw a TypeError if $this->view is null,
  • will return a retrieved class if $this->view->plugin() method exists
  • will create a FormHidden() if $this->view doesn’t provide plugin() method

AbstractFormDateSelect::getSelectElementHelper()

  • will throw a TypeError if $this->view is null
  • will return a retrieved class if $this->view->plugin() method exists
  • will NOT create a FormSelect() if $this->view doesn’t provide plugin() method

Is there a reason they don’t all apply the same pattern ie,

  • have $this->view !== null && $this->view…
  • create the required class as fallback if that condition isn’t true
    ?

Hello and welcome to our forums! :smiley:

No, there is no special reason. At most, there are historical reasons, different developments and avoiding bigger breaks in backward compatibility, but nothing specific.
The problem here is that the plugin method is not part of the interface and the renderer/view itself can be null, therefore the additional checks.

But any help to improve consistency is welcome. Create an issue report or pull request for this problem and we will be happy to review it.
Thanks in advance! :+1:t2:

Thanks,

I’ll aim to create a PR in the next few days.

While looking at it I noticed that all of the tests attach a renderer to the helper.

\LaminasTest\Form\View\Helper\AbstractCommonTestCase::setUp

    protected function setUp(): void
    {
        Doctype::unsetDoctypeRegistry();

        $this->renderer      = new PhpRenderer();
        $helperPluginManager = $this->renderer->getHelperPluginManager();
        $viewHelperConfig    = (new ConfigProvider())->getViewHelperConfig();
        $helperPluginManager->configure($viewHelperConfig);
        $this->renderer->setHelperPluginManager($helperPluginManager);

        $this->helper->setView($this->renderer);
    }

I’ll look at adding some tests that cover helpers without the render attached too.

(That is how I got here, I have come across some code where the helpers are used without a view attached, which worked fine in most cases, but broke when someone tried to use particular form elements the same way)

For reference