Working with Forms
This section continues to improve on "Saying Hello". Instead of using URL, you will now ask user for a message via form.
Through this tutorial, you will learn how to:
- Create a form model to represent the data entered by a user through a form.
- Declare rules to validate the data entered.
- Build an HTML form in a view.
Installing form package
To install form package, issue the following command in your application directory:
composer require yiisoft/form
Creating a form
The data to be requested from the user will be represented by an EchoForm
class as shown below and saved in the file /src/Form/EchoForm.php
:
<?php
namespace App\Form;
use Yiisoft\Form\FormModel;
class EchoForm extends FormModel
{
private string $message = '';
public function getMessage(): string
{
return $this->message;
}
public function getPropertyLabels(): array
{
return [
'message' => 'Message',
];
}
}
The class extends from a base class provided by Yii, commonly used to represent form data.
The EchoForm
class has $message
property and related getter. These are regular data-related code. attributeLabels()
method provides labels that you're going to display in a view.
Using the form
Now, that you have a form, use it in your action from "Saying Hello". Here's what you end up with in /src/Controller/EchoController.php
:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Form\EchoForm;
use Yiisoft\Yii\View\ViewRenderer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\Hydrator\Hydrator;
use Yiisoft\Http\Method;
class EchoController
{
private ViewRenderer $viewRenderer;
public function __construct(ViewRenderer $viewRenderer)
{
$this->viewRenderer = $viewRenderer->withControllerName('echo');
}
public function say(ServerRequestInterface $request): ResponseInterface
{
$form = new EchoForm();
$hydrator = new Hydrator();
if ($request->getMethod() === Method::POST) {
$hydrator->hydrate($form, $request->getParsedBody()[$form->getFormName()]);
}
return $this->viewRenderer->render('say', [
'form' => $form,
]);
}
}
Instead of reading from request directly, you fill your form with the help of load()
method if the request method is POST and then pass it to your view.
Now, to allow POST, you need to adjust your route in config/routes.php
:
<?php
declare(strict_types=1);
use App\Controller\EchoController;
use App\Controller\SiteController;
use Yiisoft\Http\Method;
use Yiisoft\Router\Route;
return [
Route::get('/')->action([SiteController::class, 'index'])->name('home'),
Route::methods([Method::GET, Method::POST], '/say[/{message}]')->action([EchoController::class, 'say'])->name('echo/say'),
];
Adjusting view
To render a form, you need to change your view, resources/views/echo/say.php
:
<?php
use Yiisoft\Form\Field;
use Yiisoft\Html\Html;
/* @var \App\Form\EchoForm $form */
/* @var string $csrf */
/* @var \Yiisoft\Router\UrlGeneratorInterface $urlGenerator */
?>
<?php if (!empty($form->getMessage())): ?>
<div class="notification is-success">
The message is: <?= Html::encode($form->getMessage()) ?>
</div>
<?php endif ?>
<?= Html::form()
->post($urlGenerator->generate('echo/say'))
->csrf($csrf)
->open() ?>
<?= Field::text($form, 'message') ?>
<?= Html::submitButton('Say') ?>
<?= '</form>' ?>
If a form has a message set, you're displaying a box with the message. The rest if about rendering the form.
You get the action URL from the URL manager service. You access it as $urlGenerator
that's a default parameter available in all views. This variable and alike ones such as $csrf
are provided by view injections listed in config/common/params.php
:
'yiisoft/yii-view' => [
'injections' => [
Reference::to(CommonViewInjection::class),
Reference::to(CsrfViewInjection::class),
Reference::to(LayoutViewInjection::class),
],
],
You set the value of CSRF token, and it is rendered as a hidden input to ensure that the request originates from the form page and not from another website. It will be submitted along with POST form data. Omitting it would result in HTTP response code 422.
To turn on the CSRF protection, you need to add CsrfMiddleware
to config/web/params.php
:
<?php
declare(strict_types=1);
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
use Yiisoft\Router\Middleware\Router;
use Yiisoft\Session\SessionMiddleware;
use Yiisoft\Csrf\CsrfMiddleware;
return [
'middlewares' => [
ErrorCatcher::class,
SessionMiddleware::class,
CsrfMiddleware::class, // <-- here
Router::class,
],
// ...
You use Field::text()
to output "message" field, so it takes case about filling the value, escaping it, rendering field label and validation errors you're going to take care of next.
Adding validation
Right now it's possible to submit an empty value. Make it required. Modify /src/Controller/EchoController.php
:
<?php
namespace App\Controller;
use App\Form\EchoForm;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\Http\Method;
use Yiisoft\Validator\Validator;
use Yiisoft\Yii\View\ViewRenderer;
use Yiisoft\Hydrator\Hydrator;
class EchoController
{
private ViewRenderer $viewRenderer;
public function __construct(ViewRenderer $viewRenderer)
{
$this->viewRenderer = $viewRenderer->withControllerName('echo');
}
public function say(ServerRequestInterface $request, Validator $validator): ResponseInterface
{
$form = new EchoForm();
$hydrator = new Hydrator();
if ($request->getMethod() === Method::POST) {
$hydrator->hydrate($form, $request->getParsedBody()[$form->getFormName()]);
$validator->validate($form);
}
return $this->viewRenderer->render('say', [
'form' => $form,
]);
}
}
You've obtained validator instance through type-hinting and used it to validate the form. Now you need to add validation rules to /src/Form/EchoForm.php
:
<?php
namespace App\Form;
use Yiisoft\Form\FormModel;
use Yiisoft\Validator\Rule\Required;
use Yiisoft\Validator\RulesProviderInterface;
class EchoForm extends FormModel implements RulesProviderInterface
{
private string $message = '';
public function getMessage(): string
{
return $this->message;
}
public function getPropertyLabels(): array
{
return [
'message' => 'Message',
];
}
public function getRules() : iterable{
return [
'message' => [
new Required()
]
];
}
}
Now, in case you will submit an empty message you will get a validation error: "Value cannot be blank."
Trying it Out
To see how it works, use your browser to access the following URL:
http://localhost:8080/say
You will see a page displaying a form an input field that has a label that indicates what data are to be entered. Also, there is a submit button labeled "Say". If you click the submit button without entering anything, you will see an error message displayed next to a problematic input field.
After entering a message and clicking the submit button, you will see a new page displaying the data that you just entered.
Summary
In this section of the guide, you've learned how to create a form model class to represent the user data and validate said data.
You've also learned how to get data from users and how to display data back in the browser. This is a task that could take you a lot of time when developing an application, but Yii provides powerful widgets to make this task easy.
In the next section, you will learn how to work with databases, which are needed in nearly every application.