Skip to main content
Version: Current

Validation

Data sent from the user side must be validated before any operation. Checking the received strings to ensure there is no malicious code or malicious programming code is one of the most important things that must be controlled in this section. Specify only the fields you need to receive so that the remaining fields (which may have been included in the submitted values by malicious individuals) are ignored. You can specify the type or the value you expect to receive from the user for each field. The validator will automatically prevent the operation from continuing—by throwing an exception of type packages\base\InputValidationException—if it receives any value or type other than what has been specified. For this purpose, you can use the checkinputs method of the packages\base\controller class.

The checkinputs method

The input argument of this method is an array whose keys are the form field names, and each key receives an array that separately specifies the type, default value, expected values, whether it is optional, or whether it is allowed to be empty for each field. The output of this method is an array with the same given keys and the validated values.

Data type

The type of the received data is specified using the type key.

$this->checkinputs(array(
'id' => array(
'type' => 'number',
),
));

Validation data types

The framework defines classes for validating various data types, which check the following data:

If the data to be validated is of a type other than the above, the programmer can create a new validator.

To define a new validator, you must define a class that implements the packages\base\Validator\IValidator interface.

In the IValidator interface, the getTypes() method is intended for specifying the validator's name, and the validate() method for the validation operation.

To view sample validator code, you can refer to the framework's defined validator classes in this folder.

Using a new validator

When using a new validator, the new validator is introduced using the addValidator() method. This method must be called before calling the checkinputs() method.

Also, instead of calling the addValidator() method, the namespace of the validator class can be given in the type index.

In the example below, a new validator (IBANValidator) is introduced. The value given to the type index is the name specified in the getTypes() method of the validator class.

The new validator that has been written

Validators/IBANValidator.php
<?php
namespace packages\packagename\Validators;

use packages\packagename\Bank;
use packages\base\{InputValidationException, Validator\IValidator, db\DuplicateRecord};

class IBANValidator implements IValidator {

/**
* Get alias types
*
* @return string[]
*/
public function getTypes(): array {
return ['iban'];
}

/**
* Validate data to be a IBAN code.
*
* @throws packages\base\InputValidationException
* @param string $input
* @param array $rule
* @param mixed $data
* @return array
*/
public function validate(string $input, array $rule, $data): array {
if (!is_array($data)) {
throw new InputValidationException($input);
}
if (!$data) {
if (!isset($rule['empty']) or !$rule['empty']) {
throw new InputValidationException($input);
}
if (isset($rule['default'])) {
return $rule['default'];
}
}
foreach ($data as $key => $value) {
if (!isset($value["id"])) {
throw new InputValidationException($input . "[{$key}][id]");
}
if (!isset($value["account"])) {
throw new InputValidationException($input . "[{$key}][account]");
}
}
foreach ($data as $key => $value) {
foreach ($data as $key2 => $value2) {
if ($key != $key2 and $value["id"] == $value2["id"]) {
throw new DuplicateRecord($input . "[{$key}][id]");
}
}
$bank = Bank::byId($value["id"]);
if (!$bank) {
throw new InputValidationException($input . "[{$key}][id]");
}
$data[$key]["bank"] = $bank;
}
return $data;
}
}

Example 1

controllers/Banks.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, Validator};
use packages\packagename\{Validators\IBANValidator};

class Banks extends Controller {
public function update(): Response {
Validator::addValidator(IBANValidator::class);
$inputs = $this->checkinputs(array(
"banks" => array(
"type" => 'iban',
),
));
foreach ($inputs["banks"] as $item) {
$item["bank"]->account = $item["account"];
$item["bank"]->save();
}
$this->response->setStatus(true);
return $this->response;
}
}

Example 2

controllers/Banks.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response};
use packages\packagename\{Validators\IBANValidator};

class Banks extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
"banks" => array(
"type" => IBANValidator::class,
),
));
foreach ($inputs["banks"] as $item) {
$item["bank"]->account = $item["account"];
$item["bank"]->save();
}
$this->response->setStatus(true);
return $this->response;
}
}

