Complete CRUD CodeIgniter 4 and Vue JS Tutorial (Full Stack)

Complete CRUD CodeIgniter 4 and Vue JS Tutorial (Full Stack)

In this tutorial you will learn how to create a Create-Read-Update-Delete (CRUD) application using CodeIgniter 4 on the backend and Vue JS on the frontend.

Not only that,

I will also share with you how to use Bulma CSS to style the User Interface (UI).

Thus, the application that is built becomes more elegant, responsive, and user friendly.

Let's get started.

Step #1. CodeIgniter 4 Installation

To install CodeIgniter 4 can be done in 2 ways, namely: Manual installation and Installation using composer.

In this tutorial, I will use composer.

If you don't have composer, please visit the following URL to download composer, then install it on your computer:

https://getcomposer.org/

After composer is installed on your computer and to make sure composer is installed properly on your computer, open a terminal or Command Prompt (CMD).

Then type the following command:

composer -v

Like the following picture:

composer

Once composer is properly installed on your computer, then you can create a CodeIgniter 4 project using composer.

Create a folder on your web server, here I name it "fullstack".

If you are using WAMPSERVER, create it in folder:

C:/wamp64/www

If you are using XAMPP, create it in the folder:

C:/xampp/htdocs

In this tutorial, I'm using XAMPP.

Then open the "fullstack" folder using the Code Editor, here I am using Visual Studio Code.

After that, integrate with the terminal on Visual Studio Code.

Then type the following command in the terminal to create a CodeIgniter 4 project:

composer create-project codeigniter4/appstarter backend

Wait until the installation process is complete.

After the installation is completed, go to the "backend" folder by typing the following command in the terminal:

cd backend

Then type the following command in the terminal to run the project:

php spark serve

If it goes well, it will looks like the following picture:

php spark serve

 

Step #2. Create a Database and Table

Create a new database in MySQL, you can use tools such as SQLyog, PHPMyAdmin or similar tools.

Here I create a database with the name "fullstack_db".

If you create a database with the same name it's even better.

To create a database in MySQL, it can be done by executing the following query:

CREATE DATABASE fullstack_db;

The SQL command above will create a database with the name "fullstack_db".

Next, create a connection between the database and the CodeIgniter project.

Find the env file in the project root, then rename it to .env and open the file.

Then find the following code:

# database.default.hostname = localhost
# database.default.database = ci4
# database.default.username = root
# database.default.password = root
# database.default.DBDriver = MySQLi

Change it to like the following:

database.default.hostname = localhost
database.default.database = fullstack_db
database.default.username = root
database.default.password = 
database.default.DBDriver = MySQLi

Next, create a table on the database "fullstack_db".

In this tutorial, I will use the migration feature in CodeIgniter 4 to create a table.

Type the following command in Terminal:

php spark make:migration Products

Then CodeIgniter will create a file with the initials "Products" in the "app/Database/Migrations" folder.

Like the following picture:

migrations

Open the file, then type the following code:

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class Products extends Migration
{
    public function up()
    {
        $this->forge->addField([
            'id' => [
                'type' => 'INT',
                'constraint' => 5,
                'auto_increment' => true
            ],
            'title' => [
                'type' => 'VARCHAR',
                'constraint' => 200,
            ],
            'price' => [
                'type' => 'INT',
                'constraint' => 11,
            ]
        ]);
        $this->forge->addKey('id', true);
        $this->forge->createTable('products');
    }

    public function down()
    {
        //
    }
}

In the code above, we only add code to the function up().

Function up, serves to create a table in the database with the specified fields.

In the above case, we create a table with the name "products" with fields id, title, and price along with the data type and other attributes.

If you're not familiar with migrations, it may look complicated, but it's really not.

Next, type the following command at the Terminal/Command Prompt:

php spark migrate

Press Enter, then CodeIgniter will automatically create a "products" table with fields id, title, and price in the database "fullstack_db".

 

Step #3. Models

In this tutorial, only one model file is needed which is “ProductModel.php”.

Create a model file named "ProductModel.php" in the "app/Models" folder, then type the following code:

<?php

namespace App\Models;

use CodeIgniter\Model;

class ProductModel extends Model
{
    protected $DBGroup              = 'default';
    protected $table                = 'products';
    protected $primaryKey           = 'id';
    protected $useAutoIncrement     = true;
    protected $insertID             = 0;
    protected $returnType           = 'array';
    protected $useSoftDeletes       = false;
    protected $protectFields        = true;
    protected $allowedFields        = ['title','price'];

