17
[
    'type' => 'int',
    'backend' => '',
    '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
]

I am adding custom product attribute which works fine, but not able to add validate-greater-than-zero validation.

If we look at any attribute properties in Input Validation for Store Owner there are limited number of validations in select options.

validate-number,validate-digits,validate-email,validate-url,validate-alpha,validate-alphanum

These are the only validations applied in Product attribute section.

  • Please see my answer, it will be helping you to validate your attribute value. – Matthéo Geoffray Jan 17 '17 at 10:14
13
+50

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.

  • Thanks for your reply but would it be possible to apply frontend validation. – Amit Singh Jan 17 '17 at 12:04
  • If you take a look at your attribute line in eav_attribute table in the column frontend_class is thee the value validate-greater-than-zero ? – Matthéo Geoffray Jan 17 '17 at 12:28
  • Yes but it doesn't work. These are the only classes which works validate-number,validate-digits,validate-email,validate-url,validate-alpha,validate-alphanum. – Amit Singh Jan 17 '17 at 13:18
  • 1
    Can you try my second edit, to add your custom frontend classes ? – Matthéo Geoffray Jan 17 '17 at 17:06
  • I did it using plugin, thanks for the hint – Amit Singh Jan 18 '17 at 9:34
12

With the help of Matthéo Geoffray, this is what I did to apply frontend validation for custom attributes.

[
    'type' => 'int',
    'backend' => '',
    '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
]

This is the custom attribute in install script.

I added plugin in di.xml

<type name="Magento\Catalog\Ui\DataProvider\CatalogEavValidationRules">
      <plugin name="namespace_custom_validation_for_product_attribute" type="Namespace\Module\Model\Plugin\Product\ValidationRules"/>
</type>

Here is the plugin code.

<?php

namespace Namespace\Module\Model\Plugin\Product;

use Closure;

class ValidationRules
{

    /**
     * @param \Magento\Catalog\Ui\DataProvider\CatalogEavValidationRules $rulesObject
     * @param callable $proceed
     * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute,
     * @param array $data
     * @return array
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function aroundBuild(
        \Magento\Catalog\Ui\DataProvider\CatalogEavValidationRules $rulesObject,
        Closure $proceed,
        \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute,
        array $data
    ){
        $rules = $proceed($attribute,$data);
        if($attribute->getAttributeCode() == 'xyz'){ //custom filter
            $validationClasses = explode(' ', $attribute->getFrontendClass());
            foreach ($validationClasses as $class) {
                $rules[$class] = true;
            }
        }
        return $rules;
    }
}

Basically in \Magento\Catalog\Ui\DataProvider\CatalogEavValidationRules, the method called mapRules only matches the frontend class against limited number of validation rules. To apply more validation rules we need to append rules using plugin.

For server side validation, Please refer to Matthéo Geoffray answer.

3

I'm not sure it could be possible from install script. But I'm sure it's possible if you will create "before listener plugin" with function beforeSave() and check value there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.