1

I am trying to validate entries on Save in a Module.

My specific use case requires that the Entry validation - on failure to validate will return user to form with field errors.

On successful validation I want to use that information to call an API with the field information and if all goes well I want to save - otherwise (e.g. API error) - I want to cause validation failure and show specific errors on the form.

From My understanding: the events occur in the following order:

EVENT_BEFORE_SAVE, EVENT_BEFORE_VALIDATE, EVENT_AFTER_VALIDATE, EVENT_AFTER_SAVE

I know I can do this:

    Event::on(
        Entry::class,
        Entry::EVENT_BEFORE_VALIDATE,
        function(ModelEvent $event) {
            $entry = $event->sender;
            //if send to api failure
            $entry->addError('field','api failed');
            $event->isValid=false;

        }
    );

It allows me to invalidate the save but - It does so before the regular fields are validated. My desire here is that I can protect some bad inputs to the API using crafts validation features and then send to API and then act upon its result.

From what I have seen - EVENT_AFTER_VALIDATE always runs even if the field validation failed.

So It made sense to me that I could check $event->isValid in EVENT_AFTER_VALIDATE so my API hit would only happen on successful validate

I have discovered that EVENT_AFTER_VALIDATE is receiving an Event instead of a ModelEvent Which means $event->isValid cannot be used to invalidate the save or check if the validation was successful. I've tried switching this around to no avail.

I seem to be able to use $entry->addError() to invalidate the save. I'm just missing how to find out how the regular validation went..

    Event::on(
        Entry::class,
        Entry::EVENT_AFTER_VALIDATE,
        function(Event $event) {
            $entry = $event->sender;
            //if ($event->isValid) {
                //if send to api failure
                $entry->addError('field','api failed');
            //}
        }
    );

1 Answer 1

2

You can check if an element is valid by calling Entry::validate() yourself. The problem is that if you do that inside the EVENT_BEFORE_VALIDATE hook, it might cause an infinite loop (not sure if Yii2/Craft are smart enough to detect this, may be worth testing). However, it should work fine if you execute the validation in an EVENT_BEFORE_SAVE hook:

Event::on(
    Entry::class,
    Element::EVENT_BEFORE_SAVE,
    function (ModelEvent $e) {
        $entry = $e->sender;
        // bail if the entry is not valid
        if (!$entry->validate()) return;

        // now we can safely execute the API call since the entry is valid
    },
);

If the validation fails, you don't have to do anything else because the Craft will prevent saving the entry anyway. If the validation succeeds, you know the regular fields are valid and can continue with your API call. If something goes wrong there, you can set $e->isValid = false and it will prevent the entry from saving, same as in the EVENT_BEFORE_VALIDATE hook.

Now there might be some edge cases, for example if you have another hook modifying the entry to make it valid AFTER your hook has run. But I doubt that will make a difference in practical terms.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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