bertugfahriozer/ci4shoppingcart

Codeigniter 4 Shopping Cart

Maintainers

Package info

github.com/bertugfahriozer/ci4shoppingcart

Forum

pkg:composer/bertugfahriozer/ci4shoppingcart

Transparency log

Statistics

Installs: 146

Dependents: 0

Suggesters: 0

Stars: 5

Open Issues: 0

2.0.0 2026-08-27 13:59 UTC

This package is auto-updated.

Last update: 2026-08-27 14:01:35 UTC


README

πŸ›’ CodeIgniter 4 Shopping Cart

CodeIgniter 3's Cart class ported to CodeIgniter 4 β€” session-backed, dependency-free, drop-in.

Packagist Version Total Downloads PHP Version License

English Β· TΓΌrkΓ§e

The Cart class lets you add items to a session that stays active while a user browses your site. Those items can then be retrieved and displayed in a standard "shopping cart" format, allowing the user to update quantities or remove items.

Important

The Cart class provides only the core cart functionality. It does not provide shipping, credit card authorization, or other order-processing components.

πŸ“‘ Table of contents

πŸ”§ Requirements

Requirement Version Note
PHP ^8.2
CodeIgniter 4.x The session() helper must be available
Session driver database recommended See below

Warning

The Cart class stores its contents using CodeIgniter's Session class. Before using the library, create the session table described in the Session documentation and set your session preferences in .env to use the database.

πŸ“¦ Installation

composer require bertugfahriozer/ci4shoppingcart

The package ships only the library itself β€” no tests, tooling or sample code end up in your vendor/ directory. A complete working example lives in the repository under example/; copy it into your own project and adapt it however you like.

πŸš€ Quick start

<?php

namespace App\Controllers;

use ci4shoppingcart\Libraries\Cart;

class Product extends BaseController
{
    protected Cart $cart;

    public function __construct()
    {
        $this->cart = new Cart();
    }

    public function add()
    {
        $this->cart->insert([
            'id'    => 'sku_123ABC',
            'qty'   => 1,
            'price' => 39.95,
            'name'  => 'T-Shirt',
        ]);

        return redirect()->to('/cart');
    }
}

The Cart class loads and initialises the Session class for you. Unless you use sessions elsewhere in your application, you do not need to load Session separately.

πŸ“– Usage

Adding an item to the cart

Pass an array containing the product information to insert():

$data = [
    'id'      => 'sku_123ABC',
    'qty'     => 1,
    'price'   => 39.95,
    'name'    => 'T-Shirt',
    'options' => ['Size' => 'L', 'Color' => 'Red'],
];

$rowid = $this->cart->insert($data);

The first four keys are required. If any of them is missing the data is not saved to the cart and the method returns FALSE.

Key Required Type Description
id βœ… string A unique identifier for each product in your store β€” typically an SKU.
qty βœ… int|float The quantity being purchased. A value of 0 inserts nothing.
price βœ… int|float The unit price of the item.
name βœ… string The name of the item.
options ❌ array Additional attributes that identify the product (size, colour…). Included in the Row ID calculation.

Caution

rowid and subtotal are reserved keys used internally by the Cart class. Do not use those names for your own data when inserting products.

Extra fields

Your array may contain any additional data you like; all of it is stored in the session. Standardising field names across products makes displaying the cart in a table much easier.

$data = [
    'id'     => 'sku_123ABC',
    'qty'    => 1,
    'price'  => 39.95,
    'name'   => 'T-Shirt',
    'coupon' => 'XMAS-50OFF',
];

$this->cart->insert($data);

Adding multiple items to the cart

Passing a multi-dimensional array adds several products in one call. This is useful when you let people pick from among several items on the same page.

$data = [
    [
        'id'      => 'sku_123ABC',
        'qty'     => 1,
        'price'   => 39.95,
        'name'    => 'T-Shirt',
        'options' => ['Size' => 'L', 'Color' => 'Red'],
    ],
    [
        'id'    => 'sku_567ZYX',
        'qty'   => 1,
        'price' => 9.95,
        'name'  => 'Coffee Mug',
    ],
    [
        'id'    => 'sku_965QRS',
        'qty'   => 1,
        'price' => 29.95,
        'name'  => 'Shot Glass',
    ],
];

$this->cart->insert($data);

Note

The return value comes in two shapes: inserting a single item gives you that item's rowid (a string), while a multi-dimensional array returns TRUE if at least one item was inserted. If nothing could be inserted, both cases return FALSE. See Behaviour worth knowing for details.

Displaying the cart

Pass the cart object to your view:

public function yourMethod()
{
    return view('path/view', ['cart' => $this->cart]);
}

Then loop over it inside the view:

