How To Develop An AJAX CRUD App In CodeIgniter 4

How To Develop An AJAX CRUD App In CodeIgniter 4

Avatar photoPosted by

Hello fellow developers, Today I will be sharing to you how to develop a AJAX CRUD application using CodeIgniter 4. Before let have first a brief explanation of what is AJAX and CRUD.

CodeIgniter is a open-source web framework that is used for rapid web development. CodeIgniter follows the MVC (Model-View-Controller) architectural pattern. It is noted for its speed compared to other PHP web frameworks.

AJAX(Asynchronous JavaScript and XML) is a set of web development techniques that use many web technologies which allow web applications work in asynchronously.

CRUD is an acronym for CREATE, READ, UPDATE, DELETE. The four basic functions in any type of programming. CRUD typically refers to operations performed in a database.

  • Create -Generate new record/s.
  • Read – Reads or retrieve record/s.
  • Update – Modify record/s.
  • Delete – Destroy or remove record/s.

Tutorial Video:

Step 1: Install CodeIgniter 4

For us to install CodeIgniter 4 we can install via composer or directly download CodeIgniter 4 here:

Install via composer:

composer create-project codeigniter4/appstarter ci-4-crud-ajax

Step 2: Change CodeIgniter Environment

The default environment of CodeIgniter is production, it is a safety feature to add security in case settings are messed up when it goes live. For us to change the environment we will rename or copy the file env to .env. Once it is renamed, open the file and uncomment and change the CI_ENVIRONMENT value from production to development.

.env

CI_ENVIRONMENT = development

Step 3: Configure Database

After setting up the environment, we will then configure our database. You can configure it on .env or on the config file located at app/Config/Database.php. For this tutorial we will configure it on app/Config/Database.php.

Configure the database connection values:

app/Config/Database.php.

<?php

namespace Config;

use CodeIgniter\Database\Config;

/**
 * Database Configuration
 */
class Database extends Config
{
	/**
	 * The directory that holds the Migrations
	 * and Seeds directories.
	 *
	 * @var string
	 */
	public $filesPath = APPPATH . 'Database' . DIRECTORY_SEPARATOR;

	/**
	 * Lets you choose which connection group to
	 * use if no other is specified.
	 *
	 * @var string
	 */
	public $defaultGroup = 'default';

	/**
	 * The default database connection.
	 *
	 * @var array
	 */
	public $default = [
		'DSN'      => '',
		'hostname' => 'localhost',
		'username' => 'root',
		'password' => '',
		'database' => 'ci_4_crud_ajax',
		'DBDriver' => 'MySQLi',
		'DBPrefix' => '',
		'pConnect' => false,
		'DBDebug'  => (ENVIRONMENT !== 'production'),
		'charset'  => 'utf8',
		'DBCollat' => 'utf8_general_ci',
		'swapPre'  => '',
		'encrypt'  => false,
		'compress' => false,
		'strictOn' => false,
		'failover' => [],
		'port'     => 3306,
	];

	/**
	 * This database connection is used when
	 * running PHPUnit database tests.
	 *
	 * @var array
	 */
	public $tests = [
		'DSN'      => '',
		'hostname' => '127.0.0.1',
		'username' => '',
		'password' => '',
		'database' => ':memory:',
		'DBDriver' => 'SQLite3',
		'DBPrefix' => 'db_',  // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
		'pConnect' => false,
		'DBDebug'  => (ENVIRONMENT !== 'production'),
		'charset'  => 'utf8',
		'DBCollat' => 'utf8_general_ci',
		'swapPre'  => '',
		'encrypt'  => false,
		'compress' => false,
		'strictOn' => false,
		'failover' => [],
		'port'     => 3306,
	];

	//--------------------------------------------------------------------

	public function __construct()
	{
		parent::__construct();

		// Ensure that we always set the database group to 'tests' if
		// we are currently running an automated test suite, so that
		// we don't overwrite live data on accident.
		if (ENVIRONMENT === 'testing')
		{
			$this->defaultGroup = 'tests';
		}
	}