Empty values

The empty key specifies whether the field is permitted to be empty or not. The value of this key can be true or false. A value of true permits emptiness. The default value of this key is false.

Optional

The optional key allows the input field to be present or absent. The default value of this key is false. If this key is false and the input field is not defined, an inputValidationException is thrown.

Default value

The default key specifies a default value for the field. If the field is permitted to be empty, when it is empty the default value is considered as its value.

Constant values

If the received data has specific, constant values, you can specify the values using the values key. This key is more applicable for checkbox, select, ... inputs that have a fixed number of values.

String data

To validate string data, the string value is used for the type key. For data of type string, the following keys can be defined to manage its validation.

Regular expression

If a field we receive must follow a specific rule, its rule is specified using the regex key.

Example

controllers/Order.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response};

class Order extends Controller {
public function validateDomain(): Response {
$this->checkinputs(array(
'domain' => array(
'type' => 'string',
'regex' => '/^([a-z0-9\\-]+\\.)+[a-z]{2,12}$/i', // Thrown InpuvalidationException in response if given domain is not match to this pattern
),
));
$this->response->setStatus(true);
return $this->response;
}
}

Removing whitespace

The trim key works like the trim() method in php. This key can have the values true or false. If trim is not set, the framework automatically removes the whitespace of the data. To prevent the removal of whitespace, you must set the value of trim to false.

Code and tags

If the received data is code, you must set the value of the htmlTags key to true; otherwise the code is converted to its ascii form. For example, if the input data is <h1> salam </h1> and the value of htmlTags is true, the input data is converted to &lt;h1&gt;salam&lt;/h1&gt;. The default value of this key is false.

Multi-line strings

If the input data is multi-line, and we want to remove \n from the data, we give the multiLine key the value false. If this key is not set, the data can be multi-line.

Example

controllers/Blog.php
<?php
namespace packages\packagename\controllers;

use packages\blog\Comment;
use packages\base\{Controller, Response};

class Blog extends Controller {
public function comment(): Response {
$inputs = $this->checkinputs(array(
'name' => array(
'type' => 'string',
'multiLine' => false,
),
'message' => array(
'type' => 'string',
),
));
$model = new Comment();
$model->name = $inputs["name"];
$model->message = $inputs["message"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}

Numeric data

To validate numeric data, the values number, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float are used for the type key.

Each of the above specifies the number's range as well as whether it is positive or negative; their rules can be seen in the table below.

TypeAllowed numeric range
number
Accepts all positive and negative numbers
int
Accepts all positive and negative numbers
int8
-128 to 127
int16
-32768 to 32767
int32
-2147483648 to 2147483647
int64
Accepts all positive and negative numbers
uint8
0 to 255
uint16
0 to 65535
uint32
0 to 4294967295
uint64
Accepts all positive numbers
float
Decimal numbers

In addition to the rules that exist for each of the above values, the numeric range can be specified manually. These settings can be done using the two keys min and max.

The float and number types do not accept an input value of zero, and an InputValidationException is thrown. To prevent this exception and accept the number zero, you must define the zero key with a value of true.

note
  1. If the field is numeric and its empty is true, when the field's value is empty, null is considered as the field's value.
  2. If the entered number is outside the allowed numeric range, an exception of type InputValidationException is thrown with the message min-value or max-value.
  3. If the entered data is not a number, an exception of type InputValidationException is thrown with the message not-a-number.
  4. If the entered number is not equal to the defined value, an exception of type InputValidationException is thrown with the message not-defined-value.

Example 1

controllers/Cronjobs.php
<?php
namespace packages\packagename\controllers;

use packages\cronjob\Task;
use packages\base\{Controller, Response};

class Cronjobs extends Controller {
public function store(): Response {
$inputs = $this->checkinputs(array(
'hour' => array(
'type' => 'number',
'values' => range(0, 24),
),
'minuts' => array(
'type' => 'string',
'min' => 0,
'max' => 60,
),
'command' => array(
'type' => 'string',
'htmlTags' => true,
),
'port' => array(
'type' => 'uint16',
'optional' => true,
'default' => 22,
),
));
$model = new Task();
$model->hour = $inputs["hour"];
$model->minuts = $inputs["minuts"];
$model->command = $inputs["command"];
$model->port = $inputs["port"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}

Example 2

controllers/Chats.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, Options};

class Chats extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
'prev_messages_count' => array(
'type' => 'number',
'min' => -100,
'max' => 100,
'zero' => true,
),
));
Options::save("packages.packagename.chats.prev_messages_count", $inputs['prev_messages_count']);
$this->response->setStatus(true);
return $this->response;
}
}
note

