I have made controllers and models for post and comment.
I've made my post index calling my add comment view. It works fine but the add comment view open another page.
Then, I have included the add comment view into an element in order to call directy into my post page but when I click to the submit button of my comments add view, it does not save anything.
When I look at the link displayed by the submit button it points to my post index view.
How to make it works ?
Here is the code of my comment element
div class="comments form">
<?php echo $this->Form->create('Comment'); ?>
<fieldset>
<legend><?php echo __('Add Comment'); ?></legend>
<?php
$this->request->data['Comment']['user_id'] = $current_user['id'];
$this->request->data['Comment']['post_id'] = $post_id;
echo $this->Form->input('post_id', array('type' => 'hidden'));
echo $this->Form->input('user_id', array('type' => 'hidden'));
echo $this->Form->input('content');
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>
here is part of the code of index post view calling that element
<div class="element">
<?php echo $this->element('add_comment', array('post_id' => $post['Post']['id'])); ?>
</div>
I must add that this div class element is inside a clickable div. But the click works fine for other button I have in the view.
Change the url of the form to direct it to the add action in Comments controller like this
echo $this->Form->create('Comment', array(
'url' => array('controller' => 'comments', 'action' => 'add')
));
By default, if you don't pass the url` parameter, the form points to the current action been rendered (in this case, the post controller), so you need to specify where you want it to post. Check the docs to read about that option.
Related
I'm trying a bit around with grid views. I show different contents so the user can have something like a dashboard.
Please have a look at the screenshot:
The notizen area should be a search form.
Here part of my controller action:
$view = new ViewModel();
$wiedervorlageView = new ViewModel(['wvs' => $this->wvTable->fetchAll()]);
$wiedervorlageView->setTemplate('layout/template/wiedervorlage');
$notizenView = new ViewModel(['notizen' => $this->notizenTable->fetchAll()]);
$notizenView->setTemplate('layout/template/notizen');
$geburtstageview = new ViewModel();
$geburtstageview->setTemplate('layout/template/geburtstageview');
$sidebarBlockView = new ViewModel(['aps' => $this->ansprechpartnerTable->getGeburtstage()]);
$sidebarBlockView->setTemplate('layout/template/block');
$geburtstageview->addChild($sidebarBlockView, 'block');
$view->addChild($wiedervorlageView, 'wiedervorlage')
->addChild($notizenView, 'notizen')
->addChild($geburtstageview, 'geburtstage');
return $view;
And here the search viewscript:
<?php
$title = 'Suche';
$this->headTitle($title);
?>
<h1><?= $this->escapeHtml($title) ?></h1>
<?php
$suche= $form->get('suche');
$suche->setAttribute('class', 'form-control');
$suche->setAttribute('placeholder', 'suche');
$suchtyp= $form->get('suchtyp');
$suchtyp->setAttribute('class', 'form-control');
$suchtyp->setAttribute('placeholder', 'suchtyp');
$submit = $form->get('submit');
$submit->setAttribute('class', 'btn btn-primary');
$form->prepare();
echo $this->form()->openTag($form);
?>
<div class="form-group">
<?= $this->formLabel($suche) ?>
<?= $this->formElement($suche) ?>
<?= $this->formElementErrors()->render($suche, ['class' => 'help-block']) ?>
</div>
<div class="form-group">
<?= $this->formLabel($suchtyp) ?>
<?= $this->formElement($suchtyp) ?>
<?= $this->formElementErrors()->render($suchtyp, ['class' => 'help-block']) ?>
</div>
<?php
echo $this->formSubmit($submit);
echo $this->form()->closeTag();
My question is how to change the notizen area so that it works with the form?
I tried different possibilities but I came to the conclusion that I didn't understood the logic in this case.
Any help or explanation appreciated.
Solution:
ok I changed to partials, before I wanted to try with the described concept to see the difference to the partials. I worked before with partials,my challenge here was really only the form, now it is clear hopefully. But either way, the concept with the partials is much more convenient and readable.
So, I changed the viewscripts like suggested, here for completion the one with the form:
<div class="row">
<h2> Suche </h2>
<?php
$suche= $form->get('suche');
$suche->setAttribute('class', 'form-control');
$suche->setAttribute('placeholder', 'suche');
$suchtyp= $form->get('suchtyp');
$suchtyp->setAttribute('class', 'form-control');
$suchtyp->setAttribute('placeholder', 'suchtyp');
$submit = $form->get('submit');
$submit->setAttribute('class', 'btn btn-primary');
$form->prepare();
echo $this->form()->openTag($form);
?>
<div class="form-group">
<?= $this->formRow($suche) ?>
<?= $this->formElementErrors()->render($suche, ['class' => 'help-block']) ?>
<?= $this->formRow($suchtyp) ?>
<?= $this->formElementErrors()->render($suchtyp, ['class' => 'help-block']) ?>
<?php
echo $this->formSubmit($submit);
echo $this->form()->closeTag(); ?>
</div>
</div>
As I posted this I thought I would need to redirect to the controller Action with the search script. Now I copied it to the same controller action. So it works and it is ok for me.
Here part of the changed controller action for anybody who has the same understanding issue:
$form = new SearchForm(NULL);
$form->get('submit')->setValue('suche');
return new ViewModel([
'wvs' => $this->wvTable->fetchAll(),
'form' => $form,
'aps' => $this->ansprechpartnerTable->getGeburtstage()
]);
You should really learn to create partials. What you're doing now is mixing concerns.
What you're doing in your controller will give you a giant mess if/when you have to refactor.
With partials you would do something like this.
File: notizen-partial.phtml
<?php
/** #var array $notizen */
?>
<table>
<tr>
<th>datum</th>
<th>text</th>
</tr>
<tr>
<td><?= $notizen['datum'] ?></td>
<td><?= $notizen['text'] ?></td>
</tr>
</table>
So, the above expects a variable $notizen, of type array (hinted in the PHP at the top). You could add some validation that the array is actually present, contains the values, etc. but that's up to you.
Now, register this partial in the configuration of the module, with the following config:
'view_manager' => [
'template_map' => [
'partial/notizen-partial' => __DIR__ . '/../view/partials/notizen-partial.phtml',
],
],
Make sure you correct the path for your situation!!!
Repeat the above for each little "container" of your data handling (wvs, aps, etc.).
Next, have an Action return the data you need to the View:
// ... create/get data
return [
'wvs' => $this->wvTable->fetchAll(),
'notizen' => $this->notizenTable->fetchAll(),
'aps' => $this->ansprechpartnerTable->getGeburtstage(),
];
This goes to "action-view.phtml", or whatever. This file handles the "place data where it needs to be".
The layout of the view, based on your screenshot, would be something like this (I'm going under the assumption your using Bootstrap 4.*):
<div class="row">
<div class="col-12">
<!-- That top row - full width -->
<!-- ## notizen here ## -->
</div>
</div>
<div class="row">
<div class="col-8">
<!-- second row - two thirds of width -->
<!-- Wiedervorlagen here -->
</div>
<div class="col-4">
<!-- second row, second column - one thirds of width -->
<!-- Geburtstage here -->
</div>
</div>
Now, you need to use the additional partials to display the data in the right locations. For example, your 'notizen' data, is now $notizen variable in this view. However, we're not using it in this view, so lets pass it on to the partial we created earlier, like so:
<?= $this->partial('partial/notizen-partial, ['notizen' => $notizen]) ?>
A lot happening here:
Calling the partial ViewHelper (look in the folder of this file to see other ViewHelpers already available!)
Passing it the name of the partial, which we set in the configuration earlier
Passing key/value pairs. The keys will be variable names, the values will be their values (you can add as many as you like/need)
Place this call in your action view, like so:
<div class="row">
<div class="col-12">
<!-- That top row - full width -->
<?= $this->partial('partial/notizen-partial, ['notizen' => $notizen]) ?>
</div>
</div>
And you're done.
You now have:
Single partials to display collections of data
Using ViewHelpers, so you can recycle the same view in different locations throughout your application
Separated concerns of your code (kept displaying in phtml template files, kept data collection / handling in Controller, kept logic in Repositories/Models/Entities)
I've studiously ignored your pasted Form. The principle for that is the same, you could pass the $form in to a partial and have that partial contain the displaying of the form. Creating something re-usable.
If you'd like to know more about customizing Form ViewHelpers (e.g. FormRow, FormElement, FormNumber, etc.), please see answers I've given others, such as this one.
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?
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:
A label (optional)
Element
ElementErrors (if present after server-side validation)
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:
Create a FormRow.php in your own project, e.g. module/Foo/src/View/Helper/FormRow.phtml
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
}
}
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
}
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,
);
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:)
I wanted to display a comment block below the activity rather than on the left or right side bars.
It was a lot easier than I thought.
I've added code for center-post which will add a block at the bottom of the main content. It could easily be modified to have a centre-pre for displaying at the top of the main content.
In /theme/yourthemename/config.php
Add 'centre-post' to the regions() array for each page layout where it is required. Eg for modules only added it to 'incourse' layout
// Part of course, typical for modules - default page layout if $cm specifi
'incourse' => array(
'file' => 'general.php',
'regions' => array('side-pre', 'side-post', 'center-post'),
'defaultregion' => 'side-pre',
),
In /theme/yourthemename/lang/en/theme_yourthemename.php add
$string['region-center-post'] = 'Center Bottom';
In /theme/yourthemename/layout/general.php
near the top after $hassidepost ... add
$hascenterpost = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('center-post', $OUTPUT));
then look for MAIN_CONTENT_TOKEN and add
<div class="region-content"> <?php echo core_renderer::MAIN_CONTENT_TOKEN ?> </div>
// Add this
<?php if ($hascenterpost) { ?>
<div id="region-center-post" class="block-region">
<div class="region-content">
<?php echo $OUTPUT->blocks_for_region('center-post'); ?>
</div>
</div>
<?php } ?>
// End of add this
<?php echo $coursecontentfooter; ?>
Now go to a module, add a block to the module and you will have a choice to move the block to the centre bottom.
I have a submit button in my form like this :
<?php echo $this->Form->end(__('Submit')); ?>
and I want to embed variables that are part of the input form.
For example, I want to embed a variable that is input here $this->Form->input('user_id');.
Does anyone know how to do this? I currently have this onsubmit as part of the options array, but it throws errors when I try to embed variables.
<?php echo $this->Form->create('Shift', array(
'onsubmit' => 'return confirm("Creating shift for '$id' on ");'
)); ?>
Now making an edit to this question. So the Javascript solution worked fine. Issue now is that I want the alert box to display the user's name, which is what is displayed in the dropbox. However, the HTML tags contain the user's ids. Here is the HTML
<div class="input select required">
<label for="my_user_id">User</label>
<select name="data[Shift][user_id]" id="my_user_id" required="required">
<option value="1">john doe</option>
<option value="2">john johnson</option>
Is there a way to access the value between the <option> tags using Javascript?
You need to use double quotes:
<?php
echo $this->Form->create('Shift', array(
'onsubmit' => "return confirm(\"Creating shift for '$id' on \");"
));
?>
Another option is to use $this->Form->postLink() and set $confirmMessage parameter if it is a simple state shift form.
You can use JQuery
echo $this->Form->create('Shift', array('id' => 'myform'));
echo $this->Form->input('user_id', array('id' => 'my_user_id', 'type'=>'text'));
After the form:
<?php echo $this->Html->scriptBlock('
$(document).ready(function() {
$("#myform").submit(function(event) {
alert("Creating shift for " + $("#my_user_id").val());
});
});
');
?>
I want to use a view script to render my zend form as it seems to be the best way to
control the layout/design of the form while still using the Zend_Elements classes.
From the view script, I render the element with $this->element->getElement('elementName') .
I'm having problems with the names of the elements. This is actually a sub-form inside a sub-form inside a form.
When I used the FormElements decorators , the fully qualified name of the elements was form[subForm][subForm][element] , which was good.
Wehn I moved to the viewScript decorators, it changed to subForm[subForm][element].
I understood that I need to use the PrepareElements decorator to fix this, but this caused the name to change form[subForm][form][subForm][subForm][elements] (it doubled the first two names in the start).
Any ideas how I should handle this?
Thanks.
UPDATE: I tried to debug PrepareElements and I really don't understand what is doing.
It seems like it works ok in the first iteration, but then it adds again the form[subform] prefix when running on one of the middle subforms.
When I'm not using the PrepareElements decorator, I'm just missing the "form" prefix in the names (i.e., instead of form[subForm][element], I get only subForm[element]).
May be I can just fix this somehow?
I tried to change the belongsTo but that only replaced the "subForm" prefix .
It actually seems like what is missing is a belongsTo method on the subForm.
Again, this is all because of the ViewScript decorator. It works fine with the FormElements decorators.
UPDATE 2: Just to clarify, I wouldn't mind this name change, but it causes my fields to not populate when I call form->populate .
Edit: I think that I've narrowed the problem to this: when I get my values back in setDefaults, they are ordered like this:
array(
\"formElements1-name\" => value1... \"subFormName\" => array(
\"parentFormName\" => array(
\"subFormName\" => subForm-values-array
)
)
...
The main problem here is the "parentFormName" => "subFormNAme".. what does it repeat itself? I'm already in the main form. I'm guessing this is caused because I've set the setElementsBelongTo(formName[subFormName]) , but if I wouldn't do that, then I would get my subform values completely separate from the form,
i.e.
values array = array(
\"formName\" => array(
formValues
), \"subFormNAme\" => array(
subFormValues
)
, while I exepct it to be
array(
formName => array(
subFormNAme => values-array
)
)...
Is it even possible to make this work?
Are you just trying to output your form using <?php echo $this->form; ?> from your view script?
That works well for simple forms, but for my more complex forms I tend to render each element individually but don't need to use ViewScript decorator on each individual element to do this. Just try something like this from your view script:
<div class="form">
<fieldset>
<legend>Some Form Name</legend>
<form action="<?php echo $this->escape($this->form->getAction()) ?>"
method="<?php echo $this->escape($this->form->getMethod()) ?>"
enctype="multipart/form-data">
<?php echo $this->form->id; // render the id element here ?>
<div class="half">
<?php echo $this->form->name; // render the user name field here ?>
</div>
<div class="half">
<?php echo $this->form->description; // render the description element here ?>
</div>
<div class="clear"></div>
<div class="half">
<?php echo $this->form->address1; // render the address ?>
</div>
<div class="half">
<?php echo $this->form->address2; // render address2 ?>
</div>
<div class="clear"></div>
<div class="third">
<?php echo $this->form->zip; // render zip code ?>
</div>
<div class="third">
<?php echo $this->form->city; // render city ?>
</div>
<div class="third">
<?php echo $this->form->state; // render state ?>
</div>
<div class="clear"></div>
<div class="half">
<?php echo $this->form->country; // render country ?>
</div>
<div class="clear"></div>
<?php echo $this->form->submit; ?>
</form>
</fieldset>
</div>
That is how I do most of my forms because I want to have some elements take up half the width and others the full width.
Surprisingly, the reference guide doesn't tell you that you can do this. I seem to remember a page about it in the past but cannot find it now. When I got started with Zend Framework, I thought the only way I could get my form to output exactly how I wanted was to create complex decorators, but that is not the case.
Matthew Weier O'Phinney has a great blog post on rendering Zend_Form decorators individually which explains what I did above. I hope they add this to the first page of Zend Form because that was discouraging to me at first. The fact is, 90% of my forms render elements individually instead of just echo'ing the form itself.
Note: To stop ZF from enclosing my form elements in the dt and dd tags, I apply this decorator to all of my standard form elements. I have a base form class that I extend all of my forms from so I don't have to repeat this everywhere. This is the decorator for the element so I can use tags to enclose my elements.
public $elementDecorators = array(
'ViewHelper',
'Errors',
array('Description', array('tag' => 'p', 'class' => 'description')),
array('HtmlTag', array('tag' => 'div', 'class' => 'form-div')),
array('Label', array('class' => 'form-label', 'requiredSuffix' => '*'))
);
For my submit buttons I use
public $buttonDecorators = array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div', 'class' => 'form-button'))
);
The current solution is to use the PrepareElements decorator on the subforms with one change - remove the recursive call in the PrepareElements code. Also, no "setElementsBelongTo" is required.
This seem to generate the correct names and ids.
The solution would be to use the belongsTo() form element property.
Example :
new Zend_Form_Element_Text('<elementName>', array('belongsTo' => '<subformName>'))
In this way, the render() method will use a form element name like
name="<subformName>[<elementName>]"
I had the same problem and i solved it with a decorator
1 : Create a generic subform with elements
2 : Using a specific decorator with PrepareElements
3 : Change form to an array with setIsArray(true)
Example :
Form
$i = 4;
for($i = 0; $i < $nbReclam ; $i++)
{
$rowForm = new Zend_Form_SubForm($i);
$name= new Zend_Form_Element_Textarea('name');
$rowForm->addElement($name);
$this->addSubForm($rowForm, $i);
}
$this->setDecorators(array(
'PrepareElements',
array('ViewScript', array('viewScript' => 'myDecorator.phtml')),
));
$this->setIsArray(true);
Decorator
<table>
<thead>
<tr>
<th>N°</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->element->getSubForms() as $subForm) : ?>
<tr>
<td> <?php echo $i++?> </td>
<?php foreach ($subForm->getElements() as $row) : ?>
<td><?php echo $row ?></td>
<?php endforeach ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
Enjoy
Sorry for my english, i am french