zend-frameworkzend-formzend-framework3zend-view

understanding grid layout in zend


I'm a bit confused with designing forms in zend. I understood that I have the fields in my form class and the look should be done in the views.

In the index view which is nearly plain html I don't have problems, but in the add and edit views which show my form I have problems to change the look.

I have a viewscript like follows:

 <?php
$title = 'AVB ändern';        
$this->headTitle($title);
?>
<h1><?= $this->escapeHtml($title) ?></h1>
<?php

$id= $form->get('id');
$id->setAttribute('class', 'form-control');
$id->setAttribute('placeholder', 'id');

$avbname= $form->get('avbname');
$avbname->setAttribute('class', 'form-control');
$avbname->setAttribute('placeholder', 'avbname');

$vbedingungen= $form->get('vbedingungen');
$vbedingungen->setAttribute('class', 'form-control');
$vbedingungen->setAttribute('placeholder', 'vbedingungen');

$versichererid= $form->get('versichererid');
$versichererid->setAttribute('class', 'form-control');
$versichererid->setAttribute('placeholder', 'versichererid');

$aktiv= $form->get('aktiv');
$aktiv->setAttribute('class', 'form-control');
$aktiv->setAttribute('placeholder', 'aktiv');

$submit = $form->get('submit');
$submit->setAttribute('class', 'btn btn-primary');


$form->prepare();

echo $this->form()->openTag($form);
?>
<div class="form-group">
    <?= $this->formElement($id) ?>
    <?= $this->formElementErrors()->render($id, ['class' => 'help-block']) ?>
</div>

<div class="form-group">
    <?= $this->formLabel($avbname) ?>
    <?= $this->formElement($avbname) ?>
    <?= $this->formElementErrors()->render($avbname, ['class' => 'help-block']) ?>
</div>

<div class="form-group">
    <?= $this->formLabel($vbedingungen) ?>
    <?= $this->formElement($vbedingungen) ?>
    <?= $this->formElementErrors()->render($vbedingungen, ['class' => 'help-block']) ?>
</div>

<div class="form-group">
    <?= $this->formLabel($versichererid) ?>
    <?= $this->formElement($versichererid) ?>
    <?= $this->formElementErrors()->render($versichererid, ['class' => 'help-block']) ?>
</div>

<div class="form-group">
    <?= $this->formLabel($aktiv) ?>
    <?= $this->formElement($aktiv) ?>
    <?= $this->formElementErrors()->render($aktiv, s['class' => 'help-block']) ?>
</div>

<?php
echo $this->formSubmit($submit);
echo $this->formHidden($form->get('id'));      
$form->setAttribute('action', $this->url('typavb', ['action' => 'edit']));    
echo $this->form()->closeTag();

Of course it shows one field beneath the other. How can I show two fields in a row (with the labels) ? I really would appreciate an example or a tip to a good tutorial, which shows how to do it properly with this zend3 concept.

Is it even the right place to do it in the view or do I need a new layout.phtml for this case?


Solution

  • To print parts of Elements separately, there's several functions pre-defined in ZF. You can find all of them in \Zend\Form\ConfigProvider->getViewHelperConfig(), see here on Github.

    In your case, your already using formLabel, formElement and formElementErrors.

    These are handy for separte use if you have something like Currency, where you'd like a user to both fill in an amount and choose a currency but only use a single label, e.g.:

    $this->formLabel($form->get('amount'));
    $this->formElement($form->get('amount'));
    $this->formElementErrors($form->get('amount'));
    $this->formElement($form->get('currency'));
    $this->formElementErrors($form->get('currency'));
    

    An entire "form row" is made up out of:

    So, as in this example you need the entire "amount" bit, you could shorten the above to:

    $this->formRow($form->get('amount'));             // prints all elements for the row
    $this->formElement($form->get('currency'));
    $this->formElementErrors($form->get('currency'));
    

    If you look closely through the linked ConfigProvider of 'zendframework/zend-form', you might've noticed there's also a form ViewHelper. This can be used to print an entire form in a single go, like so:

    file: add-foo.phtml

        <?= $this->form($form) ?>
    

    And that's it. It prints the whole form. Of course it uses the ZF defined ViewHelpers, as such also with that layout and classes applied.

    If you wish, can take that config and override it in your own projects.

    For example, your question code shows you add <div class="form-group"></div> around each row. Presumably for Bootstrap 4. To do this magically so you need not do:

        <div class="form-group">
            <?= $this->formRow($form->get('foo')) ?>
        </div>
    

    We can adjust the formRow ViewHelper. Simply follow these steps:

    1. Create a FormRow.php in your own project, e.g. module/Foo/src/View/Helper/FormRow.phtml
    2. Make sure to extend it from ZF's FormRow and copy in the original (ZF) render function, like so:
        use Zend\Form\View\Helper\FormRow as ZendFormRow;
    
        class FormRow extends ZendFormRow
        {
            public function render(ElementInterface $element, $labelPosition = null)
            {
                // its content
            }
        }
    
    1. We want to add a wrapper (form-group class div), so define it in the class, like so:
        class FormRow extends ZendFormRow
        {
            protected $inputRow = '<div class="form-group">%s</div>';
            // the other stuff
        }
    
    1. At the bottom of the render function, you'll find the following code (before the else):
        if ($this->renderErrors) {
            $markup .= $elementErrors;
        }
    

    Place after the above:

        $markup = sprintf(
            $this->inputRow,
            $markup,
        );
    
    1. Register your new ViewHelper, using the same aliases as ZF so as to overwrite the values:
        'view_helpers'    => [
            'aliases'    => [
                'formrow'             => FormRow::class,
                'form_row'            => FormRow::class,
                'formRow'             => FormRow::class,
                'FormRow'             => FormRow::class,
            ],
            'factories'  => [
                FormRow::class           => InvokableFactory::class,
            ],
        ],
    

    Done.

    Now when you do $this->form($form) the FormElement ViewHelper from ZendFramework will receive your custom formRow ViewHelper when it its Factory does ->get('formRow'), as the config is overwritten to your own. As such, all rows will now automagically have the surrounding div.


    Bit more than you asked for, but have fun ;) I'm gonna stop avoiding work now O:)