Creating and Processing GET and POST Requests with Forms in CodeIgniter 4

Video thumbnail

Now that we have developer mode active in CodeIgniter 4, we are going to learn how to perform the typical data submission from an HTML form with some HTML fields defined in it in CodeIgniter 4, so they can be processed through a specific controller or function defined within the controller; for this, we will need a couple of functions, an HTML view, and a model that we explained previously, in addition to having the database connection and the generated table; all these are concepts that we saw earlier, and therefore you can visit the corresponding articles.

A very important point if we remember how we worked in CodeIgniter 3, that is, the previous version of the framework, is that you have more flexibility when defining these types of processes; that is, we could perfectly define the entire process of rendering the view/form and processing it in a single function, but this is not recommended in CodeIgniter 4 and is not the approach we should follow here; and this is a very important point, CodeIgniter 4 adopts the organization of other frameworks like Laravel to define resource-type controllers that we will address in another entry, but in short, it allows us to create all CRUD-type routes.

Handling Routes: The Resource concept

To build this, we must return to route management. In CodeIgniter 4, we can define individual routes (GET, POST, PUT, DELETE), but if you are familiar with Laravel, this will sound very familiar: we can use Resource Routes.

A resource-type route automatically builds the entire CRUD for us. If we go to the official documentation, we will see that defining a resource route generates the following internal routes:

  • GET /persona/new: To display the creation form (new function).
  • POST /persona: To process the form submission (create function).
  • GET /persona: For the general listing (index function).
  • PUT/PATCH/DELETE: To update and delete records.

For our example, we will define the route in app/Config/Routes.php:

$routes->resource('persona');

Necessary components to work with forms in CodeIgniter 4

  • A view that will only be consumed via GET, a GET-type request that will be responsible for rendering our view, specifically our form.
  • Another function that will be responsible for processing the form request that will go via POST, which is the recommended method type for creating resources (and CodeIgniter also uses these types of requests to update records, but we will see this in another entry).
  • An HTML view, which is our form.
  • A model, which is simply the entity we will work with to create a record based on the information or data received by the form.

Creation of the CRUD Controller, Model, and Route

In this block, we are going to go step by step building the application we mentioned earlier.

Defining the Model

We are going to work with the people model:

<?php namespace App\Models;
 
use CodeIgniter\Model;
 
class PersonModel extends Model {
    protected $table = 'people';
    protected $primaryKey = 'id';
    protected $allowedFields = ['name', 'surname','age','description'];
}

Define the controller

Now, we are going to create a controller that we will call Person.php:

namespace App\Controllers;
use App\Models\PersonModel; // We import the model
class Persona extends BaseController {
    
    // Shows the form
    public function new() {
        return view('persona/new');
    }
    // Processes the form data
    public function create() {
        // Here we will receive the POST request
    }
}

Remember that the controller is the layer responsible for communicating the view with the model, and beyond that, it is what holds the logic and organization to perform each of the processes mentioned above: rendering a view and processing a form.

Define CRUD resource routes

Since we are going to take advantage of this form to create a complete CRUD in subsequent entries, let's define a resource route as explained earlier:

$routes->resource('person');

With this, we now have to define functions in our controller with corresponding names and parameters that will be in charge of performing our CRUD, although for now we are only interested in creating a record.

Function to render a GET form

The first function we will see would be to render a form, therefore we don't have to do anything else; let's remember we are creating a resource-type route and therefore we must create a function named "new", which is the one that allows being consumed via a GET request and will simply return a view:

public function new(){
   return view('person/new');
}

Function to create a POST record

Now the most interesting function, the one that can only be consumed via a POST request and therefore is the one that receives our form data traveling via a POST request:

public function create() {
    // Instantiate the model
    $model = new PersonModel();
    // Get the POST data
    $data = [
        'name'        => $this->request->getPost('name'),
        'surname'     => $this->request->getPost('surname'),
        'age'         => $this->request->getPost('age'),
        'description' => $this->request->getPost('description'),
    ];
    // Insert into the database
    $model->insert($data);
    echo "Record inserted successfully";
}

The $allowedFields field

If you receive an error when trying to insert, remember that in the Model (PersonModel.php) you must specify which fields you allow to be insertable through the $allowedFields property. This is a security measure in CodeIgniter 4:

protected $allowedFields = ['name', 'surname', 'age', 'description'];

The request to obtain data

Let's remember that regardless of what you use, there is always something called "request", which is our user's request; here the user's data travels, the data from the previous form, therefore, it is the mechanism we must use to obtain the data. In C4, to obtain data sent via POST it would be as follows:

this->request->getPost('key')

Where "key" matches the name of the field; therefore, to obtain each of the fields from the previous form, for example, "name":

$this->request->getPost('name')

And now we simply want to insert a record: for that we must create an instance of our model, which in this case would be for people, and use the function called "insert" which is in charge of inserting a record; it's that simple, with this we have the complete process.

View Layer (Form) and URL Configuration

We are going to create a simple form in the app/Views folder of our application as follows, which we will call new.php to match the function name:

<form action="<?= base_url('persona') ?>" method="post">
    <label for="name">Name</label>
    <input type="text" name="name" id="name">
    
    <label for="surname">Surname</label>
    <input type="text" name="surname" id="surname">
    
    <label for="age">Age</label>
    <input type="number" name="age" id="age">
    
    <label for="description">Description</label>
    <textarea name="description" id="description"></textarea>
    <button type="submit">Send</button>
</form>

Return to the form view

Now then, at this point, if you try to create a record from: http://codeigniter4.test/person/new or whichever domain you have configured, you will see that in the end it stays on a blank screen, and this is because the create function returns nothing; here you could perfectly do something like the following to render a view from create:

public function create()
    {
     *** Rest of code
        return view('person/new');
    }

But in this way, we are basically doing the same work as the new function, and we know that duplicating code to make it look the same is bad in any programming language. Furthermore, if we try to refresh the page at this point, we will see a strange alert like the following:

Blank screen

And this is because we are sending a POST type request again; and it doesn't look right. Therefore, what we are going to do is simply return to the previous function; for this, we can use routes and we have a function called "back" that allows us to return to the previous function:

return redirect()->back(); 

It's as if you press back or return in your browser.

If we complete the form and press "Send", the routing will take us to the create function, which will take the data and save it in the "people" table. When checking our database (for example, in PHPMyAdmin or Laragon), we will see the new record inserted correctly.

With this, we have learned how to basically process a form using resource-type routes in CodeIgniter 4!

Extra: Remember previous values with the old function in CodeIgniter 4

Surely you are interested in having C4 remember previous values in your form; for that, we can use a function called "old" which receives two parameters:

  1. Field name
  2. Default value (which is what we will set when no previous value exists, for example, when entering the "new" page for the first time.)

Now, so the data can be taken from the redirection we are doing, we must use the function called "withInput" at the moment of redirection:

return redirect()->back()->withInput();

And for the form fields, we have to define the function in question; for example:

<?=old('name',"")?>

Form Validation Rules

Performing validations on data received from the user through a form is fundamental to ensuring the integrity and quality of the requested data. For this, form validations are used, indicating which fields are mandatory and what structure they must have. Validations are particularly important in situations where this data is relied upon, as errors can be costly or even dangerous since non-validated forms can allow SQL injection processes among other types of attacks.

Validating forms in CodeIgniter is very easy; we just need to know what we want to validate and which rules we are going to incorporate. For example, if we have the following form:

	<div class="form-group">
		<label>Name</label>
		<input type="text" value="<?php echo $name ?>" class="form-control" placeholder="Name" name="name" required>
		<?php echo form_error('name', '<div class="text-danger">', '</div>') ?>
	</div>
	<div class="form-group has-feedback">
		<label>Surname</label>
		<input type="text" value="<?php echo $surname ?>" class="form-control" placeholder="Surname" name="surname" required>
		<?php echo form_error('surname', '<div class="text-danger">', '</div>') ?>
	</div>
	<div class="form-group has-feedback">
		<label>Phone</label>
		<input type="text" value="<?php echo $phone1 ?>" class="form-control" placeholder="Phone" name="phone1" required>
		<?php echo form_error('phone1', '<div class="text-danger">', '</div>') ?>
	</div>
	<div class="form-group has-feedback">
		<label>Phone 2</label>
		<input type="text" value="<?php echo $phone2 ?>" class="form-control" placeholder="Phone 2" name="phone2" required>
		<?php echo form_error('phone2', '<div class="text-danger">', '</div>') ?>
	</div>

We want all fields to be present except for Phone 2 which is completely optional. We want to place various maximum and minimum lengths on our form and also want some fields like age to only allow integer data types; all this and much more can be done in CodeIgniter through form validation. Among the most important ones, we have:

RuleParametersDescriptionExample
requiredNoReturns true or false if the element is empty. 
min_lengthYesReturns FALSE if the form element has a length shorter than the one defined in the parameter.min_length[3]
max_lengthYesReturns FALSE if the form element has a length longer than the one defined in the parameter.max_length[12]
exact_lengthYesReturns FALSE if the form element has a length different from the one defined in the parameter.exact_length[8]
valid_emailNoReturns FALSE if the form element does not have a valid email. 
numericNoReturns FALSE if the form element contains any other element that is not a number. 
integerNoReturns FALSE if the form element is not an integer number. 

