Custom Element and FormElement (ViewHelper) - How to get ZF3 to match and use custom ViewHelper?

zend-form

#1

So, been working on it for a fair bit, and I’m stuck.

What I wish to do is a to create a custom “type”, Markdown. So, I’ve created the Element class Markdown, and set the attributes to 'type' => 'markdown', like so:

use Zend\Form\Element;

final class Markdown extends Element
{
    protected $attributes = [
        'type' => 'markdown',
    ];
}

That works, it’s passed on for rendering. But then, I want to render it like a <textarea> get’s rendered. However, I wish to do a few custom things in the ViewHelper. As such, for the type “markdown” I want a custom ViewHelper.

use Markdown\Form\Element\Markdown;
use Zend\Form\ElementInterface;
use Zend\Form\Exception\DomainException;
use Zend\Form\View\Helper\FormInput;
use Zend\Form\View\Helper\FormTextarea;

final class FormMarkdown extends FormInput // or extends FormElement
{
    protected $validTypes = [ // or not this when FormElement
        'markdown' => true,
    ];

    public function __construct()
    {
        $this->addClass(Markdown::class, FormMarkdown::class);
    }
}

And lastly, make sure this is all registered:

'view_helper'     => [
    'aliases'   => [
        'mark_down' => FormMarkdown::class,
        'markdown'  => FormMarkdown::class,
        'Markdown'  => FormMarkdown::class,
        'MarkDown'  => FormMarkdown::class,
    ],
    'factories' => [
        FormMarkdown::class => InvokableFactory::class,
    ],
],

However, when debugging, I found that it goes all the way to the FormElement::renderType(..) function, where it decides that no renderHelper is known for at the key “markdown” ($this->typeMap[$type]) - \Zend\Form\View\Helper\FormElement::renderType()).

That array ($typeMap) in the FormElement class, is there a way of extending it from an external module, e.g. “markdown”, to have this new type used by the new ViewHelper?

Been scouring the docs, SO and now here for hours and got zip :frowning:


p.s. - just to clarify -> not trying to modify behaviour of existing ones, I want to realize a completely new “type”
p.p.s - if you read the above and thought: why? doing it “like this” is much simpler, then I’m all ears


#2

Ok, so after a few more hours, figured the following:

In addition to the above, add:

'form_elements'   => [
    'aliases'   => [
        'markdown' => Markdown::class,
    ],
    'factories' => [
        Markdown::class => InvokableFactory::class,
    ],
],

Then, by further careful examination of FormElement class, found the function addType. This function is actually never used (that PhpStorm could find), however, it does allow for this:

public function onBootstrap(MvcEvent $event)
{
    $serviceManager = $event->getApplication()->getServiceManager();

    /** @var FormElement $formElementViewHelper */
    $formElementViewHelper = $serviceManager->get('ViewHelperManager')->get(FormElement::class);

    $formElementViewHelper->addType('markdown', FormMarkdown::class);
}

And that registered my custom ViewHelper FormMarkdown (naming in line with ZF) to the configuration of FormElement.

That in turn allows FormElement->renderType() function to find a result in if (isset($this->typeMap[$type])), et voila: it works :slight_smile:


Leaving it here in case some future soul finds this helpful.


#3

…and the module class is the wrong place for that. :wink:
Create a factory for the FormElement class instead.


#4

Hmmm, true, but then usable just the once per project?

I think the issue with this bit of configuration is that it’s hard-coded in the class instead of retrieved from configuration.

(I’m planning to but: ) If I were to create the above into it’s own Github package for others to use, and would use the Factory method, then if someone were wanting to also add an element type, they would have to create a Factory that does both what this Factory would do (add FormMarkdown) and add their own FormElementType. Seems… off…

Anyhow, will give that a go tonight and see what I can do. :slight_smile: Thanks


#5

Add the option of configuration to the factory. :wink:

Where is the problem on (pre-)registering the default / included helpers? And this via a very simple way – also known as KISS.