Symfony form price field - custom assert with message - forms

By default the message: This value is not valid. is not sufficient. I am in the process of adding #Assert to each property in my entities for more specific validation.
I am displaying errors globally at the top of the form - not individually - in case it matters in the context of this discussion.
/**
* #ORM\Column(type="decimal", nullable=false, precision=10, scale=2)
* #Assert\Type(message="list price must be a numeric value", type="decimal")
* #Assert\NotBlank(message="list price cannot be empty")
* #Assert\GreaterThanOrEqual(message="list price must be no less than zero", value = 0)
*/
private $listPrice;
This is what I have thus far but the form still invalidates when I enter a value "D44.33"
How do I override the default "type" validator? Note the other validators like GreaterThanOrEqual works as expected - but for the life of me I cannot get the "type" to work???

The type decimal is not a valid type in Symfony, so it's probably looking for a custom decimal type and trying to transform the data and failing (which would coincide with the type of error messages you are getting).
The list of allowable types is here:
http://symfony.com/doc/current/reference/constraints/Type.html#reference-constraint-type-type
Try using this instead:
#Assert\Type(type="float", message="List price must be a numeric value")

#Jason Roman - thanks again for your help
The issue I was facing was solved by using "invalid_message" like so:
->add('listPrice', null, ['error_bubbling' => true, 'invalid_message' => '{{ value }} is not a valid list price'])

Related

Symfony 4: Where is the logic of the CollectionType's "allow_delete" option?

So, I'm trying to understand Symfony forms. I'm searching the core code for "allow_delete" option to see how it works under the hood, but the only place where it can be found is in the CollectionType class and I cannot find any logic there.
Documentation states:
If set to true, then if an existing item is not contained in the
submitted data, it will be correctly absent from the final array of
items.
Where in the code exactly it influences the submitted data?
You can find the function in MergeCollectionListener.php beginning on line 91:
// Remove deleted items before adding to free keys that are to be
// replaced
if ($this->allowDelete) {
foreach ($itemsToDelete as $key) {
unset($dataToMergeInto[$key]);
}
}
$dataToMergeInto is set as $dataToMergeInto = $event->getForm()->getNormData();
which refers to a function in which is explained in FormInterface.php:
/**
* Returns the normalized data of the field.
*
* #return mixed When the field is not submitted, the default data is returned.
* When the field is submitted, the normalized submitted data is
* returned if the field is valid, null otherwise.
*/
public function getNormData();

TYPO3 8.7.13 - getArguments() return no response