In the example above, if the zero index is not defined, the number zero is not accepted.

Boolean data

To validate boolean data, the bool key is used for the type value. The input values can be 0, 1, true, or false.

note

If the value of empty is true, when the field is empty its value is considered to be false.

Example

controllers/Chats.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, Options};

class Chats extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
'status' => array(
'type' => 'bool',
),
));
Options::save("packages.packagename.chats.one-on-one-chats.status", $inputs['status']);
$this->response->setStatus(true);
return $this->response;
}
}

Date

To validate dates, the date value is used for the type key. This validator accepts date inputs in the form YYYY/MM/DD, or together with time in the form YYYY/MM/DD HH:II:SS.

note

The year must be entered as four digits. The day and month can be entered as one or two digits.

For dates, the unix key is defined. If this key is not defined or has a value of false, the validator's output is the entered date; and if it has a value of true, its output is the timestamp of the entered date.

Example

controllers/Shop.php
<?php
namespace packages\packagename\controllers;

use packages\shop\category\Special;
use packages\base\{Controller, Response, Date, Options, InputValidationException};

class Shop extends Controller {
public function updateSpecialSells(): Response {
$inputs = $this->checkinputs(array(
'start_at' => array(
'type' => 'date',
'unix' => true,
'optional' => true,
'default' => Date::time(),
),
'end_at' => array(
'type' => 'date',
'unix' => true,
),
));
if ($inputs["start_at"] < Date::time()) {
throw new InputValidationException("start_at");
}
if ($inputs["end_at"] <= $inputs["start_at"]) {
throw new InputValidationException("end_at");
}
$models = Special::where("status", Special::ACTIVE)->get();
foreach ($models as $model) {
$model->start_at = $inputs["start_at"];
$model->end_at = $inputs["end_at"];
$model->save();
}
$this->response->setStatus(true);
return $this->response;
}
}

Cellphone number

To validate a mobile number, the cellphone value is used for the type key.

note

Currently, Jalno only has validation for the cellphone numbers of Iranian operators by default. Also, Iran's code 98 is set by default. To change it, you can change the setting named packages.base.validators.default_cellphone_country_code in Jalno's configuration file at the path packages/base/libraries/config/config.php to the desired value.

The validator accepts inputs in the formats 9131101234, 09131101234, 989131101234, 9809131101234, +989131101234, and 98989131101234, and after checking, returns the entered number in the form 989131234567.

Example

controllers/Users.php
<?php
namespace packages\packagename\controllers;

use packages\packagename\User as Medel;
use packages\base\{Controller, Response, InputValidationException, Password, Session};

class Users extends Controller {
public function login(): Response {
$inputs = $this->checkinputs(array(
'username' => array(
'type' => 'cellphone',
),
'password' => array(),
));
$model = new Medel();
$model->where("username", $inputs["username"]);
$user = $user->getOne();
if (!$user) {
throw new InputValidationException("username");
}
if (!Password::verify($inputs["password"], $user->password)) {
throw new InputValidationException("password");
}
Session::set("loggin", true);
Session::set("userID", $user->id);
$this->response->setStatus(true);
return $this->response;
}
}

Email