    // Dates
    protected $useTimestamps        = false;
    protected $dateFormat           = 'datetime';
    protected $createdField         = 'created_at';
    protected $updatedField         = 'updated_at';
    protected $deletedField         = 'deleted_at';

    // Validation
    protected $validationRules      = [];
    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          = [];
}

 

Step #4. Controllers

Create a controller file named “Product.php” in the “app/Controllers” folder, then type the following code:

<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourceController;
use CodeIgniter\API\ResponseTrait;
use App\Models\ProductModel;

class Product extends ResourceController
{
    /**
     * Return an array of resource objects, themselves in array format
     *
     * @return mixed
     */
    use ResponseTrait;
    public function index()
    {
        $model = new ProductModel();
        $data = $model->findAll();
        if(!$data) return $this->failNotFound('No Data Found');
        return $this->respond($data);
    }

    /**
     * Return the properties of a resource object
     *
     * @return mixed
     */
    public function show($id = null)
    {
        $model = new ProductModel();
        $data = $model->find(['id' => $id]);
        if(!$data) return $this->failNotFound('No Data Found');
        return $this->respond($data[0]);
    }

    /**
     * Create a new resource object, from "posted" parameters
     *
     * @return mixed
     */
    public function create()
    {
        $json = $this->request->getJSON();
        $data = [
            'title' => $json->title,
            'price' => $json->price
        ];
        $model = new ProductModel();
        $product = $model->insert($data);
        if(!$product) return $this->fail('Failed Saved', 400);
        return $this->respondCreated($product);
    }

    /**
     * Add or update a model resource, from "posted" properties
     *
     * @return mixed
     */
    public function update($id = null)
    {
        $json = $this->request->getJSON();
        $data = [
            'title' => $json->title,
            'price' => $json->price
        ];
        $model = new ProductModel();
        $findById = $model->find(['id' => $id]);
        if(!$findById) return $this->fail('No Data Found', 404);
        $product = $model->update($id, $data);
        if(!$product) return $this->fail('Update failed', 400);
        return $this->respond($product);
    }

    /**
     * Delete the designated resource object from the model
     *
     * @return mixed
     */
    public function delete($id = null)
    {
        $model = new ProductModel();
        $findById = $model->find(['id' => $id]);
        if(!$findById) return $this->fail('No Data Found', 404);
        $product = $model->delete($id);
        if(!$product) return $this->fail('Failed Deleted', 400);
        return $this->respondDeleted('Deleted Successful');
    }
}

 

Step #5. Configure Routes.php

Do a little configuration on the “Routes.php” file located in the "app/Config" folder.

Open the “Routes.php” file in the “app/Config” folder, then find the following code:

$routes->get('/', 'Home::index');

Then, change to the following:

$routes->resource('products');

 

Step #6. CORS (Cross-Origin Resources Sharing)

This is important!

In order for resources to be accessible outside the domain, we need to allow CORS.

To allow CORS, create a file named “Cors.php” in the “app/Filters” folder.

Then type the following code:

<?php

namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;

class Cors implements FilterInterface
{
    public function before(RequestInterface $request, $arguments = null)
    {
        header("Access-Control-Allow-Origin: *");
        header("Access-Control-Allow-Headers: X-API-KEY, Origin,X-Requested-With, Content-Type, Accept, Access-Control-Requested-Method, Authorization");
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
        $method = $_SERVER['REQUEST_METHOD'];
        if($method == "OPTIONS"){
            die();
        }
    }
    
    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        //
    }
}

Then open the "Filters.php" file located in the "app/Config" folder.

Then find the following code:

public $aliases = [
        'csrf'     => CSRF::class,
        'toolbar'  => DebugToolbar::class,
        'honeypot' => Honeypot::class
    ];

Then add a “cors” filter like the following:

public $aliases = [
        'csrf'     => CSRF::class,
        'toolbar'  => DebugToolbar::class,
        'honeypot' => Honeypot::class,
        'cors'     => AppFiltersCors::class,
    ];

Next define "cors" in public $globals as follows:

public $globals = [
        'before' => [
            'cors'
            //'honeypot'
            // 'csrf',
        ],
        'after'  => [
            'toolbar',
            //'honeypot'
        ],
    ];

 

Step #7. Vue CLI (Frontend)

In this tutorial, I will be using the Vue CLI (Command Line Interface) for the frontend.

You can install Vue CLI using NPM (Node Package Manager) or using YARN.

In this tutorial, I will be using NPM.

To be able to use NPM, you only need to install Node.js.

Please download Node.js from the following link and install it on your computer:

https://nodejs.org

To make sure Node.js is installed on your computer, type the following command in CMD (Command Prompt) or Terminal:

