After few hours of experimenting I made some changes for previous code which works for my problem:
jQuery(document).ready(function () {
Validation.add('validate-postcode', Translator.translate('Please enter a valid post code.'), function (v, elm) {
var elem1 = jQuery(elm),
relatedElem = jQuery('#' + elem1.attr('data-related-field')),
passed;
elem1.removeClass('validation-failed');
elem1.removeClass('validation-passed');
if (typeof relatedElem !== 'undefined') {
relatedElem.removeClass('validation-failed');
relatedElem.removeClass('validation-passed');
passed = !Validation.get('IsEmpty').test(elem1.val()) &&
!Validation.get('IsEmpty').test(relatedElem.val()) &&
/^\d{3}$/.test(elem1.val()) &&
/^\d{2}$/.test(relatedElem.val());
if (!passed) {
relatedElem.addClass('validation-failed');
elem1.addClass('validation-failed');
}
}
return passed;
});
});
Hope that help or inspire someone for better solution.
"Document ready" was required due Translator.translate() existence.
Translation of string "Please enter a valid post code." was added in jstranslator.xml file.
One of the solution is to add a backend model
to your attribute which is used to format / validate your attribute value before save and/or after load.
Add a backend class :
[
'type' => 'int',
'backend' => '\Foo\Bar\Model\Attribute\Backend\YourAttribute',
'frontend' => '',
'label' => 'XXXX',
'input' => 'text',
'frontend_class' => 'validate-greater-than-zero',
'source' => '',
'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
'visible' => true,
'required' => true,
'user_defined' => false,
'default' => 0,
'searchable' => false,
'filterable' => true,
'comparable' => false,
'visible_on_front' => false,
'used_in_product_listing' => true,
'unique' => false
]
Here is an example of your custom class \Foo\Bar\Model\Attribute\Backend\YourAttribute
<?php
namespace Foo\Bar\Model\Attribute\Backend;
/**
* Class YourAttribute
*/
class YourAttribute extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
{
/**
* @var int $minimumValueLength
*/
protected $minimumValueLength = 0;
/**
* @param \Magento\Framework\DataObject $object
*
* @return $this
*/
public function afterLoad($object)
{
// your after load logic
return parent::afterLoad($object);
}
/**
* @param \Magento\Framework\DataObject $object
*
* @return $this
*/
public function beforeSave($object)
{
$this->validateLength($object);
return parent::beforeSave($object);
}
/**
* Validate length
*
* @param \Magento\Framework\DataObject $object
*
* @return bool
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function validateLength($object)
{
/** @var string $attributeCode */
$attributeCode = $this->getAttribute()->getAttributeCode();
/** @var int $value */
$value = (int)$object->getData($attributeCode);
/** @var int $minimumValueLength */
$minimumValueLength = $this->getMinimumValueLength();
if ($this->getAttribute()->getIsRequired() && $value <= $minimumValueLength) {
throw new \Magento\Framework\Exception\LocalizedException(
__('The value of attribute "%1" must be greater than %2', $attributeCode, $minimumValueLength)
);
}
return true;
}
/**
* Get minimum attribute value length
*
* @return int
*/
public function getMinimumValueLength()
{
return $this->minimumValueLength;
}
}
If you want a simple example of that kind of class you can check
\Magento\Customer\Model\Customer\Attribute\Backend\Website
- all the classes which extend
\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
- the classes into
backend_model
column in eav_attribute
table
EDIT
If you want a class that do nearly the same thing as you want you can take a look at the
SKU
attribute validation
\Magento\Catalog\Model\Product\Attribute\Backend\Sku
I also added the method in the example class
EDIT
Another solution (maybe not the best one) is to create a plugin on the function
\Magento\Eav\Helper\Data::getFrontendClasses
and add your frontend class here that can be validated in front.
Best Answer
There is a way to return a custom error message in json