	//--------------------------------------------------------------------

}

Step 4: Create A Model and Migration

Model – it a class that represents a database table.

Migration – like version control for the database that allows us to modify and share database schema to your team.

Execute this command on the Terminal or CMD to create a model:

php spark make:model ProjectModel

Open the created model at app/Models/ProjectModel.php. Inside the file you can see configuration options, you can read the documentation to further learn about its configuration options. We will now update the configs:

app/Models/ProjectModel.php

<?php
 
namespace App\Models;
 
use CodeIgniter\Model;
 
class ProjectModel extends Model
{
    protected $DBGroup              = 'default';
    protected $table                = 'projects';
    protected $primaryKey           = 'id';
    protected $useAutoIncrement     = true;
    protected $insertID             = 0;
    protected $returnType           = 'object';
    protected $useSoftDeletes       = true;
    protected $protectFields        = true;
    protected $allowedFields        = ['name', 'description'];
 
    // Dates
    protected $useTimestamps        = true;
    protected $dateFormat           = 'datetime';
    protected $createdField         = 'created_at';
    protected $updatedField         = 'updated_at';
    protected $deletedField         = 'deleted_at';
 
    // Validation
    protected $validationRules      = [
        'name' => 'required',
        'description' => 'required',
    ];
    protected $validationMessages   = [];
    protected $skipValidation       = false;
    protected $cleanValidationRules = true;
 
    // Callbacks
    protected $allowCallbacks       = true;
    protected $beforeInsert         = [];
    protected $afterInsert          = [];
    protected $beforeUpdate         = [];
    protected $afterUpdate          = [];
    protected $beforeFind           = [];
    protected $afterFind            = [];
    protected $beforeDelete         = [];
    protected $afterDelete          = [];
}

After creating the model, we will then create a migration file.

Execute this command on the Terminal or CMD to create a migration:

php spark make:migration AddProject

Open the created migration file on app/Database/Migrations/ and paste these codes:

<?php
 
namespace App\Database\Migrations;
 
use CodeIgniter\Database\Migration;
 
class AddProject extends Migration
{
    public function up()
    {
        $this->forge->addField([
            'id' => [
'type' => 'BIGINT',
'constraint' => 255,
'unsigned' => true,
'auto_increment' => true
            ],
            'name' => [
'type' => 'VARCHAR',
'constraint' => '255',
            ],
            'description' => [
'type' => 'TEXT'
            ],
            'created_at' => [
'type' => 'TIMESTAMP',
'null' => true
            ],
            'updated_at' => [
'type' => 'TIMESTAMP',
'null' => true
            ],
            'deleted_at' => [
'type' => 'TIMESTAMP',
'null' => true
            ],
        ]);
        $this->forge->addPrimaryKey('id');
        $this->forge->createTable('projects');
    }
 
    public function down()
    {
        $this->forge->dropTable('projects');
    }
}

Run the migration by executing the migrate command:

php spark migrate

Step 5: Create Controller

We will be using the ResourceController in handling HTTP requests. ResourceController gives us convenience in building the RESTful API because it provides methods for each HTTP methods.

Execute this command on the Terminal or CMD to create a ResourceController :

php spark make:controller Project --restful

After executing the command, it will create a file located at app/Controllers/Project.php. Open the file and insert these codes:

app/Controllers/Project.php

<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourceController;

class Project extends ResourceController
{
	protected $modelName = 'App\Models\ProjectModel';
	protected $format    = 'json';

	/**
	 * Return an array of resource objects, themselves in array format
	 *
	 * @return mixed
	 */
	public function index()
	{
		return $this->respond($this->model->findAll(), 200);
	}