node –v

Then, make sure NPM is also installed properly by typing the following command in CMD (Command Prompt) or Terminal:

npm –v

Look at the following picture for more details:

node npm

Then install Vue CLI by typing the following command in CMD or Terminal:

npm install –g @vue/cli

The above command will install Vue CLI globally on your computer.

After you have installed Vue CLI, and to make sure Vue CLI is installed properly on your computer, type the following command in CMD or Terminal:

vue --version

Like the following picture:

vue version

Next open a new terminal in VS Code, and type the following command to create a Vue JS project:

vue create frontend

Make sure you are in the "fullstack" folder, as shown below:

vue project

Then select => “Manually select features”.

Like the following picture:

features

Then, select “Choose Vue version, Babel, and Router” then press Enter.

Like the following picture:

vue options

Then select Vue version 3.x, then press Enter so on.

Then Vue CLI will create a new project for you.

If the installation is completed, then there are "backend" and "frontend" folders in the "fullstack" folder.

Like the following picture:

project folder

The “backend” folder is the project we created earlier using CodeIgniter 4, while the “frontend” is the project we created using the Vue CLI.

Then, go to the “frontend” folder by typing the following command in the terminal:

cd frontend

Next, install the dependencies we need by typing the following command in the terminal:

npm install axios bulma

The above command will install axios and bulma css.

Axios makes it easier for us to interact with the API, while Bulma CSS will make it easier for us to style a user interface (UI).

To make sure our Vue JS project runs properly, type the following command in the terminal:

npm run serve

Like the following picture:

run serve

In the picture above, it can be seen that the frontend is running on port: 8081, while our previous backend is running on port: 8080.

 

Step #8. Show and Delete Data (READ & DELETE)

Open the "App.vue" file in the "frontend/src" folder, then change it to the following:

<template>
  <div class="container is-max-desktop">
    <router-view />
  </div>
</template>

<style>
@import "~bulma/css/bulma.css";
</style>

After that, open the "main.js" file located in the "frontend/src" folder, then change it to the following:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// import axios
import axios from 'axios'
// define baseURL
axios.defaults.baseURL = 'http://localhost:8080/'

createApp(App).use(router).mount('#app')

Next, create a components file named “Product.vue” in the “frontend/src/components” folder.

Then type the following code:

<template>
  <div>
    <h1 class="title">Product List</h1>
    <router-link :to="{ name: 'AddProduct' }" class="button is-primary"
      >Add New</router-link
    >
    <table class="table is-striped is-fullwidth">
      <thead>
        <tr>
          <th>Title</th>
          <th>Price</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="product in products" :key="product.id">
          <td>{{ product.title }}</td>
          <td>{{ product.price }}</td>
          <td>
            <router-link
              :to="{ name: 'EditProduct', params: { id: product.id } }"
              class="button is-info is-small"
              >Edit</router-link
            >
            <button
              class="button is-danger is-small"
              @click="deleteProduct(product.id)"
            >
              Delete
            </button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "Product",
  data() {
    return {
      products: [],
    };
  },
  created() {
    this.getProducts();
  },
  methods: {
    async getProducts() {
      try {
        const response = await axios.get("product");
        this.products = response.data;
      } catch (error) {
        console.log(error);
      }
    },
    async deleteProduct(id) {
      try {
        await axios.delete(`product/${id}`);
        this.getProducts();
      } catch (error) {
        console.log(error);
      }
    },
  },
};
</script>

<style>
</style>

Then create another file named "ProductList.vue" in the "frontend/src/views" folder, then type the following code:

<template>
  <Product />
</template>

<script>
import Product from "@/components/Product.vue";
export default {
  name: "ProductList",
  components: {
    Product,
  },
};
</script>

<style>
</style>

After that, open the “index.js” file located in the “frontend/src/router” folder, then change it to be as follows:

import { createRouter, createWebHistory } from 'vue-router'
import ProductList from '../views/ProductList.vue'

