CodeIgniter 4 CRUD App Easy Tutorial

CodeIgniter 4 CRUD App Easy Tutorial

Avatar photoPosted by

One of fundamental operation in an application are the CRUD operations. CRUD stands for Create, Read, Update, Delete. So today I will show you how to develop a CRUD application using CodeIgniter 4.

CodeIgniter is one of the most powerful PHP frameworks. It is an open-source web framework that is used for rapid web development. It is popular for its exceptional performance, small footprint, requires nearly zero configuration, thorough documentation and many more.

If you want to learn more about CodeIgniter, you can check and read their documentation.

Now we proceed on showing you how to develop a CRUD app in CodeIgniter 4:

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

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',
		'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 ResourcePresenter in handling HTTP requests. ResourcePresenter gives as a convenience on providing views to our resources.

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

php spark make:controller Project --restful presenter

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

<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourcePresenter;

class Project extends ResourcePresenter
{

	protected $modelName = 'App\Models\ProjectModel';

	/**
	 * Present a view of resource objects
	 *
	 * @return mixed
	 */
	public function index()
	{
		return view('projects/index', ['projects' => $this->model->orderBy('created_at', 'asc')->findAll()]);
	}

	/**
	 * Present a view to present a specific resource object
	 *
	 * @param mixed $id
	 *
	 * @return mixed
	 */
	public function show($id = null)
	{
		return view('projects/show', ['project' => $this->model->find($id)]);
	}

	/**
	 * Present a view to present a new single resource object
	 *
	 * @return mixed
	 */
	public function new()
	{
		return view('projects/create');
	}

	/**
	 * Process the creation/insertion of a new resource object.
	 * This should be a POST.
	 *
	 * @return mixed
	 */
	public function create()
	{

		$data = [
			'name' => $this->request->getPost('name'),
			'description'    => $this->request->getPost('description'),
		];
		
		if ($this->model->insert($data) === false)
		{
			return redirect()->back()->withInput()->with('errors', $this->model->errors());
		}

		return redirect()->back()->with('success', 'Saved Successfully!');
	}

	/**
	 * Present a view to edit the properties of a specific resource object
	 *
	 * @param mixed $id
	 *
	 * @return mixed
	 */
	public function edit($id = null)
	{
		return view('projects/edit', ['project' => $this->model->find($id)]);
	}

	/**
	 * Process the updating, full or partial, of a specific resource object.
	 * This should be a POST.
	 *
	 * @param mixed $id
	 *
	 * @return mixed
	 */
	public function update($id = null)
	{

		$data = [
			'name' => $this->request->getPost('name'),
			'description'    => $this->request->getPost('description'),
		];

		if ($this->model->where('id', $id)->set($data)->update() === false)
		{
			return redirect()->back()->withInput()->with('errors', $this->model->errors());
		}

		return redirect()->back()->with('success', 'Updated Successfully!');
	}

	/**
	 * Present a view to confirm the deletion of a specific resource object
	 *
	 * @param mixed $id
	 *
	 * @return mixed
	 */
	public function remove($id = null)
	{
		//
	}

	/**
	 * Process the deletion of a specific resource object
	 *
	 * @param mixed $id
	 *
	 * @return mixed
	 */
	public function delete($id = null)
	{
		$this->model->delete($id);
		return redirect()->back()->with('success', 'Deleted Successfully!');
	}
}

Step 6: Register Routes

Since we are using ResourcePresenter we will use the presenter() method. This method will create routes necessary for the ResoucePresenter. 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:

<?php

namespace Config;

// Create a new instance of our RouteCollection class.
$routes = Services::routes();

// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (file_exists(SYSTEMPATH . 'Config/Routes.php'))
{
	require SYSTEMPATH . 'Config/Routes.php';
}

/**
 * --------------------------------------------------------------------
 * Router Setup
 * --------------------------------------------------------------------
 */
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('Home');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
$routes->setAutoRoute(true);

/*
 * --------------------------------------------------------------------
 * Route Definitions
 * --------------------------------------------------------------------
 */

// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');


$routes->presenter('projects', ['controller' => 'Project']);

/*
 * --------------------------------------------------------------------
 * Additional Routing
 * --------------------------------------------------------------------
 *
 * There will often be times that you need additional routing and you
 * need it to be able to override any defaults in this file. Environment
 * based routes is one such time. require() additional route files here
 * to make that happen.
 *
 * You will have access to the $routes object within that file without
 * needing to reload it.
 */
if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php'))
{
	require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}

Step 7: Create View Files

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. First, create a folder named projects and inside this folder create these files:

  • layout.php
  • index.php
  • show.php
  • create.php
  • edit.php

After creating the view files, Insert the code below:

app/Views/projects/layout.php

<!DOCTYPE html>
<html>
<head>
    <title>CodeIgniter Project Manager</title>
    <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>
      <?= $this->renderSection("content"); ?>
   <body>
</html>

app/Views/projects/index.php

<?=$this->extend("projects/layout")?>

<?=$this->section("content")?>