\Templates\Snippets\Search.html
<f:form id="snippetSearchForm"
action="search"
controller="Snippets"
extensionName="snippet_highlight_syntax"
pluginName="feshs"
name="searchSnippets"
method="POST"
pageType="5513">
<f:form.textfield class="form-control" property="searchWords"/>
<f:form.submit id="searchBtn" value="Search"/>
</f:form>
SnippetsController.php
public function searchAction()
{
$arguments = $this->request->getArguments();
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($arguments);
}
ajax.js
$("#snippetSearchForm").submit(function (event) {
event.preventDefault();
var form = $(this);
var action = form.attr("action"),
method = form.attr("method"),
data = form.serialize();
$.ajax({
url: action,
type: method,
data: data,
cache: false
}).done(function (data) {
console.log(data);
}).fail(function () {
( "div.tx-feshs" ).replaceWith("errorMessage");
}).always(function () {
});
});
Request URL
index.php?id=148&type=5513&tx_snippet_highlight_syntax_feshs[action]=search&tx_snippet_highlight_syntax_feshs[controller]=Snippets&cHash=4662b6b5a3fa0dc4e590e8d5c90fa
I can't solve this problem with getArguments(). The response and console.log are (empty). Seems like I'm missing something but I can't pinpoint where :/
You have a few common errors in your code and most of them has already been mentioned here, but please allow me to sum up.
Extension key/name
First, a lot of people confuses extension name with extension key. The directory name of your extension is your extension key, in this case snippet_highlight_syntax. The extension key is used all over TYPO3 as the unique identifier of your extension. With Extbase a new convention did come along called extension name to satisfy PSR2 coding convention and is primarily used in Extbase context. The extension name is a upper camel case edition of your extension key.
ExtbaseFluidBook: CodingGuidelines - It´s a bid old but still valid
The name of the extension in UpperCamelCase. For example, if the extension-key is blog_example, then this part of the classname is BlogExample.
Extension key: snippet_highlight_syntax
Extension name: SnippetHighlightSyntax
Be aware of what the TYPO3/Extbase framework asks for, key or name - it will help you a lot.
Plugin name
You have also declared a plugin named feshs. According to the DocBlock documentation of both \TYPO3\CMS\Extbase\Utility\ExtensionUtility::(configure|register)Plugin() methods it should, as with the extension name, be in upper camel case format like Feshs. It´s not well documented and I do not think it has any negative impacted on your application jet but now you knows and has a change to future proof your application by correcting it.
/**
* ...
*
* #param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
* #param string $pluginName must be a unique id for your plugin in UpperCamelCase (the string length of the extension key added to the length of the plugin name should be less than 32!)
* #param array $controllerActions is an array of allowed combinations of controller and action stored in an array (controller name as key and a comma separated list of action names as value, the first controller and its first action is chosen as default)
* #param array $nonCacheableControllerActions is an optional array of controller name and action names which should not be cached (array as defined in $controllerActions)
* #param string $pluginType either \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_PLUGIN (default) or \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
* #throws \InvalidArgumentException
*/
public static function configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions = [], $pluginType = self::PLUGIN_TYPE_PLUGIN)
Plugin signature
Together with your extension name it will form a plugin signature called snippethighlightsyntax_feshs. This signature is the valued stored in the tt_content database table as list_type or ctype depending of the plugin configuration.
The plugin signature is further used in TypoScript and GET/POST arguments prefixed with tx_. In your case tx_snippethighlightsyntax_feshs.
Fluid & Extbase forms
In your form snippet you have declared a element <f:form:textfield /> with the property tag. The property tag is only used together with the object and objectName tags on the <f:form /> element and is used to bind values to this objects properties (autofill, validation result etc.).
See \TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper::initializeArguments.
Name of Object Property. If used in conjunction with <f:form object="...">, "name" and "value" properties will be ignored.
In your case you should properly just use name in stead of property.
Your updated form should look something like below:
<f:form id="snippetSearchForm"
action="search"
controller="Snippets"
extensionName="SnippetHighlightSyntax"
pluginName="Feshs"
method="POST"
pageType="5513">
<f:form.textfield class="form-control" name="searchWords"/>
<f:form.submit id="searchBtn" value="Search"/>
</f:form>
Controller arguments
You should declare your arguments as controller arguments.
/**
* #param string $searchWords
*/
public function searchAction(string $searchWords = null)
{
if (is_string($searchWords)) {
// TODO: Do something here...
}
}
Note how I have given the argument a default value. This should suppress the error Required argument "searchWords" is not set for... you are getting.
This was a long write up. Hopes it helps your or some others.
Happy coding
$this->request->getArguments() will only return field value arguments that are prefixed with the extensions / plugins identifier like tx_anything_pi1[anything]
Please checkout if the "name"-tag of the fields is correct. Maybe those tags are wrong because you are reffering to a "property" of an object in your textfield but there is no object bound to your f:form tag.
Since the response should at least return the HTML of an empty debug, maybe something is wrong with your action. Can you call it in the browser?
First of all use name attribute instead of property in <f:form.textfield> tag.
Then you need to register argument as follows
public function searchAction(string $searchWords)
Furthermore PHP docblock must contain the parameter as #param string $searchWords. After clearing all caches in the Install Tool you should get your arguments.
Assumed your extension-key (=foldername) is "snippet_highlight_syntax" the parameter for URLs is usually like this:
tx_snippethighlightsyntax_feshs
That means all underscores of the extension-key are removed.
Probably it's possible to make it different, but that's not standard.
Therefore $this->request->getArguments() never returns anything.
You've to adjust the parameters in the url like this:
index.php?id=148&type=5513&tx_snippethighlightsyntax_feshs[action]=search&tx_snippethighlightsyntax_feshs[controller]=Snippets&cHash=4662b6b5a3fa0dc4e590e8d5c90fa
In the TypoScript-Object-Browser you should find your plugin with that name:
plugin.tx_snippethighlightsyntax_feshs
After many tries, I've sent the extension to test on another computer and it's working.
I've cleared all the caches, disabled/enabled, and so on. Seems like my environment is at fault.
Thanks to all of you for the help !

Symfony 3 - Form collection field error displaying outside the field

Could anyone tell me why error related to form collection is displaying outside the particular field and how to move it to place like you see in image included below?
Code of this field:
/**
* #Assert\Valid
* #ORM\OneToMany(
* targetEntity="PageFile",
* mappedBy="page",
* cascade={"persist","remove"},
* orphanRemoval=true
* )
* #var PageFile[]
* #Assert\Count(max="1")
*/
private $pageFiles;
Config:
- property: 'pageFiles'
type: 'collection'
type_options:
entry_type: 'Notimeo\PageBundle\Form\Type\MyFileType'
by_reference: false
error_bubbling: false
I'm using EasyAdminBundle and here's my whole project: https://github.com/ktrzos/SymfonyBasic. Problem applies to "Notimeo\PageBundle".
I see other errors are places above the input fields, so unless this is somehow positioned using CSS (which is very unlikely) it looks like the error is related to the form itself and not the input field. That's the same type of error like invalid CSRF token for example.
Your issue is probably related to Form Collection error bubbling where poster asks basically the same question as you.
The recommendation is to set:
cascade_validation' => true
Or, if you are using Symfony 3:
error_bubbling => false

How do I create a custom currency type in Magento or Zend?