const routes = [
  {
    path: '/',
    name: 'ProductList',
    component: ProductList
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

To make sure everything goes well, go back to the browser and visit the following URL:

http://localhost:8081/

If it goes well, it will look like the following image:

products

If you click one of the "Delete" buttons, then one of the data will be deleted.

 

Step #9. POST Data (CREATE)

Create a components file named “AddFrom.vue” in the “frontend/src/components” folder.

Then type the following code:

<template>
  <div>
    <h1 class="title">Add New Product</h1>
    <form @submit.prevent="saveProduct">
      <div class="field">
        <label class="label">Title</label>
        <div class="control">
          <input
            type="text"
            v-model="title"
            class="input"
            placeholder="Title"
          />
        </div>
      </div>
      <div class="field">
        <label class="label">Price</label>
        <div class="control">
          <input
            type="text"
            v-model="price"
            class="input"
            placeholder="Price"
          />
        </div>
      </div>
      <div class="field">
        <div class="control">
          <button class="button is-primary">Save</button>
        </div>
      </div>
    </form>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "AddForm",
  data() {
    return {
      title: "",
      price: "",
    };
  },
  methods: {
    async saveProduct() {
      try {
        await axios.post("product", {
          title: this.title,
          price: this.price,
        });
        (this.title = ""), (this.price = ""), this.$router.push("/");
      } catch (error) {
        console.log(error);
      }
    },
  },
};
</script>

<style>
</style>

Then create another file named "AddProduct.vue" in the "frontend/src/views" folder, then type the following code:

<template>
  <AddForm />
</template>

<script>
import AddForm from "@/components/AddForm.vue";
export default {
  name: "AddProduct",
  components: {
    AddForm,
  },
};
</script>

<style>
</style>

After that, open the “index.js” file located in the “frontend/src/router” folder, then change it to be as follows:

import { createRouter, createWebHistory } from 'vue-router'
import ProductList from '../views/ProductList.vue'
import AddProduct from '../views/AddProduct.vue'

const routes = [
  {
    path: '/',
    name: 'ProductList',
    component: ProductList
  },
  {
    path: '/add',
    name: 'AddProduct',
    component: AddProduct
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

Return to the browser, then click the “Add New” button or visit the following URL:

http://localhost:8081/add

If it goes well, the form will appear as shown below:

add product

Enter “Title” and “Price”, then click the “Save” button.

If it goes well, it will show one additional data as shown below:

product added

 

Step #10. UPDATE Data (UPDATE)

Create a components file named “EditFrom.vue” in the “frontend/src/components” folder.

Then type the following code:

<template>
  <div>
    <h1 class="title">Update Product</h1>
    <form @submit.prevent="updateProduct">
      <div class="field">
        <label class="label">Title</label>
        <div class="control">
          <input
            type="text"
            v-model="title"
            class="input"
            placeholder="Title"
          />
        </div>
      </div>
      <div class="field">
        <label class="label">Price</label>
        <div class="control">
          <input
            type="text"
            v-model="price"
            class="input"
            placeholder="Price"
          />
        </div>
      </div>
      <div class="field">
        <div class="control">
          <button class="button is-primary">Update</button>
        </div>
      </div>
    </form>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "EditForm",
  data() {
    return {
      title: "",
      price: "",
    };
  },
  created() {
    this.getProductById();
  },
  methods: {
    async getProductById() {
      try {
        const response = await axios.get(`product/${this.$route.params.id}`);
        (this.title = response.data.title), (this.price = response.data.price);
      } catch (error) {
        console.log(error);
      }
    },
    async updateProduct() {
      try {
        await axios.put(`product/${this.$route.params.id}`, {
          title: this.title,
          price: this.price,
        });
        (this.title = ""), (this.price = ""), this.$router.push("/");
      } catch (error) {
        console.log(error);
      }
    },
  },
};
</script>

<style>
</style>

Then create a view file named "EditProduct.vue" in the "frontend/src/views" folder, then type the following code:

<template>
  <EditForm />
</template>

<script>
import EditForm from "@/components/EditForm.vue";
export default {
  name: "EditProduct",
  components: {
    EditForm,
  },
};
</script>

<style>
</style>

After that, open the “index.js” file located in the “frontend/src/router” folder, then change it to be as follows:

import { createRouter, createWebHistory } from 'vue-router'
import ProductList from '../views/ProductList.vue'
import AddProduct from '../views/AddProduct.vue'
import EditProduct from '../views/EditProduct.vue'

const routes = [
  {
    path: '/',
    name: 'ProductList',
    component: ProductList
  },
  {
    path: '/add',
    name: 'AddProduct',
    component: AddProduct
  },
  {
    path: '/edit/:id',
    name: 'EditProduct',
    component: EditProduct
  },
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

Return to the browser, then click one of the "Edit" buttons, an edit form will appear as shown below:

edit form

Change the “Title” and “Price”, then click the “Update” button.

If it goes well, the data changes will look like the following picture:

product updated

 

Conclusion:

The discussion this time is about how to create a full stack application using CodeIgniter 4 and Vue JS.

Not only that, you have also learned how to use bulma css to create a user interface (UI).

So what are you waiting for, let's coding!

Download Source Code

Share:



Sponsorship:


Recommended for you


Comments (0)

Leave a Comment