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:
- String data
- Numeric data
- Boolean data
- Date
- Cellphone number
- IP version 4
- Internet address (URL)
- File
- Image
- Data of Model class types
- Closures
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
<?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
<?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
<?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
<?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 <h1>salam</h1>.
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
<?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.
| Type | Allowed 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.
- If the field is numeric and its
emptyistrue, when the field's value is empty,nullis considered as the field's value. - If the entered number is outside the allowed numeric range, an exception of type
InputValidationExceptionis thrown with the messagemin-valueormax-value. - If the entered data is not a number, an exception of type
InputValidationExceptionis thrown with the messagenot-a-number. - If the entered number is not equal to the defined value, an exception of type
InputValidationExceptionis thrown with the messagenot-defined-value.
Example 1
<?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
<?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;
}
}
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.
If the value of empty is true, when the field is empty its value is considered to be false.
Example
<?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.
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
<?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.
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
<?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
<?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
<?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
<?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
<?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.
For more information about files, refer to the File page.
When working with files, the form tag must have the enctype="multipart/form-data" attribute.
Example 1
<?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.
For more information about images, refer to the Image page.
When working with files, the form tag must have the enctype="multipart/form-data" attribute.
Example
<?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
<?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
<?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
<?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;
}
}
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
<?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.
For more information, refer to the Form page.
Example 1
<?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.
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.
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
<?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;
}
}