To validate an email, the email value is used for the type key.

Example

controllers/Users.php
<?php
namespace packages\packagename\controllers;

use packages\packagename\User as Model;
use packages\base\{Controller, Response, InputValidationException, Session};

class Users extends Controller {
public function login(): Response {
$inputs = $this->checkinputs(array(
'username' => array(
'type' => 'email',
),
'password' => array(),
));
$model = new Model();
$model->where("username", $inputs["username"]);
$user = $user->getOne();
if (!$user) {
throw new InputValidationException("username");
}
if (!Password::verify($inputs["password"], $user->password)) {
throw new InputValidationException("password");
}
Session::set("loggin", true);
Session::set("userID", $user->id);
$this->response->setStatus(true);
return $this->response;
}
}

Example 2

controllers/Users.php
<?php
namespace packages\packagename\controllers;

use packages\packagename\User as Model;
use packages\base\{Controller, Response, InputValidationException, Session};

class Users extends Controller {
public function login(): Response {
$inputs = $this->checkinputs(array(
'username' => array(
'type' => ['email', 'cellphone'],
),
'password' => array(),
));
$model = new Model();
$model->where("username", $inputs["username"]);
$user = $user->getOne();
if (!$user) {
throw new InputValidationException("username");
}
if (!Password::verify($inputs["password"], $user->password)) {
throw new InputValidationException("password");
}
Session::set("loggin", true);
Session::set("userID", $user->id);
$this->response->setStatus(true);
return $this->response;
}
}

IP version 4

To validate IP version 4, the ip4 value is used for the type key.

Example 1

controllers/Cronjobs.php
<?php
namespace packages\packagename\controllers;

use packages\cronjob\Task;
use packages\base\{Controller, Response};