<form action="<?= route_to('yourRoute') ?>" method="post">
  <table>
    <tr>
      <th>QTY</th>
      <th>Item description</th>
      <th style="text-align:right">Item price</th>
      <th style="text-align:right">Sub-total</th>
    </tr>

    <?php foreach ($cart->contents() as $items): ?>
      <tr>
        <td>
          <input type="hidden" name="rowid[]" value="<?= $items['rowid'] ?>">
          <input type="number" name="qty[]" value="<?= $items['qty'] ?>">
        </td>
        <td>
          <?= esc($items['name']) ?>

          <?php if ($cart->has_options($items['rowid'])): ?>
            <p>
              <?php foreach ($cart->product_options($items['rowid']) as $name => $value): ?>
                <strong><?= esc($name) ?>:</strong> <?= esc($value) ?><br>
              <?php endforeach; ?>
            </p>
          <?php endif; ?>
        </td>
        <td style="text-align:right">$<?= $cart->format_number($items['price']) ?></td>
        <td style="text-align:right">$<?= $cart->format_number($items['subtotal']) ?></td>
      </tr>
    <?php endforeach; ?>

    <tr>
      <td colspan="2"></td>
      <td style="text-align:right"><strong>Total</strong></td>
      <td style="text-align:right">$<?= $cart->format_number($cart->total()) ?></td>
    </tr>
  </table>

  <button type="submit">Save cart</button>
</form>

Carrying each row's rowid through the form in a hidden field matters β€” updating depends on that value.

Updating the cart

Pass update() an array containing the rowid and whichever properties you want to change.

$this->cart->update([
    'rowid' => 'b99ccdf16028f015540f341130b6d8ec',
    'qty'   => 3,
]);

A multi-dimensional array updates several rows in one call:

$this->cart->update([
    ['rowid' => 'b99ccdf16028f015540f341130b6d8ec', 'qty' => 3],
    ['rowid' => 'xw82g9q3r495893iajdh473990rikw23', 'qty' => 4],
    ['rowid' => 'fh4kdkkkaoe30njgoe92rkdkkobec333', 'qty' => 2],
]);

You may also update options, price, or any custom field you defined when inserting the item:

$this->cart->update([
    'rowid'  => 'b99ccdf16028f015540f341130b6d8ec',
    'qty'    => 1,
    'price'  => 49.95,
    'coupon' => null,
]);

Tip

Setting qty to 0 removes the item from the cart β€” you do not need to call remove() as well.

Removing items and emptying the cart

// Remove a single row
$this->cart->remove($rowid);

// Destroy the whole cart (the session key is cleared too)
$this->cart->destroy();

βš™οΈ How it works

What is a Row ID

The Row ID is a unique identifier generated when an item is added to the cart. It exists so that identical products with different options can be managed as separate rows.

For example, say someone buys two identical t-shirts (same product ID) but in different sizes. The product ID and name are identical for both; the only difference is the size. The cart therefore needs a way to tell them apart, and it does so by deriving a "row ID" from the product ID and any options:

without options  β†’  rowid = md5(id)
with options     β†’  rowid = md5(id . serialize(options))

In practice you rarely touch the Row ID directly. It is enough that your "view cart" page carries the value in a hidden form field and passes it to update() when the form is submitted.

Where the cart is stored

The entire cart lives under a single session key: cart_contents. That array holds both the product rows β€” keyed by rowid β€” and the cart_total and total_items counters at the same level. contents() strips those two counters before handing the array back to you.

When the last item is removed, cart_contents is deleted from the session entirely.

Validation rules

Every insert() call validates the product ID and name. If validation fails the item is not inserted, an error is written to the log, and FALSE is returned.

Field Rule Default pattern
id Letters, digits, dots, dashes and underscores only \.a-z0-9_-
name Letters, digits, punctuation, symbols and spaces; 1–255 characters \p{L}\p{N}\p{P}\p{S} + space
name Control characters plus < and > are always rejected β€”
qty Cast to float; an item with 0 is not inserted β€”
price Cast to float β€”

The patterns can be changed through the $product_id_rules and $product_name_rules properties. Setting $product_name_safe = FALSE disables name validation entirely β€” but that also disables the < and > rejection, so escaping output with esc() becomes your responsibility.

🧠 Behaviour worth knowing

These behaviours are not obvious from the method names, but they do come up in practice.

insert() does not return a single type

Inserting a single item returns the rowid (a string); a multi-dimensional array returns TRUE. Both return FALSE on failure. Comparing the result with === TRUE therefore misbehaves for the single-item case β€” if you only want to know whether it succeeded, use a loose check:

if ($this->cart->insert($data)) {
    // success
}
Re-inserting the same product adds to the quantity β€” but the existing quantity is truncated to an integer

Inserting a product that resolves to an existing rowid adds the new quantity on top of the current one. The current quantity is cast to int before that addition, so if you work in fractional quantities (selling by weight, for instance) the decimal part is lost.

