Display Zend_Form form errors in ViewScript - zend-framework

I'm trying to display all form errors before the form using a ViewScript. Here is the code that I'm currently trying to use within my ViewScript:
<div class="errors">
<?php echo $this->formErrors($this->element->getMessages()); ?>
</div>
This call gives me an error message:
Warning: htmlspecialchars() expects parameter 1 to be string, array given
I've seen this same code suggested other places but its not working for me. If I print out $this->element->getMessages() I do see the error messages as the following:
Array ( [myField] => Array ( [isEmpty] => Value is required and can't be empty ) )
Any ideas?

The getMessages() returns an array of form element names as keys which each contain an array of errors for that element. So basically instead of handing the formErrors view helper:
Array ( [isEmpty] => Value is required and can't be empty )
You are handing it:
Array ( [myField] => Array ( [isEmpty] => Value is required and can't be empty ) )
You would want to do something like this instead:
$arrMessages = $this->myForm->getMessages();
foreach($arrMessages as $field => $arrErrors) {
echo sprintf(
'<ul><li>%s</li>%s</ul>',
$this->myForm->getElement($field)->getLabel(),
$this->formErrors($arrErrors)
);
}

As Mark points out in his answer, the getMessages() returns an array of form element names as keys which each contain an array of errors for that element; and his solution is:
$arrMessages = $this->myForm->getMessages();
foreach($arrMessages as $field => $arrErrors) {
echo sprintf(
'<ul><li>%s</li>%s</ul>',
$this->myForm->getElement($field)->getLabel(),
$this->formErrors($arrErrors)
);
}
This works, as long as getMessages() results in a two-dimensional array. However, if the form is based on relational data sets generated by Doctrine (or some other plugin), the error message associated with a field might also be an array and the above code will crash because it treats $arrErrors as a string when it turns out to be an array.
To capture the error messages if there's a second data set we could introduce a foreach statement nested inside the first foreach statement, but that won't work when getMessages() results in a two-dimensional array; nor does it work if the data sets are more than two-deep.
In a relational data scenario where we don't know how deep the error message comes from, a scalable solution is
$arrMessages = $this->myForm->getMessages();
print_r ($arrMessages);

Related

How to debug in TYPO3 if <f:debug> returns strings instead of object?

In a custom TYPO3 8.7.12 extbase extension I am unable to f:debug items in templates.
We are in the listAction controller and simply do:
$institutions = $this->institutionRepository->findAll();
$this->view->assignMultiple([
'institutions' => $institutions,
// ... pagination limit ...
]
);
And in the template:
<f:debug>
{institutions}
</f:debug>
This returns
sometimes the string 'Array' in the fluid debugger (can't reproduce now)
When the code is on 3 lines: #1273753083: Cannot cast object of type "TYPO3\CMS\Extbase\Persistence\Generic\QueryResult" to string.
Or also #1273753083: Cannot cast object of type "TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage" to string.
When the code is on 1 line: #1234386924: Cannot create empty instance of the class "TYPO3\CMS\Extbase\Persistence\ObjectStorage" because it does not implement the TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface.
If I loop through {institutions} with f:for and then f:debug:
<f:for each="{institutions}" as="institution" iteration="i">
<f:debug>
{institution}
</f:debug>
</f:for>
I get the first property of the object, e.g. the name.
EDIT: this is due to a __toString() magic method in the model. If I remove it, instead I get the namespace and uid STUBR\Extension\Domain\Model\Institution:55 – this looks again as if the object isn't rendered.
Wait... php.net says The __toString() method allows a class to decide how it will react when it is treated like a string. So could something be treating (typecasting?) the object as a string?
Working with the properties is normal, the issue just occurs when trying to print the whole object.
Where should I look? Lazy loading? There are some lazy loading properties, but not that many. Or maybe something is missing from the class? Or is there a workaround.
PS:
Unable to print_r or var_dump the query result, I get a memory limit error.
I saw https://wiki.typo3.org/Exception/CMS/1234386924 but initStorageObjects() is already called in the constructor
To answer the question;
<f:debug>{institutions}</f:debug>
will be parsed as an object, but any whitespace inside will make it parse as a string so.
The following methods do the same job as <f:debug> and work similarly in my case:
\TYPO3\CMS\Core\Utility\DebugUtility::debug(
$var = $variable,
$header = 'Institutions',
$group = ''
);
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump(
$variable,
$title = 'Institutions',
$maxDepth = 8,
$plainText = FALSE,
$ansiColors = TRUE,
$return = FALSE,
$blacklistedClassNames = NULL,
$blacklistedPropertyNames = NULL
);
execute in list or show action in controller.
It's less convenient than with f:debug (because you have to do the work in two different places, e.g. when you're in a loop in the template, you have to go to the controller and build that loop again), but it's a helpful workaround.
EDIT: I found it's sufficient to do
<f:debug>{var}</f:debug>
on one line

Phalcon form validation messages

There are some misunderstandings about how the messaging works for the Forms in Phalcon. Say we have a form and trying to add some extended error message for one of fields named 'code' in controller:
$form = new SampleForm();
Implementation of SampleForm is done in a corresponding class through initialize and the code element is $code = new \Phalcon\Forms\Element\Text('code');
The next code adds the message :
$form->get('code')->appendMessage(new \Phalcon\Validation\Message("The Code desn\'t exist or not valid"));
but trying to receive this message like
$form->getMessagesFor('code')
gives me nothing (the dump):
Phalcon\Validation\Message\Group Object
(
[_position:protected] =>
[_messages:protected] =>
)
Another attempt via
$form->get('code')->getMessages()
gives (the dump):
Phalcon\Validation\Message\Group Object
(
[_position:protected] =>
[_messages:protected] => Array
(
[0] => Phalcon\Validation\Message Object
(
[_type:protected] =>
[_message:protected] => The Code desn\'t exist or not valid
[_field:protected] =>
[_code:protected] => 0
)
)
)
My question: what am I doing wrong and why $form->[get/has]MessagesFor($name) doesn't work as expected?
I'm sorry to tell you, form handling is really poorly implemented and messed up in Phalcon, as I can see now, after a few months work.
In this case, you have multiple getMessage functions and they DO NOT return the same value. Even some of them returns a reference and some of them returns a copy of the error messages, and if you are appending messages to the copy, they won't be available elsewhere through the getMessage functions.
This even changed form version 1.2 to 1.3, we had a hard time figuring out which functions should we use. If you like, you could check out the C code behind this, in the Phalcon repository, it has been a great help for me to figure out why things don't work the way I expected.
My advices is, print the values of getMessage functions: Form::getMessages(), Form::getMessagesFor(), Form::get('element_name')->getMessages(). Then try appending new messages to them and print them again. See which one contains your messages.
I ended up getting messages for Phalcon's built in validation classes by $form->get('email')->getMessages() and getting my own added in the controller by $form->getMessagesFor('email'). I am still searching for a solution to get all the messages in one place.
Serin is right, messages are saved into different objects, so i came up with getting all of those like this
<?php foreach ($form as $element) {
//Get any generated messages for the current element
foreach ([$form->getMessagesFor($element->getName()), $element->getMessages()] as $messages) {
if (count($messages)) {
//Print each element
foreach ($messages as $message) {
echo $message;
}
}
}
}?>
Let's hope easier method will be added in nearest future:

Passing variables to a viewScript Decorator

I have tried adding a partial to my form using the Zend Form's viewScript decorator, however i seem unable to pass along variables to the partial. Here's my code:
In the controller i add the form:
$form = new Content_Form_ContentForm(array("categories" => $sortedCategories));
$form->submit_button->setLabel("Add content");
$this->view->form = $form;
Then inside the form i add the viewscript:
public function setCategories($categories) {
$this->setDecorators(array(array('ViewScript', array(
'viewScript' => 'partials/dtreePartial.phtml',
'List'=>"{$categories}",
))));
}
I have tried printing the options for the view script by using print_r($this->getDecorator('ViewScript')->getOptions()); wich results in Array ( [viewScript] => partials/dtreePartial.phtml [List] => Array )
However when i run it all, the script returns an error about the List not existing.
I have the feeling i am missing something but i am unsure as to what it is. Any advice or solutions will be appreciated! :)
The problem is with this line:
'List'=>"{$categories}",
Because you put the variable inside quotes, it gets cast to a string. In PHP, when you cast an array to a string, the result is always the word Array.
Simply change to:
'List'=> $categories,
and it should work as you expect.

How to get form element value inside Zend_Form::isValid() method?

I'm looking for best way of getting form element values inside isValid() method.
I had something like this isValid():
public function isValid($data) {
$start = (int)($data['start_hour'] . $data['start_minute']);
$end = (int)($data['end_hour'] . $data['end_minute']);
if ($start >= $end) {
$this->getElement('start_hour')->addError('Start time should be less than end time');
return false;
}
return parent::isValid($data);
}
but problem is that when my data structure changes I have to change validation too.
For example, now values of start_hour, start_minute, etc becomes elements of multidimensional array, and I need edit validation like
public function isValid($data) {
$start = (int)($data['send']['start_hour'] . $data['send']['start_minute']);
$end = (int)($data['send']['end_hour'] . $data['send']['end_minute']);
.......
}
It would be great to get value of element by permanent key (like element name), so my isValid could looks like:
public function isValid($data) {
$start = (int)($this->getElement('start_hour')->getValue() . $this->getElement('start_minute')->getValue());
$end = (int)($this->getElement('end_hour')->getValue() . $this->getElement('end_minute')->getValue());
.......
}
but $this->getElement('start_hour')->getValue() inside validation method return an empty value.
Is this possible to get element value in such way?
Thanks.
Try with $this->getValue('start_hour');
If this code takes place in the isValid() method of the form, then sure you could do it as you have described. It's just that isValid() usually needs some data passed - $form->isValid($_POST), for example - and you just end up ignoring it (assuming the parent is Zend_Form, which has an empty isValid() method; intermediate ancestors could potentially inspect the passed data). I would consider that to be potentially confusing.
Al alternative could be to create a custom validator, attach it to one of the form elements (say, the start_hour elements). The signature for the validator's isValid() can use the optional $context parameter - isValid($value, $context = null). When you call $form->isValid($data), it will pass that $data as the $context to the the element validator. You can then use that $context variable to inspect the other values (start_min, end_hour, end_min, etc).
Try calling
$form->populate($data)
Before calling isValid that way the data will be in your form.
Then $this->getValue('start_hour'); should work from within isValid().
So to be sure:
Somewhere in your code (probably controller) there is somthing like:
$this->view->form = new MyForm();
$this->populate($data); //add this
if($this->view->form->isValid($data)){
//do stuff
}

Automatic translation of multioptions in Zend_Form breaks sortorder

I have the following issue. I create a Zend_Select element and add multioptions in an array.
Zend automatically translates the options, after which my multioptions are sorted incorrectly.
Right now, my only option seems to be:
$element = $this->createElement("select", "name");
$element->setMultiOptions($myArray);
$options = $element->getMultiOptions(); // OPTIONS HAVE BEEN TRANSLATED HERE
asort($options);
$element->setMultiOptions($options);
Anyone know a better way to do this?
I usually always translate the options before sending them to the element :
$myArray = ...; // key/value array with values translated
asort($myArray);
$element->setMultiOptions($myArray);
But your solution looks just as good from my point of view.