class Cronjobs extends Controller {
public function store(): Response {
$inputs = $this->checkinputs(array(
'hour' => array(
'type' => 'number',
'values' => range(0, 24),
),
'minuts' => array(
'type' => 'string',
'min' => 0,
'max' => 60,
),
'ip' => array(
'type' => 'ip4',
),
'command' => array(
'type' => 'string',
'htmlTags' => true,
),
'port' => array(
'type' => 'uint16',
'optional' => true,
'default' => 22,
),
));
$model = new Task();
$model->hour = $inputs["hour"];
$model->minuts = $inputs["minuts"];
$model->ip = $inputs["ip"];
$model->command = $inputs["command"];
$model->port = $inputs["port"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}

Internet address (URL)

To validate URL addresses, the url value is used for the type key.

If, in the URL, we need the protocol to be written or the protocol type to be checked, by defining the protocols key with the value of the desired protocol type, we make the presence of the protocol mandatory for validation. For protocols, an array of protocols can be specified.

Example

controllers/Cronjobs.php
<?php
namespace packages\packagename\controllers;

use packages\cronjob\Task;
use packages\base\{Controller, Response};

class Cronjobs extends Controller {
public function store(): Response {
$inputs = $this->checkinputs(array(
'hour' => array(
'type' => 'number',
'values' => range(0, 24),
),
'minuts' => array(
'type' => 'string',
'min' => 0,
'max' => 60,
),
'ip' => array(
'type' => 'ip4',
),
'hostname' => array(
'type' => 'url',
'protocols' => 'https',
),
'command' => array(
'type' => 'string',
'htmlTags' => true,
),
'port' => array(
'type' => 'uint16',
'optional' => true,
'default' => 22,
),
));
$model = new Task();
$model->hour = $inputs["hour"];
$model->minuts = $inputs["minuts"];
$model->command = $inputs["command"];
$model->ip = $inputs["ip"];
$model->hostname = $inputs["hostname"];
$model->port = $inputs["port"];
$model->save();
$this->response->setStatus(true);
return $this->response;
}
}

In the example above, if the address lacks a protocol or its protocol is not equal to https, an exception is thrown.

File

To validate files, the file value is used for the type key. You can use the following keys to manage file validation.

File type

The extension key is used to specify the file type. Its value can be an array of allowed extensions. If extension is not set, files with any extension are accepted.

File size

Using the min-size and max-size keys, you can specify a size limit for the received file.

Uploading multiple files

If the received file is an array of multiple files, by setting the multiple key to true, the framework is informed that the received data contains multiple files.

Converting the received file to a File class object

By defining the obj key with a value of true, the received file is converted to an object of the local\Tmp class. If this key is not set, it is considered false, in which case the validator's output is the same as the output of $_FILES.

info

For more information about files, refer to the File page.

note

When working with files, the form tag must have the enctype="multipart/form-data" attribute.

Example 1

controllers/Files.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, Packages};

class Files extends Controller {
public function upload(): Response {
$inputs = $this->checkInputs(array(
"files" => array(
"type" => "file",
"max-size" => 2097152, // Byte
"obj" => true,
"extension" => ["pdf", "word"],
"multiple" => true, // You can false or remove this line if you to accept only one file.
),
));
foreach ($inputs["files"] as $file) {
$name = $file->md5();
$localFile = Packages::package("packagename")->file("storage/private/files/{$name}.{$file->getExtension()}");
if ($localFile->exists()) {
continue;
}
$directory = $localFile->getDirectory();
if (!$directory->exists()) {
$directory->make(true);
}
$file->copyTo($localFile);
}

$this->response->setStatus(true);
return $this->response;
}
}

In the example above, only pdf or word files are accepted, and their maximum size can be 2 megabytes. And the validator's output is an object of the local\Tmp class.

Image

In the framework, in addition to using the file validator, a dedicated validation for images has been provided. To use it, the image value is used for the type key.

The validator accepts images with the extensions jpeg, jpg, png, gif, webp.

Using the extension key, you can specify that only some of the above extensions are accepted.

For image validation, factors such as the image size and its width and height can also be validated. The following keys are used to manage image validation.

Image size

Using the min-size and max-size keys, you can specify a size limit for the received image.

Image width

To specify the minimum and maximum allowed width of the image, the min-width and max-width keys are used, whose value is a number in pixels.

Image height

To specify the minimum and maximum allowed height of the image, the min-height and max-height keys are used, whose value is a number in pixels.

Resizing the image

In the framework, you can specify dimensions for the image so that after the image is validated, its dimensions are changed to the defined size. Resizing is specified using the resize key.

Converting the received image to an Image class object

By defining the obj key with a value of true, the received image is converted to an object of the packages\base\Image class. If this key is not set, it is considered false, in which case the validator's output is the same as the output of $_FILES.

info

For more information about images, refer to the Image page.

note

When working with files, the form tag must have the enctype="multipart/form-data" attribute.

Example

controllers/Files.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, Packages};

class Files extends Controller {
public function upload(): Response {
$inputs = $this->checkInputs(array(
"pic" => array(
"type" => "image",
"max-size" => 2097152, // Byte
"obj" => true,
"extension" => array('jpeg', 'jpg'),
"resize" => array(100, 120),
),
));
$file = $inputs["pic"]->getFile();
$localFile = Packages::package("packagename")->file("storage/private/files/{$file->md5()}.{$file->getExtension()}");
if ($localFile->exists()) {
$this->response->setStatus(true);
return $this->response;
}
$directory = $localFile->getDirectory();
if (!$directory->exists()) {
$directory->make(true);
}
$inputs["pic"]->saveToFile($localFile);

$this->response->setStatus(true);
return $this->response;
}
}

In the example above, only images with jpeg and jpg formats are accepted. The maximum image size can be 2 megabytes. The validator's output is an image with a width of 100px and a height of 120px, which is an object of the packages\base\Image class.

Data of Model class types

The received data can be of Model class types. For validation, specify the Model class in the type key, and the framework performs the data validation.

To validate models, you can use the query key to add conditions for approving the data. The value of the query key is a function. The function takes one input argument, which is the object of the Model class specified in type.

