1

I have a form:

<form method="POST" enctype="multipart/form-data">
{{ csrfInput() }}
{{ actionInput('families/families/start') }}
<input type="text" name="name" value="">
<input type="submit" value="go">
</form>

I have a controller:

namespace modules\families\controllers;

use Craft;
use craft\web\Controller;
use craft\elements\Entry;

class FamiliesController extends Controller
{
    protected array|bool|int $allowAnonymous = true;
    public function actionStart() {
        $name = $this->request->getBodyParam('name');
        $host = Craft::$app->request->hostInfo;
        $title = Entry::find()->section('familySeason')->title($name)->all();
        if(count($title) == 0){
            $family = new Entry();
            $family->sectionId = 7;
            $family->enabled = false;
            $family->title = $name;
            $success = Craft::$app->elements->saveElement($family);
            if (!$success) {
                    // ...
            }
            Craft::$app->getResponse()->redirect($host."/good");
        }else{
            // ...
        }
    }
}

My controller has a check for the uniqueness of the name field, but I do not know how to output a user error if he wants to add an entry with a name that already exists. Can you tell me how to do this?

1 Answer 1

1

After calling saveElements, if saving failed due to validation errors, those errors will be available through the $family entry. The way to show those errors to the user is to provide this model back to the template so it can display any errors next to the appropriate fields.

$success = Craft::$app->elements->saveElement($family);
if (!$success) {
    Craft::$app->getUrlManager()->setRouteParams([
        'family' => $family,
    ]);
    return null;
}

The key is to return null, which will let Craft know that the controller doesn't wish to handle the request further, which means it will fall back to rendering the template again. Starting with Craft 4, you can also use $this->asModelFailure() to accomplish the same thing.

This is why you use actionInput() instead of setting the action parameter on the <form> element. The POST request goes to the same page the form is on, so the template with the form is rendered again, but this time, you have access to the submitted model. This model allows you to both repopulate the form with the values entered by the user, and show any pending errors.

<input type="text" name="name" value="{{ family.name ?? 'default' }}">
{% set errors = family.getErrors('name') ?? false %}
{% if errors %}
    {# Show errors to the user #}
{% endif %}

The key used in getErrors() is the name of the attribute on the model, so it can be any built-in attribute of entries (title, postDate, etc) or any custom field you have in your field layout.

You can also get a list of all errors to show to the user above the form:

{% set errors = family.getErrors() %}
{% if errors %}
    {# Show errors to the user #}
{% endif %}

Your Answer

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

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