$this->cart->insert(['id' => 'x', 'qty' => 1.5, 'price' => 10, 'name' => 'Cheese']);
$this->cart->insert(['id' => 'x', 'qty' => 1,   'price' => 10, 'name' => 'Cheese']);
// quantity becomes 2.0, not 2.5

If you deal in fractional quantities, write an absolute quantity with update() instead of inserting again.

update() only touches keys that already exist on the row

update() intersects the keys you pass with the keys already present on the row. That means you cannot add a new field to an item after the fact β€” it is silently ignored.

$this->cart->insert(['id' => 'x', 'qty' => 1, 'price' => 10, 'name' => 'Box']);
$this->cart->update(['rowid' => $rowid, 'gift_note' => 'Birthday']);
// gift_note is not written; it had to be defined during insert
update() cannot change id or name

Both are deliberately protected. If you need to change a product's name or ID, remove the row and insert it again β€” a different id means a different rowid anyway.

remove() returns TRUE even for a rowid that does not exist

remove() always returns TRUE; it does not tell you whether the row was actually there. If you need to confirm the removal happened, check with get_item() first.

Sub-totals are recalculated before you read them

Each row's subtotal, along with the cart's cart_total and total_items counters, is recalculated whenever the cart is written to the session. The subtotal you get from contents() is therefore always current β€” you do not have to compute it yourself.

πŸ“š API reference

class Cart

Properties

Property Default Description
$product_id_rules \.a-z0-9_- Character class used to validate product IDs β€” letters, digits, dashes, underscores and dots.
$product_name_rules \p{L}\p{N}\p{P}\p{S} Character class used to validate product names β€” Unicode letters, digits, punctuation, symbols and spaces.
$product_name_safe TRUE Whether to allow only safe product names.

Methods

insert([$items = []])

Parameters $items (array) β€” The item or items to insert into the cart
Returns The rowid for a single item, TRUE for a multi-dimensional array, FALSE on failure
Return type string|bool

Inserts items into the cart and saves it to the session. The id, qty, price and name keys are required.

update([$items = []])

Parameters $items (array) β€” The row or rows to update
Returns TRUE on success, FALSE on failure
Return type bool

Changes the properties of the row matching the given rowid. Typically called from the "view cart" page when a user changes quantities before checkout. A qty of 0 removes the row from the cart.

remove($rowid)

Parameters $rowid (string) β€” Row ID of the item to remove from the cart
Returns TRUE in every case
Return type bool

Removes the row identified by $rowid from the cart.

total()

Returns The total amount in the cart
Return type float

Returns the total amount in the cart.

total_items()

Returns The total item count in the cart
Return type float

Returns the sum of the quantities across every row in the cart.

contents([$newest_first = FALSE])

Parameters $newest_first (bool) β€” Order the array with the newest items first
Returns The cart contents
Return type array

Returns an array containing everything in the cart. Pass TRUE to sort from newest to oldest, otherwise it is sorted from oldest to newest. The cart_total and total_items counters are stripped from the result.

get_item($row_id)

Parameters $row_id (string) β€” Row ID to retrieve
Returns The row data, or FALSE if no such row exists
Return type array|bool

Returns the data for the row matching the given Row ID. Also returns FALSE for cart_total and total_items.

has_options([$row_id = ''])

Parameters $row_id (string) β€” Row ID to inspect
Returns TRUE if options exist, FALSE otherwise
Return type bool

Reports whether a particular row in the cart carries options. Designed to be used in a loop alongside contents().

product_options([$row_id = ''])

Parameters $row_id (string) β€” Row ID
Returns The product options, or an empty array
Return type array

Returns the options for a particular product. Designed to be used in a loop alongside contents().

format_number([$n = ''])

Parameters $n (float|string) β€” The number to format
Returns The number formatted with a thousands separator and two decimal places; '' in, '' out
Return type string

Formats a number as 1,234.57 β€” comma as the thousands separator, dot as the decimal point. Used when printing prices and sub-totals.

destroy()

Return type void

Empties the cart and deletes the cart_contents key from the session. Usually called once the customer's order has been processed.

πŸ§ͺ Example application

The example/ directory in the repository contains a working CodeIgniter 4 application that uses the library end to end: a product list, AJAX add-to-cart, quantity updates and emptying the cart. It is deliberately excluded from the Composer package, so browse or clone it from GitHub rather than looking for it in vendor/.

File Contents
example/app/Controllers/Product.php Cart endpoints β€” insert, update, remove, destroy
example/app/Controllers/BaseController.php Constructing the Cart object and passing it to views
example/app/Views/products/basket.php The cart view
example/public/basket.js The AJAX side

πŸ“Š Project activity

Alt

πŸ“„ License

MIT β€” see LICENSE.

Release history is in CHANGELOG.md.

Developed by Bertuğ Fahri Γ–ZER Β· GitHub Β· Packagist