Example
Sample Model file

Ticket.php
<?php
namespace packages\packagename;

use packages\base\{db\dbObject, Date};

class Ticket extends dbObject {

const OPEN = 1;
const CLOSED = 2;

protected $dbTable = "packagename_tickets";
protected $primaryKey = "id";
protected $dbFields = array(
'created_at' => array('type' => 'int', 'required' => true),
'text' => array('type' => 'text', 'required' => true),
'status' => array('type' => 'int', 'required' => true),
);

public function preLoad(array $data): array {
if (!isset($data["created_at"])) {
$data["created_at"] = Date::time();
}
if (!isset($data["status"])) {
$data["status"] = self::OPEN;
}
return $data;
}

}

Sample controller file without using Model classes

controllers/Tickets.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, InputValidationException};
use packages\packagename\{Ticket as TicketModel, ticket\Message as Model};

class Tickets extends Controller {
public function response(): Response {
$inputs = $this->checkinputs(array(
'ticket' => array(
'type' => 'number',
),
'message' => array(
'type' => 'text',
),
));

$model = new TicketModel();
$model->where("id", $inputs["ticket"]);
$model->where("status", Ticket::OPEN);
$inputs["ticket"] = $model->getOne();
if (!$inputs["ticket"]) {
throw new InputValidationException("ticket");
}

$model = new Model();
$model->ticket_id = $inputs["ticket"]->id;
$model->text = $inputs["text"];
$model->save();

$this->response->Go(url("userpanel/tickets/{$inputs['ticket']->id}/overview"));

$this->response->setStatus(true);
return $this->response;
}
}

Sample controller file using Model classes

controllers/Tickets.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response};
use packages\packagename\{Ticket as TicketModel, ticket\Message as Model};

class Tickets extends Controller {
public function response(): Response {
$inputs = $this->checkinputs(array(
'ticket' => array(
'type' => Ticket::class,
'query' => function ($query) {
$query->where("status", TicketModel::OPEN);
}
),
'message' => array(
'type' => 'text',
),
));

$model = new Model();
$model->ticket_id = $inputs["ticket"]->id;
$model->text = $inputs["text"];
$model->save();

$this->response->Go(url("userpanel/tickets/{$inputs['ticket']->id}/overview"));

$this->response->setStatus(true);
return $this->response;
}
}
info

For more information about how to create forms, refer to the Form page.

Closures

If the data you intend to validate is of a data type other than those defined in the framework, in addition to defining a new validator, you can use Closures. With this method, there is no need to create a validator class; you only need to define the desired validation operation in a function that is given to the type key.

This function is exactly like the validate method defined in the validator classes.

The function takes three input arguments, which are, respectively, the data received from the user, the rules specified for validation, and the field name.

Example

controllers/Banks.php
<?php
namespace packages\packagename\controllers;

use packages\base\{Controller, Response, Validator};

class Banks extends Controller {
public function update(): Response {
$inputs = $this->checkinputs(array(
"banks" => array(
"type" => function($data, array $rule, string $input) {
if (!is_array($data)) {
throw new InputValidationException($input);
}
if (!$data) {
if (!isset($rule['empty']) or !$rule['empty']) {
throw new InputValidationException($input);
}
if (isset($rule['default'])) {
return $rule['default'];
}
}
foreach ($data as $key => $value) {
if (!isset($value["id"])) {
throw new InputValidationException($input . "[{$key}][id]");
}
if (!isset($value["account"])) {
throw new InputValidationException($input . "[{$key}][account]");
}
}
foreach ($data as $key => $value) {
foreach ($data as $key2 => $value2) {
if ($key != $key2 and $value["id"] == $value2["id"]) {
throw new DuplicateRecord($input . "[{$key}][id]");
}
}
$bank = Bank::byId($value["id"]);
if (!$bank) {
throw new InputValidationException($input . "[{$key}][id]");
}
$data[$key]["bank"] = $bank;
}
return $data;
}
),
));
foreach ($inputs["banks"] as $item) {
$item["bank"]->account = $item["account"];
$item["bank"]->save();
}
$this->response->setStatus(true);
return $this->response;
}
}