I'm looking to create a new reward points currency, so instead of my Magento store selling products with a dollar value $300.00, I want it to display 300 Reward Points.
I've already tried a bad practice solution by adding this to the currencies section in lib/Zend/Locale/Data/en.xml
<currency type="RWP">
<displayName>Reward Point</displayName>
<displayName count="one">Reward Point</displayName>
<displayName count="other">Reward Points</displayName>
<symbol>Reward Points</symbol>
</currency>
I was able to enable and use this in Magento by following this thread:
http://www.magentocommerce.com/boards/viewthread/56508/
but it still uses the default formatting pattern: ¤ #,##0.00 so it looks like Reward Points800.00
My locale is set to en_CA and as far as i can tell there's no way for me to change the formatting pattern without affecting the CDN and USD formatting as well.
I tried overriding Mage_Core_Model_Store so that if the current currency code is RWP it will format the price using an array of formatting options but this doesn't work when I'm in the product view. Not to mention that this also seems like a really dirty way to accomplish what I want.
/**
* Format price with currency filter (taking rate into consideration)
*
* #param double $price
* #param bool $includeContainer
* #return string
*/
public function formatPrice($price, $includeContainer = true)
{
if ($this->getCurrentCurrency()) {
/**
* Options array
*
* The following options are available
* 'position' => Position for the currency sign
* 'script' => Script for the output
* 'format' => Locale for numeric output
* 'display' => Currency detail to show
* 'precision' => Precision for the currency
* 'name' => Name for this currency
* 'currency' => 3 lettered international abbreviation
* 'symbol' => Currency symbol
*/
$options = array();
if ($this->getCurrentCurrencyCode() == 'RWP') {
$options = array(
'position' => 16,
'precision' => 0,
'format'=> '#,##0.00 '
);
}
return $this->getCurrentCurrency()->format($price, $options, $includeContainer);
}
return $price;
}
The currency system is one I'm only passingly familiar with, so take all this with a grain of salt. (also, assuming Magento 1.4.2)
One approach is the directory/currency model. This is class that all currency formatting functions and methods ultimately call. You'll see calls like this throughout the source code
Mage::getModel('directory/currency')
It doesn't look like there's a way to say "use this currency model/class for this currency", so you'll be stuck with a class rewrite here. The formatPrecision and formatTxt methods are the ones you're after.
Also, it looks like the directory/currency class wraps calls to Magento's locale object (calls to getNumber and currency)
public function formatTxt($price, $options=array())
{
if (!is_numeric($price)) {
$price = Mage::app()->getLocale()->getNumber($price);
}
/**
* Fix problem with 12 000 000, 1 200 000
*
* %f - the argument is treated as a float, and presented as a floating-point number (locale aware).
* %F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
*/
$price = sprintf("%F", $price);
return Mage::app()->getLocale()->currency($this->getCode())->toCurrency($price, $options);
}
The locale object is a core/locale. You could also rewrite this class. if those were the methods you were after.
Finally, because this is Stack Overflow, there's a number of reward points systems already implemented for Magento. Checking these out to see how they solved the problems you're running into might be worthwhile.

Does exist Zend Filter similar to Zend Validator Identical?

Does exist Zend Filter similar to Zend Validator Identical?
The case I should filter input=='test'
$el->addFilter('Identical','test');
The problem that such filter not exist.
Thanks,
Yosef
I'm not sure how this filter should work, since it is not clear from your question. Anyway, I made some custom filter that will check if a value of input filed is equal to some $token. If they are equal than the input value will be empty string.
The filter looks as follows:
// file: APPLICATION_PATH/filters/Identical.php
class My_Filter_Identical implements Zend_Filter_Interface {
/**
* Token with witch input is compared
*
* #var string
*/
protected $_token;
/**
* Set token
*
* #param string
* #return void
*/
public function __construct($token = '') {
$this->_token = $token;
}
/**
* Filtering method
*
* #param string $value value of input filed
* #return string
*/
public function filter($value) {
if ($value !== $this->_token) {
return $value;
}
return '';
}
}
To apply it to a given form element:
require_once (APPLICATION_PATH . '/filters/Identical.php');
$el1->addFilter(new My_Filter_Identical('test'));
Off course instead of require_once it could be added to your resource autoloader, but as an example I think it is not needed right now.
Edit:
Forgot to mention pregReplace filter.
The same what the custom filter above does could be done using pregReplace filter:
$el1->addFilter('pregReplace',array('/test/',''));
But, as I said, I'm not sure how you want your filter to work. If you provide more info maybe I could help more.
Your question isn't all that clear - do you want a filter which removes the word test? Or are you talking about filtering a form input? So taking your example you want to remove from the el input what the test input contains?
If you want to remove test from your input, you could use Zend_Filter_PregReplace
$filter = new Zend_Filter_PregReplace(array('match' => '/test/', 'replace' => ''));
$input = 'What is this test about!';
$filter->filter($input);
Should give you What is this about!
There isn't a filter which would filter identical form input if its been entered into another input I don't think. You could try to create your own input filter and perform your own logic on the input.
It is not that clear what you are trying to do. If you give more explanation that would be good.
I need to remove all input, soo its not good to use regex.
If you just want to clear the data in form elements you can use one of the following:
Clear an element value by setting the value of the element to nothing.
$el->setValue(null);
or reset all form elements
$form->reset();