<div class="container">
    <h2 class="text-center mt-5 mb-3">CodeIgniter Project Manager</h2>
    <div class="card">
        <div class="card-header">
            <a class="btn btn-outline-primary" href="<?php echo base_url('/projects/new'); ?>"> 
                Create New Project 
            </a>
        </div>
        <div class="card-body">
                
            <?php if (session()->getFlashdata('success')) : ?>
                <div class="alert alert-success">
                    <b><?php echo session()->getFlashdata('success') ?></b>
                </div>
            <?php endif ?>

            <table class="table table-bordered">
                <tr>
                    <th>Name</th>
                    <th>Description</th>
                    <th width="240px">Action</th>
                </tr>
                <?php foreach ($projects as $project):?>
                <tr>
                    <td><?= $project->name ?></td>
                    <td><?= $project->description ?></td>
                    <td>
                        <form action="<?php echo base_url('/projects/delete/' . $project->id); ?>" method="post">
                            <a
                                class="btn btn-outline-info"
                                href="<?php echo base_url('/projects/show/' . $project->id); ?>">
                                Show
                            </a>
                            <a
                                class="btn btn-outline-success"
                                href="<?php echo base_url('/projects/edit/' . $project->id); ?>">
                                Edit
                            </a>
                            <button type="submit" class="btn btn-outline-danger">Delete</button>
                        </form>
                    </td>
                </tr>
                <?php endforeach;?>
            </table>
        </div>
    </div>
</div>

<?=$this->endSection()?>

app/Views/projects/show.php

<?=$this->extend("projects/layout")?>

<?=$this->section("content")?>
<div class="container">
    <h2 class="text-center mt-5 mb-3">Show Project</h2>
    <div class="card">
        <div class="card-header">
            <a class="btn btn-outline-info float-right" href="<?php echo base_url('/projects');?>"> 
                View All Projects
            </a>
        </div>
        <div class="card-body">
        <b class="text-muted">Name:</b>
        <p><?php echo $project->name;?></p>
        <b class="text-muted">Description:</b>
        <p><?php echo $project->description;?></p>
        </div>
    </div>
</div>

<?=$this->endSection()?>

app/Views/projects/create.php

<?=$this->extend("projects/layout")?>

<?=$this->section("content")?>
<div class="container">
    <h2 class="text-center mt-5 mb-3">Create New Project</h2>
    <div class="card">
        <div class="card-header">
            <a class="btn btn-outline-info float-right" href="<?php echo base_url('/projects'); ?>"> 
                View All Projects
            </a>
        </div>
        <div class="card-body">
            <!-- flashdata for success -->
            <?php if (session()->getFlashdata('success')) : ?>
                <div class="alert alert-success">
                    <b><?php echo session()->getFlashdata('success') ?></b>
                </div>
            <?php endif ?>
            <!-- flashdata for errors -->
            <?php if (session()->getFlashdata('errors')) : ?>
                <div class="alert alert-danger">
                    <ul>
                    <?php foreach (session()->getFlashdata('errors')  as $field => $error) : ?>
                        <li><?= $error ?></li>
                    <?php endforeach ?>
                    </ul>
                </div>
            <?php endif ?>

            <form action="<?php echo base_url('/projects/create'); ?>" method="post">
                <div class="form-group">
                    <label for="name">Name</label>
                    <input 
                        type="text" 
                        class="form-control" 
                        id="name" 
                        name="name" 
                        value="<?= old('name') ?>">
                </div>
                <div class="form-group">
                    <label for="description">Description</label>
                    <textarea 
                        class="form-control" 
                        id="description" 
                        rows="3"
                        name="description"><?= old('description') ?></textarea>
                </div>
                
                <button type="submit" class="btn btn-outline-primary mt-3">Save Project</button>
            </form>
            
        </div>
    </div>
</div>

<?=$this->endSection()?>

app/Views/projects/edit.php

<?=$this->extend("projects/layout")?>

<?=$this->section("content")?>
<div class="container">
    <h2 class="text-center mt-5 mb-3">Edit Project</h2>
    <div class="card">
        <div class="card-header">
            <a class="btn btn-outline-info float-right" href="<?php echo base_url('/projects');?>"> 
                View All Projects
            </a>
        </div>
        <div class="card-body">
           <!-- flashdata for success -->
           <?php if (session()->getFlashdata('success')) : ?>
                <div class="alert alert-success">
                    <b><?php echo session()->getFlashdata('success') ?></b>
                </div>
            <?php endif ?>
            <!-- flashdata for errors -->
            <?php if (session()->getFlashdata('errors')) : ?>
                <div class="alert alert-danger">
                    <ul>
                    <?php foreach (session()->getFlashdata('errors')  as $field => $error) : ?>
                        <li><?= $error ?></li>
                    <?php endforeach ?>
                    </ul>
                </div>
            <?php endif ?>
    
            <form action="<?php echo base_url('/projects/update/' . $project->id);?>" method="POST">
                <div class="form-group">
                    <label for="name">Name</label>
                    <input
                        type="text"
                        class="form-control"
                        id="name"
                        name="name"
                        value="<?php echo $project->name;?>">
                </div>
                <div class="form-group">
                    <label for="description">Description</label>
                    <textarea
                        class="form-control"
                        id="description"
                        rows="3"
                        name="description"><?php echo $project->description;?></textarea>
                </div>
            
                <button type="submit" class="btn btn-outline-success mt-3">Update Project</button>
            </form>
        
        </div>
    </div>
</div>

<?=$this->endSection()?>

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/projects

Results:

Index Page

ci-crud-index-page

Create Page

ci-crud-create-page

Edit Page

ci-crud-edit-page

Show Page

ci-crud-show-page