Exception

Each of the received data items is validated according to the rules specified for them as defined above. For any data item that is invalid, an exception of type packages\base\InputValidationException is thrown. The framework handles this exception, and it is converted into a form error. (There is no need to write try catch to handle the exception.) The errors are automatically recorded in the setFormError method of the packages\base\views\form class.

Also, for ease of use and managing form errors, you can use the createField method in the userpanel package. Using this method, the thrown exception is automatically displayed as an error in the template's appearance, at the location of the input or select.

info

For more information, refer to the Form page.

Example 1

controllers/Main.php
<?php
namespace packages\packagename\controllers;
use packages\base\{Controller, Response, View};
use packages\packagename\User;
use themes\themename\views;

class Main extends Controller {

public function register(): Response {
$view = View::byName(views\Register::class);
$this->response->setView($view);
$this->response->setStatus(false);
$inputsRules = array(
"firstname" => array(
"type" => "string",
),
"lastname" => array(
"type" => "string",
"optional" => true,
"empty" => true,
),
"email" => array(
"type" => "email",
),
"cellphone" => array(
"type" => "cellphone",
),
"state" => array(
"values" => array("Tehran", "Esfahan")
),
"publish_email" => array(
"type" => "bool",
),
"password" => array(),
"password_again" => array()
);
try {
$inputs = $this->checkinputs($inputsRules);

if ($inputs["password"] != $inputs["password_again"]) {
throw new InputValidationException("password_again");
}
$user = new User();
$user->firstname = $inputs["firstname"];
if (isset($inputs["lastname"])) {
$user->lastname = $inputs["lastname"];
}
$user->email = $inputs["email"];
$user->cellphone = $inputs["cellphone"];
$user->password = md5($inputs["password"]);
$user->publish_email = $inputs["publish_email"];
$user->save();
$this->response->setStatus(true);
$this->response->Go(base\url("userpanel"));
} catch(InputValidationException $error) {
$view->setFormError(FormError::fromException($error));
$view->setDataForm($this->inputsvalue($inputsRules));
}
return $this->response;
}
}

To handle the exception in the catch, the setFormError and setDataForm methods, which are defined in the packages\base\views\form class, have been used.

note

Exception handling is done by the framework, and there is no need to write try catch. The above example is stated only for the programmer's further information.

The setFormError method transfers the details of the invalid field to the view. In the view, using the getFormErrors method, the details of the field that has an error can be obtained.

When the user is redirected back to the form page, the previously entered data in the inputs has been cleared. To display the previous data, using the setDataForm method the previous data is transferred to the template, and in the template, by calling the getDataForm method, the previously entered data is accessible.

note

To work with the above methods, the defined view class must inherit from the packages\base\views\form class, or packages\base\views\traits\form must be used in the class.

Example 2

controllers/Main.php
<?php
namespace packages\packagename\controllers;

use themes\themename\views;
use packages\packagename\User as Model;
use packages\base\{Controller, Response, View};

class Main extends controller {
public function login(): Response {
$view = View::byName(views\Login::class);
$this->response->setView($view);
$this->response->setStatus(false);
$inputs = $this->checkinputs(array(
"username" => array(
"type" => "email",
),
"password" => array()
));
try {
$model = new Model();
$model->where("email", $inputs["username"]);
$user = $model->getOne();
if (!$user) {
throw new inputValidationException("username");
}
if (Password::verify($inputs["password"], $this->password)) {
throw new inputValidationException("password");
}
Session::set("login", true);
Session::set("userID", $user->id);

$this->response->setStatus(true);
$this->response->Go(base\url("userpanel"));
} catch (InputValidationException $e) {
$e->setInput(""); // For security reason, the users should not know which field is wrong
throw $e;
}
return $this->response;
}
}