	/**
	 * Return the properties of a resource object
	 *
	 * @return mixed
	 */
	public function show($id = null)
	{
		$data = $this->model->find($id);
		if(is_null($data)) {
			return $this->fail(['error' => 'Project does not exist'], 404);
		}

		return $this->respond($data,200);
	}

	
	/**
	 * Create a new resource object, from "posted" parameters
	 *
	 * @return mixed
	 */
	public function create()
	{
		$data = [
            'name' => $this->request->getPost('name'),
            'description' => $this->request->getPost('description'),
        ];

		if ($this->model->insert($data) === false){
            
			$response = [
				'errors' => $this->model->errors(),
				'message' => 'Invalid Inputs'
			];

			return $this->fail($response , 409);
        }
		
		return $this->respond(['message' => 'Created Successfully'],201);
	}

	/**
	 * Add or update a model resource, from "posted" properties
	 *
	 * @return mixed
	 */
	public function update($id = null)
	{
		$data = [
            'name' => $this->request->getVar('name'),
            'description' => $this->request->getVar('description'),
        ];
 
        if ($this->model->where('id', $id)->set($data)->update() === false)
        {
			$response = [
				'errors' => $this->model->errors(),
				'message' => 'Invalid Inputs'
			];
            return $this->fail($response , 409);
        }
 
        return $this->respond(['message' => 'Updated Successfully'], 200);
	}

	/**
	 * Delete the designated resource object from the model
	 *
	 * @return mixed
	 */
	public function delete($id = null)
	{
		$this->model->delete($id);
		return $this->respond(['message' => 'Deleted Successfully'], 200);
	}
}

Step 6: Register Routes

Since we are using ResourceController we will use the resource() method. This method will create routes necessary for the ResourceController. You can check the documentation to see the routes it creates. Open the config file for routing located at app/Config/Routes.php and add this code:

app/Config/Routes.php

<?php

use CodeIgniter\Router\RouteCollection;

/**
 * @var RouteCollection $routes
 */
$routes->get('/', 'Home::index');
$routes->resource('projects', ['controller' => 'Project', 'only' => ['index', 'show', 'create', 'update', 'delete']]);

Step 7: Create View File

View files are web page or fragments of a page like header or footer and etc. Views can be embedded to other views whenever you need hierarchy on view files.

We will create out view files in this path app/Views. Create a file project_dashboard.php and add these codes:

app/Views/project_dashboard .php

<!DOCTYPE html>
<html lang="en">
<head>
    <title>CodeIgniter Project Manager</title>
    <meta charset="utf-8">
    <meta name="app-url" content="<?php echo base_url('/');?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    