As you can see, we have validation rules for many cases: data types, emails, decimals, required, and maximum and minimum quantities for string lengths; these are the ones I consider most important.

So, to place validation rules in the previous form, we must do the following:

$this->form_validation->set_rules('name', 'Name', 'max_length[100]|required');
$this->form_validation->set_rules('surname', 'Surname', 'max_length[100]|required');
$this->form_validation->set_rules('phone1', 'Cell phone number', 'min_length[9]|max_length[9]|required');
$this->form_validation->set_rules('phone2', 'Cell phone number', 'min_length[9]|max_length[9]);

As you can see, we added multiple validation rules separated by pipes (|); additionally, note that Phone 2 does not have the "required" validation type applied because it is completely optional; then at the moment of sending the POST (it can also be GET, simply change POST for GET):

if ($this->form_validation->run()) {
	// data to save
	$save = array(
		'name' => $this->input->post("name"),
		'surname' => $this->input->post("surname")
	);
}

Here simply when we detect that the form is a POST and it is valid, we compose an array to later save it in the database; but you can do whatever you want with the validated data.

Custom Rules for the Form in CodeIgniter

You might want to create your own custom validation rules for CodeIgniter; for that, you simply have to create a function in your controller class and evaluate its condition; the important thing is that it returns either true or false:

function validate_passwd($password) {
	// At least one digit required
	// $regex = '(?=.*\d)';
	// At least one lower case letter required
	$regex = '(?=.*[a-z])';
	// At least one upper case letter required
	// $regex .= '(?=.*[A-Z])';
	// No space, tab, or other whitespace chars allowed
	$regex .= '(?!.*\s)';
	// No backslash, apostrophe or quote chars are allowed
	$regex .= '(?!.*[\\\\\'"])';
	if (preg_match('/^' . $regex . '.*$/', $password)) {
		return TRUE;
	}
	return FALSE;
}

And then you add it to your validation rules by placing the previously created function with the callback_ prefix:

$crud->set_rules('passwd', 'Password', 'min_length[8]|required|max_length[12]|callback_validate_passwd_admin');

Then go to your language file located in application\language\english\form_validation_lang.php and configure the error message for your new rule:

$lang['validate_same_passwd'] = "The password must be the same as the one stored in the system.";

Here we are taking as an example that you are using English as the default language in your project, but if you change the language, you simply change the folder.

Custom Messages for Validation Rules:

In the previous file, we can define the messages in case we want to customize them; but we can also do it from the controller:

$this->form_validation->set_message('min_length', '{field} must have at least {param} characters.');

Show Form Errors

Finally, to show the form errors we can use:

<?php echo form_error(name, '<div class="text-danger">', '</div>') ?>

This element is generally placed in the form view, next to the form element to which we applied the validation rule, which for this example is one called name.

Show All Form Errors at Once

Many times it is useful to show errors together, all form errors from a single place from a single function; for that, the following function exists:

validation_errors()

You can also define the element that will contain said error, which by default is simply a text line; you can place it in a Bootstrap alert for example:

validation_errors('<div class="alert alert-danger" role="alert">', '</div>')

Full example of a form element in Bootstrap 5:

<div class="form-group">
    <?php
    echo form_label('Title', 'name');
    ?>
    <?php
    $text_input = array(
        'name' => 'name',
        'minlength' => 10,
        'maxlength' => 100,
        'required' => 'required',
        'id' => 'name',
        'value' => $name,
        'class' => 'form-control input-lg',
    );
    echo form_input($text_input);
    ?>
    <?php echo form_error('name', '<div class="text-danger">', '</div>') ?>
</div>

Custom Validations in CodeIgniter 4

Custom validations in CodeIgniter 4
Video thumbnail

Validations in CodeIgniter 4 are a fundamental point in any application and in CodeIgniter 4 it is no exception; being able to validate our data to be sure that it is valid and safe.

These are processes that we must carry out in our applications; but surely there are validations that are a bit more specific to your business and therefore you cannot use the generic ones offered by the framework; in those cases, we have to create one ourselves; in CodeIgniter 4 we can do it easily and with them we avoid over-cluttering the controller which is where we generally place them; we have to go to the file:

config/Validation.php
In it, we will see a series of defined classes:
public $ruleSets = [
\CodeIgniter\Validation\Rules::class,
\CodeIgniter\Validation\FormatRules::class,
\CodeIgniter\Validation\FileRules::class,
\CodeIgniter\Validation\CreditCardRules::class,
];

Which you can review and you will see they are classes with functions. The function names correspond to the validations we can use and they receive an argument, which as you can guess is the validation parameter; the framework handles all this mapping for us. In my example (which is part of my full CodeIgniter 4 course that you can take on this platform), we are going to create a file inside:

app/validator/UserRules

We are going to create an extra folder called /app/Validation (it can have any name) and inside it a file called UserRules (or the name you prefer):

<?php
namespace App\Validation;
use App\Models\UserModel;
class UserRules
{
}

And within this file and classes, we create our validation functions to which we apply our validations; logically, they must return a boolean: true if it passes validation and false otherwise:

    public function provider(string $userId): bool
    {
        return $this->checkType($userId, "provider");
    }
    public function customer(string $userId): bool
    {
        return $this->checkType($userId, "customer");
    }

To use them, simply:

        $res = $this->validate([
            'user_id' => 'required|customer'
        ]);

Extra, create validations with parameter passing

We can also create more general validations in which we can pass one or more parameters through the validation definition:

$res = $this->validate([
            'user_id' => 'required|checkType[customer]',
        ]);

And in our class:

   public function checkType($userId, $type)
    {
        $userModel = new UserModel();
        $user = $userModel->asObject()->where("type", $type)->find($userId);
        return $user != null;
    }

Validations and printing form errors in CodeIgniter 4 with Bootstrap 5 style

Custom validations in CodeIgniter 4
Video thumbnail

Validations are a fundamental mechanism we use to show errors that occur in a form. Therefore, in CodeIgniter 4 we can quickly create validation rules from the framework, in its configuration file, and configure it anywhere from our controllers.

Let's see how we can use validations on our form. For this, we can basically do it in two ways: the first would be on the function responsible for receiving the data we created earlier, for example, the "create" function, to create a new element, in this case, a person:

    public function create(){
        $movie = new MovieModel();
        if($this->validate('movies')){
            $id = $movie->insert([
                'title' =>$this->request->getPost('title'),
                'description' =>$this->request->getPost('description'),
            ]);
            return redirect()->to("/movie/$id/edit")->with('message', 'Movie created successfully.');
        }
        return redirect()->back()->withInput();
    }

Validation files

CodeIgniter starting from version 4 allows creating validation files, which would simply be an array that we must implement with the validations and we just place the validations we want to use. The file is found in app/Config/Validation and you simply have to define your validation rules:

    public $movies =[
        'title' => 'required|min_length[3]|max_length[255]',
        'description' => 'min_length[3]|max_length[5000]'
    ];

Basically very similar to the previous example, but this way it is much more organized, more extensible, and therefore more usable.

The point here is that you place the name of the field as the key of the previous array followed by the rules...

Basically very similar to the previous example, but in this way it is much more organized, more extensible and therefore more usable.

The point here is that you place as the key of the previous array the name of the field followed by the rules that you can see what types of rules you can place in the official documentation.

So now we have a schema that we can use to work with validations.

Show validation errors

We have to show the validation errors in the new view, for that we have to load a validation service from this file that we then pass to the view to show them:

  public function new()
    {
        $validation =  \Config\Services::validation();
        return view('person/new',['validation' => $validation]);
    }

And in the view we receive a collection, that is, a list of errors that we can print as follows:

<?= $validation->listErrors() ?>

To give a custom style for errors; You can go to the Config/Validation.php file and in the templates variable, indicate the reference to the file that must be used as a template to print the errors; for example:

    public $templates = [
        //'list'   => 'CodeIgniter\Validation\Views\list',
        'list'   => 'App\Views\Validations\list_bootstrap',
        'single' => 'CodeIgniter\Validation\Views\single',
    ];

Create template to display errors with Bootstrap

And we create such a file, for example, with the Bootstrap style:

<?php if (! empty($errors)) : ?>
    <div class="errors" role="alert">      
        <?php foreach ($errors as $error) : ?>
            <div class="alert alert-danger"><?= esc($error) ?></div>
        <?php endforeach ?>
    </div>
<?php endif ?>

But, as you can see, you can customize it 100% and include, for example, your style that you have for other CSS kits like Tailwind.css

Conclusions

So now you know how you can use custom validations and a bit more than that, pass parameters to custom validations.

Next step, learn how to upload files to CodeIgniter 4.

We are going to show the process to send forms in CodeIgniter 4, from the creation of routes, controller, functions and of course, the process of the request or request of our user.

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español