Validation and Required Fields for EAV Attributes
Validation and Required Fields for EAV Attributes
~6 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026
Chapter 13 registered title, points_cost, and reward_type with 'required' => true - this chapter clarifies what that setting actually does, and where it hits its limits.
The built-in validation of AbstractEntity
Magento\Eav\Model\Entity\AbstractEntity ships with a validate($object) method that gets called automatically on every save(). It iterates over all attributes of the entity type, checks for every attribute with is_required = 1 whether the model carries a non-empty value for it, and collects an error for every missing required field. In the end, if there's at least one error, it throws a \Magento\Framework\Validator\Exception with all collected messages - without the module's classes having to write a single line of code for it.
$reward = $this->rewardFactory->create();
$reward->setDescription('Just a description, no title.');
try {
$this->rewardResource->save($reward);
} catch (\Magento\Framework\Validator\Exception $exception) {
foreach ($exception->getMessages() as $message) {
// "title" is a required field. (title, points_cost, and reward_type
// are all missing - three collected messages in one exception.)
}
}Tipp: This built-in validation is exactly why chapter 13 set required in the first place - without it, a reward could be saved without a title, without a points cost, and without a type, and the gap would only surface in the frontend when a customer sees an empty reward card.
The limits of built-in validation
is_required only checks "empty or not empty" per attribute, in isolation from every other attribute. A business rule like "discount_value is only required when reward_type equals RewardType::TYPE_DISCOUNT" is unknown to this built-in validation - that needs additional, custom logic.
Custom validation via beforeSave()
AbstractModel::save() calls beforeSave() before the actual ResourceModel::save() - the right place for rules that span multiple attributes at once. A LocalizedException thrown here stops the entire save operation before a single row gets written to the database.
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Model;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Model\AbstractModel;
use Mironsoft\Loyalty\Model\Reward\Source\RewardType;
use Mironsoft\Loyalty\Model\ResourceModel\Reward as RewardResource;
/**
* Reward EAV entity model, represents a single redeemable reward.
*/
class Reward extends AbstractModel
{
/**
* @var string
*/
public const ENTITY = 'mironsoft_loyalty_reward';
/**
* Binds the model to its resource model.
*
* @return void
*/
protected function _construct(): void
{
$this->_init(RewardResource::class);
}
/**
* Enforces the cross-attribute rule that a discount reward needs a discount value,
* a check the built-in is_required validation cannot express on its own.
*
* @return $this
* @throws LocalizedException
*/
public function beforeSave(): self
{
if ($this->getData('reward_type') === RewardType::TYPE_DISCOUNT
&& (string) $this->getData('discount_value') === ''
) {
throw new LocalizedException(
__('A discount value is required for rewards of type "discount".')
);
}
return parent::beforeSave();
}
}Achtung: beforeSave() runs before the built-in EAV required-field check in AbstractEntity::validate(), not after - a custom exception here reliably prevents the more expensive EAV JOINs on save from even being attempted when the input is already invalid at first glance.
Validation in the admin form
In the admin form (whose UI component XML the dedicated admin grids series from chapter 16 explains), is_required automatically translates into "validation": {"required-entry": true} on the respective form field - a client-side pre-check that complements the server-side validation from this chapter, but never replaces it. The client-side check can be bypassed (disabled JavaScript, a direct API call); the server-side one in AbstractEntity::validate() and beforeSave() cannot.
With attributes cleanly validated, chapter 18 closes out this block with the question chapter 10 deliberately left open: exactly where EAV's performance limits lie - and what to do once they're reached.