</head>
<body>
  
    <div class="container">
        <h2 class="text-center mt-5 mb-3">CodeIgniter Project Manager</h2>
        <div class="card">
            <div class="card-header">
                <button class="btn btn-outline-primary" onclick="createProject()"> 
                    Create New Project
                </button>
            </div>
            <div class="card-body">
                <div id="alert-div">
                 
                </div>
                <table class="table table-bordered">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Description</th>
                            <th width="240px">Action</th>
                        </tr>
                    </thead>
                    <tbody id="projects-table-body">
                         
                    </tbody>
                     
                </table>
            </div>
        </div>
    </div>
  
    <!-- modal for creating and editing function -->
    <div class="modal" tabindex="-1"  id="form-modal">
        <div class="modal-dialog" >
            <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Project Form</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div id="error-div"></div>
                <form>
                    <input type="hidden" name="update_id" id="update_id">
                    <div class="form-group">
                        <label for="name">Name</label>
                        <input type="text" class="form-control" id="name" name="name">
                    </div>
                    <div class="form-group">
                        <label for="description">Description</label>
                        <textarea class="form-control" id="description" rows="3" name="description"></textarea>
                    </div>
                 
                    <button type="submit" class="btn btn-outline-primary mt-3" id="save-project-btn">Save Project</button>
                </form>
            </div>
            </div>
        </div>
    </div>
 
  
    <!-- view record modal -->
    <div class="modal" tabindex="-1" id="view-modal">
        <div class="modal-dialog" >
            <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Project Information</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <b>Name:</b>
                <p id="name-info"></p>
                <b>Description:</b>
                <p id="description-info"></p>
            </div>
            </div>
        </div>
    </div>
  
    <script type="text/javascript">
  
        showAllProjects();
     
        /*
            This function will get all the project records
        */
        function showAllProjects()
        {
            let url = $('meta[name=app-url]').attr("content") + "/projects";
            $.ajax({
                url: url,
                method: "GET",
                success: function(response) {
                    $("#projects-table-body").html("");
                    let projects = response;
                    for (var i = 0; i < projects.length; i++) 
                    {
                        let showBtn =  '<button ' +
                            ' class="btn btn-outline-info" ' +
                            ' onclick="showProject(' + projects[i].id + ')">Show' +
                        '</button> ';
                        let editBtn =  '<button ' +
                            ' class="btn btn-outline-success" ' +
                            ' onclick="editProject(' + projects[i].id + ')">Edit' +
                        '</button> ';
                        let deleteBtn =  '<button ' +
                            ' class="btn btn-outline-danger" ' +
                            ' onclick="destroyProject(' + projects[i].id + ')">Delete' +
                        '</button>';
     
                        let projectRow = '<tr>' +
                            '<td>' + projects[i].name + '</td>' +
                            '<td>' + projects[i].description + '</td>' +
                            '<td>' + showBtn + editBtn + deleteBtn + '</td>' +
                        '</tr>';
                        $("#projects-table-body").append(projectRow);
                    }
     
                     
                },
                error: function(response) {
                    console.log(response.responseJSON)
                }
            });
        }
     
        /*
            check if form submitted is for creating or updating
        */
        $("#save-project-btn").click(function(event ){
            event.preventDefault();
            if($("#update_id").val() == null || $("#update_id").val() == "")
            {
                storeProject();
            } else {
                updateProject();
            }
        })
     
        /*
            show modal for creating a record and 
            empty the values of form and remove existing alerts
        */
        function createProject()
        {
            $("#alert-div").html("");
            $("#error-div").html("");   
            $("#update_id").val("");
            $("#name").val("");
            $("#description").val("");
            $("#form-modal").modal('show'); 
        }
     
        /*
            submit the form and will be stored to the database
        */
        function storeProject()
        {   
            $("#save-project-btn").prop('disabled', true);
            let url = $('meta[name=app-url]').attr("content") + "/projects";
            let data = {
                name: $("#name").val(),
                description: $("#description").val(),
            };
            $.ajax({
                url: url,
                method: "POST",
                data: data,
                success: function(response) {
                    $("#save-project-btn").prop('disabled', false);
                    let successHtml = '<div class="alert alert-success" role="alert"><b>Project Created Successfully</b></div>';
                    $("#alert-div").html(successHtml);
                    $("#name").val("");
                    $("#description").val("");
                    showAllProjects();
                    $("#form-modal").modal('hide');
                },
                error: function(response) {
                    /*
                    show validation error
                    */
                    console.log(response)
                    $("#save-project-btn").prop('disabled', false);
                    if (typeof response.responseJSON.messages.errors !== 'undefined') 
                    {
                        let errors = response.responseJSON.messages.errors;
                        let descriptionValidation = "";
                        if (typeof errors.description !== 'undefined') 
                        {
                            descriptionValidation = '<li>' + errors.description + '</li>';
                        }
                        let nameValidation = "";
                        if (typeof errors.name !== 'undefined') 
                        {
                            nameValidation = '<li>' + errors.name + '</li>';
                        }
         
                        let errorHtml = '<div class="alert alert-danger" role="alert">' +
                            '<b>Validation Error!</b>' +
                            '<ul>' + nameValidation + descriptionValidation + '</ul>' +
                        '</div>';
                        $("#error-div").html(errorHtml);        
                    }
                }
            });
        }
     
     
        /*
            edit record function
            it will get the existing value and show the project form
        */
        function editProject(id)
        {
            let url = $('meta[name=app-url]').attr("content") + "/projects/" + id ;
            $.ajax({
                url: url,
                method: "GET",
                success: function(response) {
                    let project = response
                    $("#alert-div").html("");
                    $("#error-div").html("");   
                    $("#update_id").val(project.id);
                    $("#name").val(project.name);
                    $("#description").val(project.description);
                    $("#form-modal").modal('show'); 
                },
                error: function(response) {
                    console.log(response.responseJSON)
                }
            });
        }
     
        /*
            sumbit the form and will update a record
        */
        function updateProject()
        {
            $("#save-project-btn").prop('disabled', true);
            let url = $('meta[name=app-url]').attr("content") + "/projects/" + $("#update_id").val();
            let data = {
                id: $("#update_id").val(),
                name: $("#name").val(),
                description: $("#description").val(),
            };
            $.ajax({
                url: url,
                method: "PUT",
                data: JSON.stringify(data),
                contentType: 'application/json',
                success: function(response) {
                    $("#save-project-btn").prop('disabled', false);
                    let successHtml = '<div class="alert alert-success" role="alert"><b>Project Updated Successfully</b></div>';
                    $("#alert-div").html(successHtml);
                    $("#name").val("");
                    $("#description").val("");
                    showAllProjects();
                    $("#form-modal").modal('hide');
                },
                error: function(response) {
                    /*
                    show validation error
                    */
                    console.log(response)
                    $("#save-project-btn").prop('disabled', false);
                    if (typeof response.responseJSON.messages.errors !== 'undefined') 
                    {
                        let errors = response.responseJSON.messages.errors;
                        let descriptionValidation = "";
                        if (typeof errors.description !== 'undefined') 
                        {
                            descriptionValidation = '<li>' + errors.description + '</li>';
                        }
                        let nameValidation = "";
                        if (typeof errors.name !== 'undefined') 
                        {
                            nameValidation = '<li>' + errors.name + '</li>';
                        }
         
                        let errorHtml = '<div class="alert alert-danger" role="alert">' +
                            '<b>Validation Error!</b>' +
                            '<ul>' + nameValidation + descriptionValidation + '</ul>' +
                        '</div>';
                        $("#error-div").html(errorHtml);        
                    }
                }
            });
        }
     
        /*
            get and display the record info on modal
        */
        function showProject(id)
        {
            $("#name-info").html("");
            $("#description-info").html("");
            let url = $('meta[name=app-url]').attr("content") + "/projects/" + id +"";
            $.ajax({
                url: url,
                method: "GET",
                success: function(response) {
                    let project = response
                    $("#name-info").html(project.name);
                    $("#description-info").html(project.description);
                    $("#view-modal").modal('show'); 
     
                },
                error: function(response) {
                    console.log(response.responseJSON)
                }
            });
        }
     
        /*
            delete record function
        */
        function destroyProject(id)
        {
            let url = $('meta[name=app-url]').attr("content") + "/projects/" + id;
            $.ajax({
                url: url,
                method: "DELETE",
                success: function(response) {
                    let successHtml = '<div class="alert alert-success" role="alert"><b>Project Deleted Successfully</b></div>';
                    $("#alert-div").html(successHtml);
                    showAllProjects();
                },
                error: function(response) {
                    console.log(response.responseJSON)
                }
            });
        }
     
    </script>
</body>
</html>

After creating the view file, updated the index() method of Home controller.

app/Controllers/Home.php

<?php

namespace App\Controllers;

class Home extends BaseController
{
    public function index(): string
    {
        return view('project_dashboard');
    }
}

Step 8: Run the Application

Now that we have completed the steps above we will now run the app. To run the app, execute this command:

php spark serve

Open this URL in your browser and now you can test the CodeIgniter CRUD app:

http://localhost:8080

Result:

CodeIgniter 4 AJAX CRUD(index page):

ci-crud-ajax-index-page

CodeIgniter 4 AJAX CRUD (project create form modal):

ci-crud-ajax-create-modal

CodeIgniter 4 AJAX CRUD(edit project form modal):

ci-crud-ajax-edit-modal

CodeIgniter 4 AJAX CRUD (show project information modal):

ci-crud-ajax-show-modal