There are several things you may need to dig into more to get the full picture here but here's a general idea of how you can approach this in Craft 3.
Flash errors are useful to display a message in the Control Panel, but you'll need to prepare a model with errors and signal to the controller that the form submission has failed to return errors to your template. Also, it's not necessary to use the redirect
method in this situation as the Controller request can return your errors to the template simply by preparing them using setRouteParams
and returning null
.
A common pattern that Craft and several plugins use to do this is as follows. The method below would be the method in your controller that receives the form submission:
public function actionMyControllerAction()
{
$this->requirePostRequest();
// Build a model of the data you are submitting
$model = new MyModel();
$model->someOptionalAttribute = Craft::$app->getRequest()->getBodyParam('someOptionalAttribute');
// The `getRequiredBodyParam` method can help validate required fields
$model->someRequiredAttribute = Craft::$app->getRequest()->getRequiredBodyParam('someRequiredAttribute');
// After you prepare the model, hand things off to a service
// to validate and/or save things to the database
// The "saveThisModel" method can run any additional
// validation you need to run, add errors to the model,
// and return `false` if validation fails
if (!Plugin::getInstance()->myService->saveThisModel($model)) {
Craft::$app->getSession()->setError(Craft::t('my-plugin', 'Unable to save item.'));
// This is where you name the thing you are returning to your template with errors.
// In this example, the 'myModel' variable would be available to your submitted template
Craft::$app->getUrlManager()->setRouteParams([
'myModel' => $model
]);
return null;
}
Craft::$app->getSession()->setNotice(Craft::t('my-plugin', 'Item saved.'));
// Redirects to wherever the submitted 'redirect' input points to
return $this->redirectToPostedUrl();
}
If validation fails, in your template, the myModel
variable will be available and you can access all errors as an array, or an array of errors for an individual field. You'll need to loop through this in your template. For simplicity here, I'm just dumping them:
All Errors:
{{ dump(myModel.getErrors()) }}
Errors for a specific field:
{{ dump(myModel.getError('someOptionalAttribute')) }}
You may also have to add some conditional logic around checking if the myModel
variable exists or has errors. You can use Craft's form macros in your template to help with this. Search the Craft codebase templates folder for forms.
and you'll find several examples. Here's a simplified Text Field macro that would display an error for the Categories title
field.
{% import "_includes/forms" as forms %}
{{ forms.textField({
label: "Title"|t('app'),
siteId: category.siteId,
id: 'title',
name: 'title',
value: category.title,
errors: category.getErrors('title'),
required: true,
maxlength: 255
}) }}
In this case, the Craft Categories controller the actionSaveCategory
is returning the category
variable to the template using the same pattern that is described above:
if (!Craft::$app->getElements()->saveElement($category)) {
// ...
Craft::$app->getSession()->setError(Craft::t('app', 'Couldn’t save category.'));
// Send the category back to the template
Craft::$app->getUrlManager()->setRouteParams([
'category' => $category
]);
return null;
}