PHP Object-oriented (OOP) CRUD Tutorial – Step By Step Guide!

โ€”

by

in

Previously, we learned the basics from our PHP CRUD Tutorial for Beginners.

Today we welcome you to our PHP OOP CRUD Tutorial, a comprehensive guide on mastering PHP Object-Oriented Programming (OOP) for database management.

This tutorial will cover everything you need to know to create, read, update, delete, and search records in a MySQL database using Object-Oriented Programming (OOP) in PHP.

We will also show you how to upload files, making it an all-in-one resource for anyone looking to improve their OOP skills in PHP.

This tutorial will take you through the process step-by-step, providing you with a solid foundation in PHP OOP and database management. Let’s dive in!

Database Table Structure

First, we’re going to create our database and tables. We will also put sample data in our database.

The files “products.sql” and “categories.sql” are also included in the code download, located in the “dev” folder.

Create a database

  • Open your PhpMyAdmin (http://localhost/phpmyadmin)
  • Create a new database.
  • Put php_oop_crud_level_1 as the database name.
  • Click the “Create” button.

Create products table

Next, we will create the “products” table on the database we just created. The products table will store the product data.

We will create the products table using a SQL statement. You can copy it below. Here’s how to run an SQL statement using PhpMyAdmin.

  • Click php_oop_crud_level_1 database.
  • Click the “SQL” tab.
  • Copy the SQL statement below and paste it into the text area.
  • Click the “Go” button.
-- Table structure for table `products`
CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `description` text NOT NULL,
  `price` int(11) NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=38 ;

Insert products sample data

We have to put some database records. Run the following SQL statement using your PhpMyAdmin.

-- Dumping data for table `products`
INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES
(1, 'LG P880 4X HD', 'My first awesome phone!', 336, 3, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(2, 'Google Nexus 4', 'The most awesome phone of 2013!', 299, 2, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(3, 'Samsung Galaxy S4', 'How about no?', 600, 3, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(6, 'Bench Shirt', 'The best shirt!', 29, 1, '2014-06-01 01:12:26', '2014-05-31 02:12:21'),
(7, 'Lenovo Laptop', 'My business partner.', 399, 2, '2014-06-01 01:13:45', '2014-05-31 02:13:39'),
(8, 'Samsung Galaxy Tab 10.1', 'Good tablet.', 259, 2, '2014-06-01 01:14:13', '2014-05-31 02:14:08'),
(9, 'Spalding Watch', 'My sports watch.', 199, 1, '2014-06-01 01:18:36', '2014-05-31 02:18:31'),
(10, 'Sony Smart Watch', 'The coolest smart watch!', 300, 2, '2014-06-06 17:10:01', '2014-06-05 18:09:51'),
(11, 'Huawei Y300', 'For testing purposes.', 100, 2, '2014-06-06 17:11:04', '2014-06-05 18:10:54'),
(12, 'Abercrombie Lake Arnold Shirt', 'Perfect as gift!', 60, 1, '2014-06-06 17:12:21', '2014-06-05 18:12:11'),
(13, 'Abercrombie Allen Brook Shirt', 'Cool red shirt!', 70, 1, '2014-06-06 17:12:59', '2014-06-05 18:12:49'),
(25, 'Abercrombie Allen Anew Shirt', 'Awesome new shirt!', 999, 1, '2014-11-22 18:42:13', '2014-11-21 19:42:13'),
(26, 'Another product', 'Awesome product!', 555, 2, '2014-11-22 19:07:34', '2014-11-21 20:07:34'),
(27, 'Bag', 'Awesome bag for you!', 999, 1, '2014-12-04 21:11:36', '2014-12-03 22:11:36'),
(28, 'Wallet', 'You can absolutely use this one!', 799, 1, '2014-12-04 21:12:03', '2014-12-03 22:12:03'),
(30, 'Wal-mart Shirt', '', 555, 2, '2014-12-13 00:52:29', '2014-12-12 01:52:29'),
(31, 'Amanda Waller Shirt', 'New awesome shirt!', 333, 1, '2014-12-13 00:52:54', '2014-12-12 01:52:54'),
(32, 'Washing Machine Model PTRR', 'Some new product.', 999, 1, '2015-01-08 22:44:15', '2015-01-07 23:44:15');

Create categories table

The “categories” table is used to store product categories. Run the following SQL statement using your PhpMyAdmin.

-- Table structure for table `categories`
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

Insert categories sample data

We are going to have “Fashion,” “Electronics,” and “Motors” as categories in our project. I got those three category ideas from eBay, haha! Run the following SQL statement using your PhpMyAdmin.

-- Dumping data for table `categories`
INSERT INTO `categories` (`id`, `name`, `created`, `modified`) VALUES
(1, 'Fashion', '2014-06-01 00:35:07', '2014-05-30 17:34:33'),
(2, 'Electronics', '2014-06-01 00:35:07', '2014-05-30 17:34:33'),
(3, 'Motors', '2014-06-01 00:35:07', '2014-05-30 17:34:54');

Output

In this section, we set up our database using PhpMyAdmin. It should look like the image below.

We don’t have a PHP program output yet. Let’s continue to the next section to achieve more output.

Create the Layout Files

The purpose of layout files is to have a reusable code for the header and footer of our application. This will make it easier for us to make our application look good.

Create header layout file

This “layout_header.php” file will be included at the beginning of the PHP files that will need it. This way, we won’t have to write the same header codes every time.

We use the Bootstrap framework to make our project look good. If you’re not yet familiar with you, please learn our Bootstrap tutorial here first.

Bootstrap CSS asset will be included inside the head tags.

  • Create the “PhpOopCrudLevel1” folder and open it.
  • Create layout_header.php file.
  • Place the following code.
<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title><?php echo $page_title; ?></title>

    <!-- Latest compiled and minified Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />

    <!-- our custom CSS -->
    <link rel="stylesheet" href="assets/css/custom.css" />

</head>
<body>

    <!-- container -->
    <div class="container">

        <?php
        // show page title
        echo "<div class='page-header'>
                <h1>{$page_title}</h1>
            </div>";
        ?>

This layout_footer.php will be included at the end of each PHP file that need it. This way, we won’t have to write the same footer codes every time.

The assets used in this file are:

  • jQuery – a small JavaScript library needed by Bootstrap JavaScript.
  • Bootstrap JavaScript – we use this to make excellent UI components work like the navigation drop-down.
  • BootboxJS – to show good-looking alerts or confirm dialog boxes.

Let’s go on and create the footer layout file.

  • Open the “PhpOopCrudLevel1” folder.
  • Create layout_footer.php file.
  • Place the following code.
    </div>
    <!-- /container -->

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<!-- Latest compiled and minified Bootstrap JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<!-- bootbox library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js"></script>

</body>
</html>

Create a custom CSS file

This file is used to change any style we want on our web page. It is also used to override the default style given by Bootstrap.

  • Open the “PhpOopCrudLevel1” folder.
  • Create the “assets” folder.
  • Create the “css” folder.
  • Create the “custom.css” file.
  • Place the following code.
.left-margin{
    margin:0 .5em 0 0;
}

.right-button-margin{
    margin: 0 0 1em 0;
    overflow: hidden;
}

/* some changes in bootstrap modal */
.modal-body {
    padding: 20px 20px 0px 20px !important;
    text-align: center !important;
}

.modal-footer{
    text-align: center !important;
}

Output

The layout files we created in this section is meant to be used inside another PHP file. If we try to run the layout files alone, we won’t get any meaningful output.

If you run layout_header.php, it will look like this on the browser.

The custom.css file will look like this.

The layout_footer.php is blank. Let’s continue on the next section to see a more meaningful output.

Creating record in PHP the OOP Way

Create a file: create_product.php

Go back to the “PhpOopCrudLevel1” folder, create a file with a name create_product.php and put the following code inside it.

<?php
// set page headers
$page_title = "Create Product";
include_once "layout_header.php";

// contents will be here

// footer
include_once "layout_footer.php";
?>

Create a “Read Products” Button

The following code will render a button. Replace the comments // contents will be here of the previous section with the following.

echo "<div class='right-button-margin'>
        <a href='index.php' class='btn btn-default pull-right'>Read Products</a>
    </div>";

?>
<!-- 'create product' html form will be here -->
<?php

Get a Database Connection

We can use it for retrieving categories or saving new product record later. Put the following code before // set page headers comment of create_product.php file.

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// get database connection
$database = new Database();
$db = $database->getConnection();

// pass connection to objects
$product = new Product($db);
$category = new Category($db);

Create the Database Configuration Class

Getting a database connection will not work without this class. This class file will be included in most PHP files of our PHP OOP CRUD Tutorial.

Create a config folder and inside that folder, create a database.php file. Open that file and put the following code.

<?php
class Database{

    // specify your own database credentials
    private $host = "localhost";
    private $db_name = "php_oop_crud_level_1";
    private $username = "root";
    private $password = "";
    public $conn;

    // get the database connection
    public function getConnection(){

        $this->conn = null;

        try{
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
        }catch(PDOException $exception){
            echo "Connection error: " . $exception->getMessage();
        }

        return $this->conn;
    }
}
?>

Create a Form in create_product.php

The following code will render an HTML form. Open create_product.php file.

Replace <!-- 'create product' html form will be here --> comment with the following code.

<!-- PHP post code will be here -->

<!-- HTML form for creating a product -->
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="post">

    <table class='table table-hover table-responsive table-bordered'>

        <tr>
            <td>Name</td>
            <td><input type='text' name='name' class='form-control' /></td>
        </tr>

        <tr>
            <td>Price</td>
            <td><input type='text' name='price' class='form-control' /></td>
        </tr>

        <tr>
            <td>Description</td>
            <td><textarea name='description' class='form-control'></textarea></td>
        </tr>

        <tr>
            <td>Category</td>
            <td>
            <!-- categories from database will be here -->
            </td>
        </tr>

        <tr>
            <td></td>
            <td>
                <button type="submit" class="btn btn-primary">Create</button>
            </td>
        </tr>

    </table>
</form>

Loop Through the Categories Records to show as Drop-down

The following code will retrieve categories and put it in a “select” drop-down.

Replace <!-- categories from database will be here --> comment of the previous section with the following code.

<?php
// read the product categories from the database
$stmt = $category->read();

// put them in a select drop-down
echo "<select class='form-control' name='category_id'>";
    echo "<option>Select category...</option>";

    while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row_category);
        echo "<option value='{$id}'>{$name}</option>";
    }

echo "</select>";
?>

Create the Object Class for Categories

Of course, the previous section won’t work without the category object class. Create objects folder. Create category.php file. Place the following code.

<?php
class Category{

    // database connection and table name
    private $conn;
    private $table_name = "categories";

    // object properties
    public $id;
    public $name;

    public function __construct($db){
        $this->conn = $db;
    }

    // used by select drop-down list
    function read(){
        //select all data
        $query = "SELECT
                    id, name
                FROM
                    " . $this->table_name . "
                ORDER BY
                    name";

        $stmt = $this->conn->prepare( $query );
        $stmt->execute();

        return $stmt;
    }

}
?>

Prepare readName() method

It will get the category name instead of showing just an ID. Add the following code inside our category.php, you will see this method used in the next few sections.

// used to read category name by its ID
function readName(){

    $query = "SELECT name FROM " . $this->table_name . " WHERE id = ? limit 0,1";

    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();

    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    $this->name = $row['name'];
}

Code when the Form was Submitted

The user will enter the values in the HTML form and when the create (submit) button was clicked, values will be sent via POST request, the code below will save it in the database.

Open create_product.php file. Replace <!-- PHP post code will be here --> comment with the following code.

<?php
// if the form was submitted - PHP OOP CRUD Tutorial
if($_POST){

    // set product property values
    $product->name = $_POST['name'];
    $product->price = $_POST['price'];
    $product->description = $_POST['description'];
    $product->category_id = $_POST['category_id'];

    // create the product
    if($product->create()){
        echo "<div class='alert alert-success'>Product was created.</div>";
    }

    // if unable to create the product, tell the user
    else{
        echo "<div class='alert alert-danger'>Unable to create product.</div>";
    }
}
?>

Create the Object Class for Products

The previous section will not work without the product object. Open objects folder. Create product.php file. Open that file and put the following code.

<?php
class Product{

    // database connection and table name
    private $conn;
    private $table_name = "products";

    // object properties
    public $id;
    public $name;
    public $price;
    public $description;
    public $category_id;
    public $timestamp;

    public function __construct($db){
        $this->conn = $db;
    }

    // create product
    function create(){

        //write query
        $query = "INSERT INTO
                    " . $this->table_name . "
                SET
                    name=:name, price=:price, description=:description, category_id=:category_id, created=:created";

        $stmt = $this->conn->prepare($query);

        // posted values
        $this->name=htmlspecialchars(strip_tags($this->name));
        $this->price=htmlspecialchars(strip_tags($this->price));
        $this->description=htmlspecialchars(strip_tags($this->description));
        $this->category_id=htmlspecialchars(strip_tags($this->category_id));

        // to get time-stamp for 'created' field
        $this->timestamp = date('Y-m-d H:i:s');

        // bind values
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":price", $this->price);
        $stmt->bindParam(":description", $this->description);
        $stmt->bindParam(":category_id", $this->category_id);
        $stmt->bindParam(":created", $this->timestamp);

        if($stmt->execute()){
            return true;
        }else{
            return false;
        }

    }
}
?>

Output

Form to create product.

Categories drop down in the form.

When you fill out the form and clicked the “Create” button.

Changes in the database.

Reading and Paging Record in PHP the OOP Way

In this part of our PHP OOP CRUD tutorial, we will list the records from the database.

Create File: index.php

Create a new file and name it index.php. This file will show the main page of our web app. Put the following code inside it.

<?php
// set page header
$page_title = "Read Products";
include_once "layout_header.php";

// contents will be here

// set page footer
include_once "layout_footer.php";
?>

Add a “Create Product” button

The following code will render a button. When this button was clicked, it will show us a page where we can create a record. Replace the // contents will be here comments in the previous section with the following code.

echo "<div class='right-button-margin'>
    <a href='create_product.php' class='btn btn-default pull-right'>Create Product</a>
</div>";

Configure Pagination Variables

Pagination is very important if you have thousands of data from the database. Put the following code before the set page header comment of index.php file.

// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1;

// set number of records per page
$records_per_page = 5;

// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records_per_page;

// retrieve records here

Retrieve Records from the Database

Now we will retrieve data from the database. Replace // retrieve records here comment of index.php with the following code.

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// instantiate database and objects
$database = new Database();
$db = $database->getConnection();

$product = new Product($db);
$category = new Category($db);

// query products
$stmt = $product->readAll($from_record_num, $records_per_page);
$num = $stmt->rowCount();

Add readAll() Method in product.php

Retrieving records in the previous section won’t work without this method. Put the following code inside our “product.php” file which is inside the “objects” folder.

function readAll($from_record_num, $records_per_page){

    $query = "SELECT
                id, name, description, price, category_id
            FROM
                " . $this->table_name . "
            ORDER BY
                name ASC
            LIMIT
                {$from_record_num}, {$records_per_page}";

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();

    return $stmt;
}

Display data from the database

This time, we will show the list of records to the user. An HTML table will hold our data. Put the following code after the section 6.2 code.

// display the products if there are any
if($num>0){

    echo "<table class='table table-hover table-responsive table-bordered'>";
        echo "<tr>";
            echo "<th>Product</th>";
            echo "<th>Price</th>";
            echo "<th>Description</th>";
            echo "<th>Category</th>";
            echo "<th>Actions</th>";
        echo "</tr>";

        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){

            extract($row);

            echo "<tr>";
                echo "<td>{$name}</td>";
                echo "<td>{$price}</td>";
                echo "<td>{$description}</td>";
                echo "<td>";
                    $category->id = $category_id;
                    $category->readName();
                    echo $category->name;
                echo "</td>";

                echo "<td>";
                    // read one, edit and delete button will be here
                echo "</td>";

            echo "</tr>";

        }

    echo "</table>";

    // paging buttons will be here
}

// tell the user there are no products
else{
    echo "<div class='alert alert-info'>No products found.</div>";
}

Put the Read, Edit and Delete Action Buttons

The following code will render three buttons: Read, Edit and Delete button.

Inside the “while” loop of the previous section, there is a comment “read one, edit and delete button will be here“, replace that with the following code.

// read, edit and delete buttons
echo "<a href='read_one.php?id={$id}' class='btn btn-primary left-margin'>
    <span class='glyphicon glyphicon-list'></span> Read
</a>

<a href='update_product.php?id={$id}' class='btn btn-info left-margin'>
    <span class='glyphicon glyphicon-edit'></span> Edit
</a>

<a delete-id='{$id}' class='btn btn-danger delete-object'>
    <span class='glyphicon glyphicon-remove'></span> Delete
</a>";

Create paging.php for Paging Buttons

The following code will show our pagination buttons. Create a new file and name it “paging.php“. Open that file and put the following code.

<?php
echo "<ul class='pagination'>";

// button for first page
if($page>1){
    echo "<li><a href='{$page_url}' title='Go to the first page.'>
        First
    </a></li>";
}

// calculate total pages
$total_pages = ceil($total_rows / $records_per_page);

// range of links to show
$range = 2;

// display links to 'range of pages' around 'current page'
$initial_num = $page - $range;
$condition_limit_num = ($page + $range)  + 1;

for ($x=$initial_num; $x<$condition_limit_num; $x++) {

    // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
    if (($x > 0) && ($x <= $total_pages)) {

        // current page
        if ($x == $page) {
            echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
        }

        // not current page
        else {
            echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
        }
    }
}

// button for last page
if($page<$total_pages){
    echo "<li><a href='" .$page_url. "page={$total_pages}' title='Last page is {$total_pages}.'>
        Last
    </a></li>";
}

echo "</ul>";
?>

Add the countAll() method in objects/product.php

The following code will be used to count the total number of records in the database. This will be used for pagination.

Open your product.php file which is inside the “objects” folder. Add the following method in the class.

// used for paging products
public function countAll(){

    $query = "SELECT id FROM " . $this->table_name . "";

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();

    $num = $stmt->rowCount();

    return $num;
}

Include paging.php in index.php

The following code will show our pagination buttons under our records list. Put the following code after the closing “table” tag of section 6.6 above.

// the page where this paging is used
$page_url = "index.php?";

// count all products in the database to calculate total pages
$total_rows = $product->countAll();

// paging buttons here
include_once 'paging.php';

Output

Run http://localhost/PhpOopCrudLevel1/index.php on your browser, you should see something like the image below.

List of records, page 1.

List of records, page 2.

Updating Record in PHP the OOP Way

I know our PHP OOP CRUD tutorial is kinda long. Please take a break or drink some coffee first!

Create File: update_product.php

Create update_product.php file, open that file and put the following code.

<?php
// retrieve one product will be here

// set page header
$page_title = "Update Product";
include_once "layout_header.php";

// contents will be here

// set page footer
include_once "layout_footer.php";
?>

Create a “Read Products” Button

The following code will render a button. This button, when clicked, will let us go back to the records list. Replace the previous section’s “contents will be here” comments with the following code.

echo "<div class='right-button-margin'>
          <a href='index.php' class='btn btn-default pull-right'>Read Products</a>
     </div>";

?>
<!-- 'update product' form will be here -->

Retrieve One Product Information Based on the Given ID.

The following code will retrieve data that will populate our HTML form. This is important because this will let the user know what exactly the record he is updating.

Open update_product.php file. Replace “// retrieve one product will be here” comment with the following code.

// get ID of the product to be edited
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// get database connection
$database = new Database();
$db = $database->getConnection();

// prepare objects
$product = new Product($db);
$category = new Category($db);

// set ID property of product to be edited
$product->id = $id;

// read the details of product to be edited
$product->readOne();

Add readOne() method in the Product Object Class.

The readOne() method used in the previous section will not work without the following code inside /objects/product.php file.

function readOne(){

    $query = "SELECT
                name, price, description, category_id
            FROM
                " . $this->table_name . "
            WHERE
                id = ?
            LIMIT
                0,1";

    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();

    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    $this->name = $row['name'];
    $this->price = $row['price'];
    $this->description = $row['description'];
    $this->category_id = $row['category_id'];
}

Put the Values in the Form.

Now we can put the latest values to each form elements. Replace “<!-- 'update product' form will be here -->” comment of update_product.php with the following code.

<!-- post code will be here -->

<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"] . "?id={$id}");?>" method="post">
    <table class='table table-hover table-responsive table-bordered'>

        <tr>
            <td>Name</td>
            <td><input type='text' name='name' value='<?php echo $product->name; ?>' class='form-control' /></td>
        </tr>

        <tr>
            <td>Price</td>
            <td><input type='text' name='price' value='<?php echo $product->price; ?>' class='form-control' /></td>
        </tr>

        <tr>
            <td>Description</td>
            <td><textarea name='description' class='form-control'><?php echo $product->description; ?></textarea></td>
        </tr>

        <tr>
            <td>Category</td>
            <td>
                <!-- categories select drop-down will be here -->
            </td>
        </tr>

        <tr>
            <td></td>
            <td>
                <button type="submit" class="btn btn-primary">Update</button>
            </td>
        </tr>

    </table>
</form>

Loop Through the Categories Records to show as Drop-down

The following code will list the categories in a drop-down.

Notice that we put “if($product->category_id==$category_id){…” inside the while loop. This is to pre-select the option of the current record.

Replace the previous section’s comments “categories select drop-down will be here” with the following code.

<?php
$stmt = $category->read();

// put them in a select drop-down
echo "<select class='form-control' name='category_id'>";

    echo "<option>Please select...</option>";
    while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
        $category_id=$row_category['id'];
        $category_name = $row_category['name'];

        // current category of the product must be selected
        if($product->category_id==$category_id){
            echo "<option value='$category_id' selected>";
        }else{
            echo "<option value='$category_id'>";
        }

        echo "$category_name</option>";
    }
echo "</select>";
?>

Code When Form was Submitted

The following code will assign the “posted” values to the object properties. Once assigned, it will update the database with those values using the update() method.

Open update_product.php file. Replace “<!-- post code will be here --> comment with the following code.

<?php
// if the form was submitted
if($_POST){

    // set product property values
    $product->name = $_POST['name'];
    $product->price = $_POST['price'];
    $product->description = $_POST['description'];
    $product->category_id = $_POST['category_id'];

    // update the product
    if($product->update()){
        echo "<div class='alert alert-success alert-dismissable'>
            Product was updated.
        </div>";
    }

    // if unable to update the product, tell the user
    else{
        echo "<div class='alert alert-danger alert-dismissable'>
            Unable to update product.
        </div>";
    }
}
?>

Update Code in the Product Class

The following code will make the previous section’s “$product->update()” method work. Open our “product.php” which is inside the “objects” folder and add the following code.

function update(){

    $query = "UPDATE
                " . $this->table_name . "
            SET
                name = :name,
                price = :price,
                description = :description,
                category_id  = :category_id
            WHERE
                id = :id";

    $stmt = $this->conn->prepare($query);

    // posted values
    $this->name=htmlspecialchars(strip_tags($this->name));
    $this->price=htmlspecialchars(strip_tags($this->price));
    $this->description=htmlspecialchars(strip_tags($this->description));
    $this->category_id=htmlspecialchars(strip_tags($this->category_id));
    $this->id=htmlspecialchars(strip_tags($this->id));

    // bind parameters
    $stmt->bindParam(':name', $this->name);
    $stmt->bindParam(':price', $this->price);
    $stmt->bindParam(':description', $this->description);
    $stmt->bindParam(':category_id', $this->category_id);
    $stmt->bindParam(':id', $this->id);

    // execute the query
    if($stmt->execute()){
        return true;
    }

    return false;

}

Output

Click any “Edit” button in the index page. The update record form should look like the following.

When you submit the form, a message will be shown.

A record was changed in the database.

Read One Record in PHP the OOP Way

We previously made the code for “update record”, this section for reading one record from a database will be easier to do.

Create read_one.php file

This is the page where the data of a single record will be displayed. Create a new file and name it “read_one.php“, open that file and put the following code.

<?php
// set page headers
$page_title = "Read One Product";
include_once "layout_header.php";

// read products button
echo "<div class='right-button-margin'>
    <a href='index.php' class='btn btn-primary pull-right'>
        <span class='glyphicon glyphicon-list'></span> Read Products
    </a>
</div>";

// set footer
include_once "layout_footer.php";
?>

Read one record based on given record ID

The following code will read a single record from the database. Put the following code before the “set page headers” comments of the previous section.

// get ID of the product to be read
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// get database connection
$database = new Database();
$db = $database->getConnection();

// prepare objects
$product = new Product($db);
$category = new Category($db);

// set ID property of product to be read
$product->id = $id;

// read the details of product to be read
$product->readOne();

Display record on HTML table

This time, we will display the record details on an HTML table. Put the following code under the closing “div” tag of “Read Products” button.

// HTML table for displaying a product details
echo "<table class='table table-hover table-responsive table-bordered'>

    <tr>
        <td>Name</td>
        <td>{$product->name}</td>
    </tr>

    <tr>
        <td>Price</td>
        <td>${$product->price}</td>
    </tr>

    <tr>
        <td>Description</td>
        <td>{$product->description}</td>
    </tr>

    <tr>
        <td>Category</td>
        <td>";
            // display category name
            $category->id=$product->category_id;
            $category->readName();
            echo $category->name;
        echo "</td>
    </tr>

</table>";

Output

Click any “Read” button in the index page, you should see something like the image below.

Deleting Record in PHP the OOP Way

This is the last coding part of our PHP OOP CRUD Tutorial. Enjoy every code!

Put the following JavaScript code before the closing “body” tag in layout_footer.php file. We used Bootbox.js to make a Bootstrap-style confirm dialog box.

<script>
// JavaScript for deleting product
$(document).on('click', '.delete-object', function(){

    var id = $(this).attr('delete-id');

    bootbox.confirm({
        message: "<h4>Are you sure?</h4>",
        buttons: {
            confirm: {
                label: '<span class="glyphicon glyphicon-ok"></span> Yes',
                className: 'btn-danger'
            },
            cancel: {
                label: '<span class="glyphicon glyphicon-remove"></span> No',
                className: 'btn-primary'
            }
        },
        callback: function (result) {

            if(result==true){
                $.post('delete_product.php', {
                    object_id: id
                }, function(data){
                    location.reload();
                }).fail(function() {
                    alert('Unable to delete.');
                });
            }
        }
    });

    return false;
});
</script>

Create delete_product.php

Create a new file and name it “delete_product.php“. This file accepts the ID posted by the JavaScript code in the previous section. A record will be deleted from the database based on posted ID.

Open delete_product.php and put the following code.

<?php
// check if value was posted
if($_POST){

    // include database and object file
    include_once 'config/database.php';
    include_once 'objects/product.php';

    // get database connection
    $database = new Database();
    $db = $database->getConnection();

    // prepare product object
    $product = new Product($db);

    // set product id to be deleted
    $product->id = $_POST['object_id'];

    // delete the product
    if($product->delete()){
        echo "Object was deleted.";
    }

    // if unable to delete the product
    else{
        echo "Unable to delete object.";
    }
}
?>

Delete Code in Product Class

The previous section will not work with the “delete()” method in the product object. Open “product.php” which is inside the “objects” folder and put the following code.

// delete the product
function delete(){

    $query = "DELETE FROM " . $this->table_name . " WHERE id = ?";

    $stmt = $this->conn->prepare($query);
    $stmt->bindParam(1, $this->id);

    if($result = $stmt->execute()){
        return true;
    }else{
        return false;
    }
}

Output

Click any “Delete” button in the index page. A pop up confirmation will be shown.

If the user clicks “OK” the record will be deleted and gone in the table.

A record was deleted in the database.

Search Records in PHP the OOP Way

We’ll continue by adding the search feature. This will answer the question: How to search data from database in php? This is a very useful feature because you enable your users to easily search a certain data from our MySQL database.

Please note that this is a bonus section. The code in this section is not included in our LEVEL 1 source code download.

Change index.php

We have to change index.php because we are adding a โ€œsearchโ€ feature and we want our code to be short. Our index.php will now look like the following code.

<?php
// core.php holds pagination variables
include_once 'config/core.php';

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// instantiate database and product object
$database = new Database();
$db = $database->getConnection();

$product = new Product($db);
$category = new Category($db);

$page_title = "Read Products";
include_once "layout_header.php";

// query products
$stmt = $product->readAll($from_record_num, $records_per_page);

// specify the page where paging is used
$page_url = "index.php?";

// count total rows - used for pagination
$total_rows=$product->countAll();

// read_template.php controls how the product list will be rendered
include_once "read_template.php";

// layout_footer.php holds our javascript and closing html tags
include_once "layout_footer.php";
?>

Create read_template.php

Why do we need this template? We need it because exactly the same code can be used by index.php and search.php for displaying a list of records. Using a template means lesser code.

This template holds our search form as well.

<?php
// search form
echo "<form role='search' action='search.php'>
    <div class='input-group col-md-3 pull-left margin-right-1em'>";
        $search_value=isset($search_term) ? "value='{$search_term}'" : "";
        echo "<input type='text' class='form-control' placeholder='Type product name or description...' name='s' id='srch-term' required {$search_value} />
        <div class='input-group-btn'>
            <button class='btn btn-primary' type='submit'><i class='glyphicon glyphicon-search'></i></button>
        </div>
    </div>
</form>";

// create product button
echo "<div class='right-button-margin'>
    <a href='create_product.php' class='btn btn-primary pull-right'>
        <span class='glyphicon glyphicon-plus'></span> Create Product
    </a>
</div>";

// display the products if there are any
if($total_rows>0){

    echo "<table class='table table-hover table-responsive table-bordered'>
        <tr>
            <th>Product</th>
            <th>Price</th>
            <th>Description</th>
            <th>Category</th>
            <th>Actions</th>
        </tr>";

        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){

            extract($row);

            echo "<tr>
                <td>{$name}</td>
                <td>{$price}</td>
                <td>{$description}</td>
                <td>";
                    $category->id = $category_id;
                    $category->readName();
                    echo $category->name;
                echo "</td>";

                echo "<td>";

                    // read product button
                    echo "<a href='read_one.php?id={$id}' class='btn btn-primary left-margin'>
                        <span class='glyphicon glyphicon-list'></span> Read
                    </a>";

                    // edit product button
                    echo "<a href='update_product.php?id={$id}' class='btn btn-info left-margin'>
                        <span class='glyphicon glyphicon-edit'></span> Edit
                    </a>";

                    // delete product button
                    echo "<a delete-id='{$id}' class='btn btn-danger delete-object'>
                        <span class='glyphicon glyphicon-remove'></span> Delete
                    </a>";

                echo "</td>";

            echo "</tr>";

        }

    echo "</table>";

    // paging buttons
    include_once 'paging.php';
}

// tell the user there are no products
else{
    echo "<div class='alert alert-danger'>No products found.</div>";
}
?>

Create core.php in “config” folder

Create a new folder and name it “config“. Inside that folder, create a new file and name it “core.php“.

This file will hold our pagination variables. Using a core.php file is a good practice, it can be used to hold other configuration values that you might need in the future.

Open core.php and put the following code.

<?php
// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1;

// set number of records per page
$records_per_page = 5;

// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records_per_page;
?>

Change paging.php code

The new paging.php code will look like the following.

<?php
echo "<ul class=\"pagination\">";

// button for first page
if($page>1){
    echo "<li><a href='{$page_url}' title='Go to the first page.'>
        First Page
    </a></li>";
}

// count all products in the database to calculate total pages
$total_pages = ceil($total_rows / $records_per_page);

// range of links to show
$range = 2;

// display links to 'range of pages' around 'current page'
$initial_num = $page - $range;
$condition_limit_num = ($page + $range)  + 1;

for ($x=$initial_num; $x<$condition_limit_num; $x++) {

    // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
    if (($x > 0) && ($x <= $total_pages)) {

        // current page
        if ($x == $page) {
            echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
        }

        // not current page
        else {
            echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
        }
    }
}

// button for last page
if($page<$total_pages){
    echo "<li><a href='" .$page_url . "page={$total_pages}' title='Last page is {$total_pages}.'>
        Last Page
    </a></li>";
}

echo "</ul>";
?>

Include core.php and read_template.php

The core.php file will be included at the beginning of index.php file. The read_template.php will be included before the layout_footer.php inclusion. The new index.php will look like the following code

<?php
// core.php holds pagination variables
include_once 'config/core.php';

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// instantiate database and product object
$database = new Database();
$db = $database->getConnection();

$product = new Product($db);
$category = new Category($db);

$page_title = "Read Products";
include_once "layout_header.php";

// query products
$stmt = $product->readAll($from_record_num, $records_per_page);

// specify the page where paging is used
$page_url = "index.php?";

// count total rows - used for pagination
$total_rows=$product->countAll();

// read_template.php controls how the product list will be rendered
include_once "read_template.php";

// layout_footer.php holds our javascript and closing html tags
include_once "layout_footer.php";
?>

Create search.php

This is the most important file of this section. This file will display the records based on a user’s search term.

Create a new file and name it “search.php“. Open that file and put the following code.

<?php
// core.php holds pagination variables
include_once 'config/core.php';

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';

// instantiate database and product object
$database = new Database();
$db = $database->getConnection();

$product = new Product($db);
$category = new Category($db);

// get search term
$search_term=isset($_GET['s']) ? $_GET['s'] : '';

$page_title = "You searched for \"{$search_term}\"";
include_once "layout_header.php";

// query products
$stmt = $product->search($search_term, $from_record_num, $records_per_page);

// specify the page where paging is used
$page_url="search.php?s={$search_term}&";

// count total rows - used for pagination
$total_rows=$product->countAll_BySearch($search_term);

// read_template.php controls how the product list will be rendered
include_once "read_template.php";

// layout_footer.php holds our javascript and closing html tags
include_once "layout_footer.php";
?>

Add search() and countAll_BySearch() methods

Open “product.php” file which is inside the “objects” folder. Add the following methods in the class.

// read products by search term
public function search($search_term, $from_record_num, $records_per_page){

    // select query
    $query = "SELECT
                c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
            FROM
                " . $this->table_name . " p
                LEFT JOIN
                    categories c
                        ON p.category_id = c.id
            WHERE
                p.name LIKE ? OR p.description LIKE ?
            ORDER BY
                p.name ASC
            LIMIT
                ?, ?";

    // prepare query statement
    $stmt = $this->conn->prepare( $query );

    // bind variable values
    $search_term = "%{$search_term}%";
    $stmt->bindParam(1, $search_term);
    $stmt->bindParam(2, $search_term);
    $stmt->bindParam(3, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(4, $records_per_page, PDO::PARAM_INT);

    // execute query
    $stmt->execute();

    // return values from database
    return $stmt;
}

public function countAll_BySearch($search_term){

    // select query
    $query = "SELECT
                COUNT(*) as total_rows
            FROM
                " . $this->table_name . " p
            WHERE
                p.name LIKE ? OR p.description LIKE ?";

    // prepare query statement
    $stmt = $this->conn->prepare( $query );

    // bind variable values
    $search_term = "%{$search_term}%";
    $stmt->bindParam(1, $search_term);
    $stmt->bindParam(2, $search_term);

    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    return $row['total_rows'];
}

Output

php-mysql-oop-crud-tutorial

File Upload in PHP the OOP Way

In this section, we will add a “file upload” feature. This feature is included in the PRO source code download.

Change HTML form

Open create_product.php and find the “form” tag. Change that line to the following code. The “enctype” enables the form to submit a file to the server.

<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="post" enctype="multipart/form-data">

On the same HTML table, find the closing “tr” tag of the “Category” field. Add the following code. This adds an input field where the user can browse the file he wants to upload.

<tr>
    <td>Photo</td>
    <td><input type="file" name="image" /></td>
</tr>

Set the value of the “image” field

Open create_product.php and add the new “image” field. The value will be the file name of the submitted file. We used the built-in sha1_file() function the make the file name unique.

Open create_product.php file. Place the following code under $product->category_id = $_POST[‘category_id’]; code.

$image=!empty($_FILES["image"]["name"])
        ? sha1_file($_FILES['image']['tmp_name']) . "-" . basename($_FILES["image"]["name"]) : "";
$product->image = $image;

Change create() method

Open “objects” folder and open the “product.php” file inside it. Find the “create()” method.

Add the “image” field by changing the query to:

// insert query
$query = "INSERT INTO " . $this->table_name . "
            SET name=:name, price=:price, description=:description,
                category_id=:category_id, image=:image, created=:created";

On the sanitize section, it will be:

$this->image=htmlspecialchars(strip_tags($this->image));

Then bind the value.

$stmt->bindParam(":image", $this->image);

Add the “image” property at the top of the class, maybe after public $category_id;

public $image;

Using the PhpMyAdmin, add an “image” field in the products table. Set the type to VARCHAR with 512 in length.

Call uploadPhoto() method

Open create_product.php and find this line.

// product was created in database
echo "<div class='alert alert-success'>Product was created.</div>";

Put the following code under the code above. This will call the uploadPhoto() method to try uploading the file to the server.

// try to upload the submitted file
// uploadPhoto() method will return an error message, if any.
echo $product->uploadPhoto();

Add uploadPhoto() method

The previous section will not work without the complete code of uploadPhoto() method.

Open “objects” folder and open the “product.php” file inside it. Add the following method inside the class.

// will upload image file to server
function uploadPhoto(){

    $result_message="";

    // now, if image is not empty, try to upload the image
    if($this->image){

        // sha1_file() function is used to make a unique file name
        $target_directory = "uploads/";
        $target_file = $target_directory . $this->image;
        $file_type = pathinfo($target_file, PATHINFO_EXTENSION);

        // error message is empty
        $file_upload_error_messages="";

    }

    return $result_message;
}

Validate submitted file

Now we will validate the submitted file by:

  • Identifying if it’s a real or fake image.
  • Limit the allowed file types.
  • Prevent multiple files on the server.
  • Deny uploading files with large file sizes.
  • Making sure the “uploads” directory exists.

Add the following code after $file_upload_error_messages=””; of the previous section.

// make sure that file is a real image
$check = getimagesize($_FILES["image"]["tmp_name"]);
if($check!==false){
    // submitted file is an image
}else{
    $file_upload_error_messages.="<div>Submitted file is not an image.</div>";
}

// make sure certain file types are allowed
$allowed_file_types=array("jpg", "jpeg", "png", "gif");
if(!in_array($file_type, $allowed_file_types)){
    $file_upload_error_messages.="<div>Only JPG, JPEG, PNG, GIF files are allowed.</div>";
}

// make sure file does not exist
if(file_exists($target_file)){
    $file_upload_error_messages.="<div>Image already exists. Try to change file name.</div>";
}

// make sure submitted file is not too large, can't be larger than 1 MB
if($_FILES['image']['size'] > (1024000)){
    $file_upload_error_messages.="<div>Image must be less than 1 MB in size.</div>";
}

// make sure the 'uploads' folder exists
// if not, create it
if(!is_dir($target_directory)){
    mkdir($target_directory, 0777, true);
}

Return error messages

If the file is valid, we will upload the file to the server, specifically in the “uploads” folder. If there’s any error, we will return it to be shown to the user.

Place the following code after the previous section’s code.

// if $file_upload_error_messages is still empty
if(empty($file_upload_error_messages)){
    // it means there are no errors, so try to upload the file
    if(move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)){
        // it means photo was uploaded
    }else{
        $result_message.="<div class='alert alert-danger'>";
            $result_message.="<div>Unable to upload photo.</div>";
            $result_message.="<div>Update the record to upload photo.</div>";
        $result_message.="</div>";
    }
}

// if $file_upload_error_messages is NOT empty
else{
    // it means there are some errors, so show them to user
    $result_message.="<div class='alert alert-danger'>";
        $result_message.="{$file_upload_error_messages}";
        $result_message.="<div>Update the record to upload photo.</div>";
    $result_message.="</div>";
}

Show the uploaded image file

Open “objects” folder and open “product.php” file. Find readOne() method. Add the “image” field in the method. The new method should look like the following.

function readOne(){

    $query = "SELECT name, price, description, category_id, image
        FROM " . $this->table_name . "
        WHERE id = ?
        LIMIT 0,1";

    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();

    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    $this->name = $row['name'];
    $this->price = $row['price'];
    $this->description = $row['description'];
    $this->category_id = $row['category_id'];
    $this->image = $row['image'];
}

Open read_one.php file and find the closing “tr” tag of the “Category” field in the HTML table. Add the following code. This will show the uploaded image.

echo "<tr>";
    echo "<td>Image</td>";
    echo "<td>";
        echo $product->image ? "<img src='uploads/{$product->image}' style='width:300px;' />" : "No image found.";
    echo "</td>";
echo "</tr>";

Output

Click the “Create” button. You will see something like the image below.

When you submit the form, it will show a message prompt.

If you tried to upload an invalid image file, for example a PDF file. It will show an error message.

If you click the “Read” button of a record with an image, it will look like the following.

If the record has no image, it will say the “No image found.” message.

Download source codes

Choose your download

You have a choice to download the BASIC or PRO source codes.

You can get the BASIC source code free if you follow our tutorial above. But if you think you can learn faster by looking at the complete code, you may choose to download the source codes.

Your download should depend on what you want to learn or your needed features. See the list of features below.

FEATURESBASICPRO
Object Oriented Programming Source Codeโœ”โœ”
PDO extension usedโœ”โœ”
Create productโœ”โœ”
Read productโœ”โœ”
Update productโœ”โœ”
Delete productโœ”โœ”
Price display with a dollar signโœ”โœ”
Paginationโœ”โœ”
Bootstrap UIโœ”โœ”
SQL file in the โ€œdevโ€ folderโœ”โœ”
HTML5 (font-end) validation for creating and updating the productโœ”
Category selection for creating and updating products.โœ”
Buttons with Glyphiconsโœ”
Search products by name or descriptionโœ”
HTML5 (font-end) validation for search productโœ”
Pagination in searchโœ”
Allow the user to input the page number (read and search list)โœ”
Export/download records to CSVโœ”
Price display with a dollar sign, comma, and decimal pointโœ”
Multiple deleteโœ”
File upload field when creating or updating recordโœ”
Bootstrap navigation barโœ”
Select a category in the navigationโœ”
Highlight the category in the navigationโœ”
Create, read, update, delete, and search a categoryโœ”
View products by category with paginationโœ”
Pagination for category searchโœ”
Server-side validation for creating and updating product & categoryโœ”
Sorting by fields with paginationโœ”
Search product by date range โ€“ record date createdโœ”
Pagination for each product by date rangeโœ”
jQuery UI calendar for picking a dateโœ”
Use the buttons below to download. โ†“BASICPRO

Why download the source codes?

Here are more reasons to download our source codes.

  1. Use new skills for your multiple projects.
  2. Save a massive amount of time learning Bootstrap and PHP
  3. Code examples are direct to the point.
  4. Well-explained and commented on source code.
  5. Fast and friendly email support.
  6. Free source code updates.

How To Run The Source Codes?

Ready to put your new source code to the test? Awesome! Here’s how to get started:

  1. Download and extract the zip file
  2. Open the included README.txt file
  3. Follow the step-by-step instructions to set up and run the code

If you have any questions or run into any issues, our support team is here to help. Comment below, and we’ll be happy to assist you.

Thank you for supporting codeofaninja.com and our projects. We’re excited to see what you can do with your new source code!

What’s Next?

I hope you learned a lot from our PHP OOP CRUD Tutorial! Learning PHP Object Oriented Programming is fun and can dramatically improve your career.

For our next tutorial, let’s learn how to let a user log in, log out, and register in our system. Learn our PHP Login Script with Session Tutorial โ€“ Step-by-Step Guide!

[adinserter block=”3″]


Comments

241 responses to “PHP Object-oriented (OOP) CRUD Tutorial – Step By Step Guide!”

  1. twittstrap Avatar
    twittstrap

    Hi Mike,
    Your GREAT tutorial add to twittstrap resources you can find it @ https://twittstrap.com/php-object-oriented-crud-example-bootstrap/
    Regards

    1. ninjazhai Avatar
      ninjazhai

      Hey @twittstrap, thanks for featuring our work to your site! Much appreciated!

  2. ardian Avatar
    ardian

    Hi Mike , this is tutorial i’m looking for,i do step by step like your totur , but i have an error like this “Fatal error: Call to undefined method Product::readAll() in C:xampphtdocstestindex.php on line 31”,

    1. ninjazhai Avatar
      ninjazhai

      Hi @ardian, sorry I missed that part! Thanks for pointing that out, I added the readAll() method, see the new 5.6!

  3. Justin Pullen Avatar
    Justin Pullen

    I see the download isn’t availible still so I figured I’d just comment and ask. Does this tutorial produce a result that requires no refreshing during CRUD stuff?

    1. ninjazhai Avatar
      ninjazhai

      Hey @justinpullen, see the new 5.9, just add that countAll() method code in objects/product.php

  4. Justin Pullen Avatar
    Justin Pullen

    I’m getting Call to undefined method Product::countAll()…I’ve made sure I have everything to the T. Any ideas?

    1. ninjazhai Avatar
      ninjazhai

      Hi @justinpullen, thanks for pointing that out, I’ll update the post now and let you know!

  5. newcoder Avatar
    newcoder

    Great code!!! but towards the end of the tut you need to clarify wich page the code goes on.Not completely clear!!!

    1. ninjazhai Avatar
      ninjazhai

      Hey @newcoder, thanks for the feedback! I’ll update the post and clarify it more at the end part of the tut! ๐Ÿ™‚

  6. can some one pls paste in the actual working code please

    1. ninjazhai Avatar
      ninjazhai

      Hey price, I will post the code sooner, sorry it takes a while..

  7. Hey please cover maximum of OOPS concepts (what are supported in PHP) Thanks in Advanced.Very nice one

    1. ninjazhai Avatar
      ninjazhai

      Hey @gsivaprabu , thank you for showing your appreciation and your suggestion, I’ll do that in the future!

  8. download link pls

    1. ninjazhai Avatar
      ninjazhai

      Download links are provided above, see the red or green button, thanks @gogo!

  9. chikku Avatar
    chikku

    very good tutorial.. this tutorial helps the beginners like me to understand the basic oops CRUD concept .. can u please provide a tutorial of simple Model-View-Controller CRUD application with bootstrap…

    1. ninjazhai Avatar
      ninjazhai

      This is a very good tutorial suggestion, thanks @disqus_Nkcg6ZDk0D! I’ll have to prioritize this one…

  10. $x++; Undefined variable: x

    1. ninjazhai Avatar
      ninjazhai

      Hello @cntk and everyone, please take out the $x++; code, I think it’s not needed there…

  11. hi in the edit view the drop down was not showing the selected value.

    1. ninjazhai Avatar
      ninjazhai

      This is already solved, but thanks for reporting this @disqus_1NfFA3Muot!

  12. Category values are not showing the selected value in the edit view

    1. ninjazhai Avatar
      ninjazhai

      Hello @disqus_1NfFA3Muot, code above was updated, you can also see @elmizan’s fix above.

  13. hi all, to show the category on edit view please change the code from if($category_id==$id){ to if($product->category_id==$id){

    1. ninjazhai Avatar
      ninjazhai

      Hello @elmizan, thanks for bringing this to my attention, code above now updated!

  14. Vasilis Apokapa Avatar
    Vasilis Apokapa

    You forgot to initialize $category_id on your update_product.php (at least the one i have). Just added
    ” $category_id=$product->category_id; ” in the Category php block to make it work properly,

  15. Thank You GREAT CODE (y) ….this coding helps the beginners like me to understand the basic oops CRUD concept with responsive design. and no need to $x++ n i have remove it working nicely.

    $category->id = $category_id;

    $category->readName();

    echo $category->name;

    this code was not working n i have replace this code by

    $cat=$category->readName($cat_id);

    $rowcategory = $cat->fetch(PDO::FETCH_ASSOC);

    echo $rowcategory [‘name’];

    It is working but is that good manner to write code like i wrote.
    M very graceful of you Thanks a lot sir..

    1. ninjazhai Avatar
      ninjazhai

      Hey @sheetesh, you’re welcome! Thanks for commenting about it, I’ll update the code!

      1. Nathan Soares Avatar
        Nathan Soares

        Hi, when i put this code, i have the error ” call to undefined method category:: readName” on รญndex.php… Please help me… And THANK U So muxh for this tutorial!!

        1. ninjazhai Avatar
          ninjazhai

          Hey Nathan Soares, you’re welcome! Please try to add the following code in your category.php

          function readName(){

          $query = “SELECT name FROM ” . $this->table_name . ” WHERE id = ? limit 0,1″;
          $stmt = $this->conn->prepare( $query );
          $stmt->bindParam(1, $this->id);
          $stmt->execute();
          $row = $stmt->fetch(PDO::FETCH_ASSOC);

          $this->name = $row[‘name’];

          }

    2. Nathan Soares Avatar
      Nathan Soares

      The category is not showing, return an undefined method readName error, please, van u help me?

  16. ninjazhai Avatar
    ninjazhai

    Hey @vasilisapokapa, thanks for the tip to improve the code above, I appreciate it!

  17. Fitri Izuan Abdul Ronie Avatar
    Fitri Izuan Abdul Ronie

    Thank you very much for this great tutorial!! I am a beginner in PHP and this tutorial will help me a lot for my career ๐Ÿ˜€

    1. ninjazhai Avatar
      ninjazhai

      Hello @fitriizuanabdulronie, I’m glad that in some way, I was able to help you with your career! You’re welcome, best of luck to you!

  18. tom chan Avatar
    tom chan

    First many thanks for this great tutorial , I find a bug which the attachments display, I had added charset=UTF-8 in the $dsn , but it doesn’t help. do everyone know how to solve it. thanks again.

    1. ninjazhai Avatar
      ninjazhai

      As said in your solution:
      Use htmlspecialchars & nl2br for encode and decode
      now it works

  19. tom chan Avatar
    tom chan

    I use htmlspecialchars & nl2br for encode and decode
    now it works
    thanks

    1. ninjazhai Avatar
      ninjazhai

      Thanks for the tip @tom_chan!

  20. tom chan Avatar
    tom chan

    hi guys, how can I implement this paging class . https://github.com/daveismyname/pagination
    many thanks

    1. Hello @tom_chan, the source codes above has a pagination feature, check it out!

  21. Tim Yakamoto Avatar
    Tim Yakamoto

    I’m getting the following error:

    Fatal error: Call to undefined method Product::getTimestamp() in /home/codio/workspace/app/a_phpcrudexample/objects/product.php on line 24

    1. ninjazhai Avatar
      ninjazhai

      @Tim, please see 4.11 above…

  22. how to use INNER JOIN using this query?

    1. ninjazhai Avatar
      ninjazhai

      @disqus_HlVIcL74Rf, which query are you trying to implement the inner join?

  23. Where is the function that return this value in the class Product
    // to get time-stamp for ‘created’ field
    $this->getTimestamp();

    1. ninjazhai Avatar
      ninjazhai

      @simon, please see 4.11 above…

  24. UTEHN PHNU Avatar
    UTEHN PHNU

    To easiness.I will do this with some php framework.

    1. Yes but it is also good to learn the basics of OOP first. Thanks for sharing your thoughts @utehnphnu!

  25. AAhandon Handon Avatar
    AAhandon Handon

    One question! How make form to upload de multiple files with this crud?
    I bought your code. I’m happy with result.

    1. ninjazhai Avatar
      ninjazhai

      Hello @aahandonhandon, thanks for the feature suggestion and positive feedback, I’ll add it in the future update of this code!

  26. Nasik Ahamed Avatar
    Nasik Ahamed

    This tutorial is very useful for me as a OOP beginner.
    Thank..

    1. ninjazhai Avatar
      ninjazhai

      @nasikahamed I’m glad to help you! Please share this post with your friends, thanks!

  27. edemore123 Avatar
    edemore123

    hallo @ninjazhai your code is great! I want to ask why you do not have a search product in here, but you have the search in the picture above, is it posible you put that code??

  28. ninjazhai Avatar
    ninjazhai

    Hello @edemore123, I’m releasing an update to this code soon… Thanks for your patience!

  29. Albert Drent Avatar
    Albert Drent

    It’s not a big deal, but if you buy the sourcecode there are still errors: $x in index.php is not declared and generates an error and you forgot to initialize $category_id on your update_product.php. Easy to fix, but I think that these issues should be fixed in the sourcecode when you buy it.

  30. ninjazhai Avatar
    ninjazhai

    Hello @albertdrent, first of all, thanks for bringing this to my attention, I appreciate it! I uploaded the new code that fixes the issues. Thanks again!

  31. Datamaniac Avatar
    Datamaniac

    Hi, i bought the code for some strange reason i cannot delete a row. It’s not giving me any error. Any suggestions

    1. This is already fixed, I forgot to update you guys in the comments. Apologies.

      1. Brian Land Avatar
        Brian Land

        Same thing. I cannot delete, update or add a row from the app. Was this fixed?

  32. Datamaniac Avatar
    Datamaniac

    I managed to solve the delete problem. The script was locating the “delete.php”. I have another problem when i delete all records on page two for example. It displays a message “no records found”. It’s not reloading the data again. I have to navigate to the first page to display records.

    1. Hello @Datamaniac, the fix and feature has been added to LEVEL 2 source code. Thanks for reporting it to us!

  33. hidjrie Avatar
    hidjrie

    thanks, its a great tutorial.
    but why can’t create product? always appear notification ‘unable to create product’
    update and delete no problem. thanks

  34. Hi there!

    I am getting the following error:

    Fatal error: Call to a member function countAll() on a non-object in /Users/x/x/x/x/paging_product.php on line 15

    Any idea why? I feel like I have done everything right and unsure why

    1. Hello @D91, are you sure you added the code in section 5.9 above?

      1. Hi Mike, Good day. Still have an error.

        Fatal error: Call to a member function countAll() on a non-object in C:xampphtdocslxxpaging_product.php on line 15

        i already add this code to product.php but still got an error.

        // used for paging products
        public function countAll(){
        $query = “SELECT id FROM ” . $this->table_name . “”;
        $stmt = $this->conn->prepare( $query );
        $stmt->execute();
        $num = $stmt->rowCount();
        return $num;
        }

        Thanks in advance mike. ๐Ÿ™‚

        1. Hello @scof, are you sure you include $product = new Product($db); in your index.php? It should be able to call the countAll() method.

  35. muuucho Avatar
    muuucho

    Just bought the code. I need to add some php validation to the Create and Update part. Lets say I want to validate that the product name is between 2 and 10 characters, Could you please show how to do that?

    1. Hello @muuucho, thanks for purchasing the code! You can try to add an attribute to the textbox like this

      1. muuucho Avatar
        muuucho

        Thanks, but I like to learn about PHP validatio the OOP way. To let the browser do the validation means it has to be tested against every browser. I prefer having it in the PHP code. So where do i put the validation code and how do you redirect to a the form in a sticky way with your own error messages? Bootstrap i s prepared for this, I know how to do it in procedural PHP, but not in the OOP way. Thanks.

  36. malberga Avatar
    malberga

    I just bought the code and everything is working great.
    I want to redesign the code to make it more dynamic and not have hard code table properties of such tables as Products.
    I want an object that will edit tables.
    connect to table > get all column names> then build the edit form from the results
    Does anyone know of such a CRUD Object?? I don’t want to reinvent the wheel.
    or
    would anyone be interested in building such an object as a collaborative team?

    1. Hello @michaelalberga, thanks for your support on this site! Regarding what you’re trying to do, I don’t think it is a good design to have only one object in your project. But if you can provide an example that it is a good design, please let us know!

  37. Thiviyan Avatar
    Thiviyan

    I get the same error with the pagination part as some people. $total_rows = $product->countAll();, countAll is undefined. I am sure I did everything as described above.

    1. Hello, please see my reply to @lilchivi16, or check the section 5.9 of the tutorial directly. Let me know if that solves the issue at your end, thank you!

  38. lilchivi16 Avatar
    lilchivi16

    I have done everything as described above.

    I am getting the following error:
    Notice: Undefined variable: product in C:wampwwwxpaging_product.php on line 15
    Fatal error: Call to a member function countAll() on a non-object in /Users/x/x/x/x/paging_product.php on line 15
    I have noticed many people have this problem too. Is there a solution?

    1. Hello @lilchivi16, if you saw my reply to @justinpullen a year ago, I added the section 5.9 which contains the countAll() method. See https://www.codeofaninja.com/2014/06/php-object-oriented-crud-example-oop.html#comment-1452905993

      Let me know if that solves the issue at your end, thank you!

  39. Lars I Holen Avatar
    Lars I Holen

    I have purchased you product and it is working fine ๐Ÿ™‚ I would like to password protect it so not just anybody can change my data… How can this be done?

    1. Hello @fredholmen, which data do you like to password protect? I think you are talking about an added feature…

      1. Lars I Holen Avatar
        Lars I Holen

        Yes, maybe I am talking about added features… Normally people like to protect their data. It would be nice if you could show us how to do this… I am even willing to pay for it…

        1. Hello @fredholmen, we can continue this discussion via email, let me know more about the feature you want to accomplish. Shoot me an email at [email protected], thank you!

  40. Very useful, tnak you so much for sharing ..

    1. Hello @Windofelm, I’m glad you found it useful! You’re welcome!

  41. LostInLust Avatar
    LostInLust

    WOW ๐Ÿ˜€ very nice tutor from our sensei here ๐Ÿ™‚ this code is very very very nice and well structured compared to the other ๐Ÿ˜€ now my jutsu will be more elegant with this ๐Ÿ˜€

    1. Hello @renandikagalih, thanks for the kind words! I’m glad to help make your jutsu one step higher! Please share our site to your fellow Ninjas if you have time. ๐Ÿ™‚

  42. Asim Saeed Avatar
    Asim Saeed

    you did a very great job i really appreciate u but the problem for me is i m not useful to PDO i’m working on mysqli ….

    1. Hello @disqus_Uz82d6QB1G, I’m glad you appreciate our code tutorial! I advice you to learn and code in PDO because it has more advantages over MySQLi. PDO is the extension that is often developed by the PHP community.

  43. Show Box Avatar
    Show Box

    can you help with custom project i ahve most of it done needs to be converted too oop

    1. Hello @showbox, to avail our customization service, please send an email to [email protected] with your project details, thank you!

  44. Asim Saeed Avatar
    Asim Saeed

    if u have decent tutorials on PDO for beginners kindly tell me..

    1. Hi @Asim, you can go to the first part of our tutorial series here https://www.codeofaninja.com/2011/12/php-and-mysql-crud-tutorial.html

  45. Oscar Fajardo Avatar
    Oscar Fajardo

    Hello i use MS Sqlserver and i have problems with the connection, can you help me….thks

  46. Oscar Fajardo Avatar
    Oscar Fajardo

    hello i use ms sqlserver and i i have problems with the connection, can you help me.

    1. Hello @oscar, sorry I’m not familiar with MS SQL server…

  47. Hello @brianland, yes this was fixed. You must be doing something wrong. Would you send us any error message you encounter?

  48. Gautam Sharma Avatar
    Gautam Sharma

    hello sir, tutorial was excellent, and well explained for beginners like me,
    but one thing i want to ask,

    if we delete category , ok category will be deleted, but what about products, i mean product’s should be also deleted related to category, “ON DELETE CASCADE”

    1. Hello @gautamsh, thanks for the kind words, glad you liked our tutorial!

      About your question, currently, if you delete a category products will remain, only the system will be unable to identify which category they belong.

      About the cascade feature you want to implement, that can be done as well, it depends on your requirement. You have to specify the cascade when creating your table, more info here http://www.mysqltutorial.org/mysql-on-delete-cascade/

  49. Yannick Avatar
    Yannick

    Hello, I really like your tutorials. I used this one and try to redo it with my own database example. But I get the error that my variables are not defined on the index page. Could I maybe send you the code so you can take a look at it?

    1. Hello @disqus_Gv4E7Q50Tj, Thanks for the kind words! About your concern, you can try to use the PHP isset() function for that. It will check if a variable has value or non and prevent the undefined notices.

  50. Skander Avatar
    Skander

    It worked like a charm from the first time I executed !! ๐Ÿ˜€ thanks for the tutorial.

    1. You’re welcome @SkanderAb, glad it works for you and thanks for sharing your result as well!

  51. NFrankch E.S. Avatar
    NFrankch E.S.

    No entiendo muy bien el ingles.. pero en la primera me saliรณ correcto!.Thanks you!

    1. Estamos encantados de que funciona para usted @nfrankches, usted es agradable!

  52. Roger Avatar
    Roger

    Who can I read a single record? For example, if I click on a record from the records list, I’d like to be able to see that product in a single page. Just like update but in non-edit mode.

    1. Hello @Roger, thanks for suggestion a new feature. I’ll add that feature soon.

  53. AndreW Avatar
    AndreW

    That is a very god source and very good from which we can learn. I like it and I would like to buy it despite I have some good ones that I modified for need. But for now, I would to see you add the row and the columns for total by modifying the table. That will be such unit by price to maintain a total and a balance

    It is good to know how to compute with a total value to complete the codes. More people would buy the programs for more learning experience.

    1. Hello @AndreW, thanks for the kind words and intention to support our website by buying our source code!

      About the feature you described, we have a tutorial that looks like that in this link: https://www.codeofaninja.com/2015/08/simple-php-mysql-shopping-cart-tutorial.html

      Here’s a sample live demo: http://codeofaninja.com/demos/shopping-cart-in-php-mysql-level-2/products.php

      Please subscribe or share our site to one of your friends if you have time. Thank you!

  54. AndreW Avatar
    AndreW

    Would you develop a registration package with local and online sign up with email verification link to stop the verification locally for testing purpose offline and online as well.

    1. Hello @AndeW, we already have the source code for that and you can download it here: https://www.codeofaninja.com/2016/05/php-login-system-tutorial.html

  55. Unkwon Unkown Avatar
    Unkwon Unkown

    Parse error: syntax error, unexpected ‘public’ (T_PUBLIC) in C:xampphtdocsoop3objectsproduct.php on line 88

    public function countAll(){

    $query = “SELECT id FROM ” . $this->table_name . “”;

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();

    $num = $stmt->rowCount();

    return $num;
    }

    1. Hello @unkwon_unkown , it looks like you got a mistake in the code before your countAll() method. Make sure you type the correct syntax of your PHP code.

  56. Chet West Avatar
    Chet West

    I ran into issues in the “6.5 Retrieve Records from the Database”. It worked fine without the $category->readName(); The only way I was able to get it to work was to instantiate the category class using a new DB connection. What could be causing this?

    1. Hello @disqus_MBB9LbsNP9 , category class instance is seen after the ‘if’ statement.


      $category = new Category($db);

      I’m not sure why you needed a different database connection for it to work. Which version of PHP & MySQL are you using?

      1. Chet West Avatar
        Chet West

        PHP 5.3.3 (cli) (built: May 10 2016 21:39:50)
        MySQL Server version: 5.1.73 Source distribution
        Linux 2.6.32-642.3.1.el6.x86_64

        1. Please try upgrading to PHP 5.4+, much better if PHP 5.7, PHP 5.3 is very old and insecure.

          1. Chet West Avatar
            Chet West

            Upgrading worked after all. I had forgotten to restart Apache when done. Thanks so much for the help! I am now on PHP 7 and MySQL 5.5

          2. You’re welcome @disqus_MBB9LbsNP9! Thanks for confirming the solution.

      2. Chet West Avatar
        Chet West

        I tried upgrading to PHP 7… still get the same error. However, it does work if I include the following just before the $product->readAll() call.

        $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);

  57. Hello Mike. Please help me! I am still studying but I stopped on an error that I can not figure out. In my test Bootstrap does not work. Is it not missing the statment include .css in the “header”?

    1. Hello @odilonfaustino , would you tell us any error message you see? On your browser, try: right click > inspect element > console. Let us know what messages you see.

      1. Hi Mike,
        I did not see the include statement of “bootstrap”. I included and it worked. Another thing. There is an error when you do the search only by description. Note the variable “$ TOTAL_ROWS” in “search.php”. The “countAll_BySearch” function does not count when the search is only by description. Do you understand what I say? Above, new function fixed by me.

        public function countAll_BySearch($search_term){

        // select query
        $query = “SELECT
        COUNT(*) as total_rows
        FROM
        ” . $this->table_name . ” p
        LEFT JOIN
        categories c
        ON p.category_id = c.id
        WHERE
        p.name LIKE ? OR p.description LIKE ?”;

        // prepare query statement
        $stmt = $this->conn->prepare( $query );

        // bind variable values
        $search_term = “%{$search_term}%”;
        $stmt->bindParam(1, $search_term);
        $stmt->bindParam(2, $search_term);

        $stmt->execute();
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        return $row[‘total_rows’];
        }

        1. @odilonfaustino , though we are unable to replicate the issue, thanks for telling us your solution! This might help other people who experience the same issue.

  58. Is the pagination on the Read Products page supposed to work after the code for section 6 has been included. I see the pagination buttons but nothing happens when I click “2”, “3”, etc…

    1. I think I figured it out myself. The pagination does not appear to be part of the Level 1 Code. I noticed that in products.php, the readAll function limits the number of records read to what will fit on 1 page. (In other words, the contents of pages 2,3,etc,… are not even loaded.)
      The pagination functionality appears to be part of the Level 2 Code.

      1. @disqus_gL39EVJV49 , the pagination is part of the LEVEL 1 source code, as seen on the demo screenshots. The readAll function has limits to show only the requested data by page. When page 2 or 3 was clicked, the program will request and show data of page 2 and 3.

      2. Hi Mike, I didn’t see your reply until after I’d posted my own reply. (I’m guessing my own explanation wasn’t quite correct.) Anyway, I’m not getting an error. It’s just that nothing happens when I click on the page links. I probably made a mistake when I was pasting your code into my editor and I’ll check it all again. If it still doesn’t work, I’ll buy the Level 1 code. Your site is really good and I feel I should support you anyway. Thx!

        1. Okay let me know how it goes. @disqus_gL39EVJV49 , you’re welcome and thanks for your support!

          1. Ok, I noticed something strange and made an edit so now it works. When I clicked on page 2, here is what appeared in the address bar:

            http://127.0.0.1/index.php?%20?%20page=2

            So, it looks like the page parameter was being passed. But then I noticed there were two “?” between “index.php” and “page=2”. I went back to the first line of code in section 6.10 and deleted the “?” after “Index.php”. That made it work.

            I assume I could have also gone into the paging.php file and deleted the leading “?” from all relevant lines of code but that seemed harder than the former.

            Does that all make sense? It was hard to detect this issue because the browser inserts “%20” whenever there’s a space.

          2. Hello @disqus_gL39EVJV49 , I changed section 6.8 and removed two ‘?’ after ‘{$page_url}’, that way, it won’t have double ‘?’ in the address bar. Thanks for pointing this out!

    2. Hello @disqus_gL39EVJV49 , yes, pagination should work after section 6. We are unable to replicate the issue, would you tell us any error message you see?

  59. Hi Mike, I already purchased and downloaded your highest package for this tutorial. It looks like you updated it in the last year though. Am I correct, and if so, can I download your latest updates? Thanks, Adam!

    1. Hello @disqus_NSPUtEqBP0 , thanks for purchasing our code! I just sent an email update with the download link (via sellfy), please check your inbox. Check you spam folder if you didn’t see it there.

  60. Ibrahim Samad Avatar
    Ibrahim Samad

    Good! but if it is indeed OOP, then we have gone against the basic principle of Encapsulation. You accessed the Class properties directly without through any getter methods. What prevents the user from putting anything there?

    1. Hello @ibrahimsamad , it depends on the situation or example. On our example above, we don’t need to do anything fancy with the class properties, so the best way to keep our code short and clean is to access it directly.

      Many people say getter and setter methods are evil. You must read:
      http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html
      http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html
      http://marcus-biel.com/getters-and-setters-are-evil/
      http://stackoverflow.com/a/565227/827418

      But there are good reasons to use it as well. For example: http://stackoverflow.com/questions/1568091/why-use-getters-and-setters

      Additional reading:
      http://stackoverflow.com/a/2747746/827418
      http://softwareengineering.stackexchange.com/a/21809/42673

  61. Chester Dale Charles Avatar
    Chester Dale Charles

    function readAll($from_record_num, $records_per_page){

    $query = “SELECT
    id, name, description, price, category_id
    FROM
    ” . $this->table_name . ”
    ORDER BY
    name ASC
    LIMIT
    {$from_record_num}, {$records_per_page}”;

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();

    return $stmt;
    }

    i got fatal error: call to a member function prepare() on null in C:/xampp/htdocs/objects/product.php on line 68 … im struggling to find this error.. someone help me?

    1. Hello @chesterdalecharles , based on the error message, please make sure you have the $conn property / variable in your class.

      1. Chester Dale Charles Avatar
        Chester Dale Charles

        thanx mate!

  62. Gerardo Lerma Bahena Avatar
    Gerardo Lerma Bahena

    Hi Mr. Mike: I’ getting this error on product.php:
    Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined on line 75 wich is

    // bind values
    $stmt->bindParam(1, $this->name);
    $stmt->bindParam(2, $this->price);
    $stmt->bindParam(3, $this->description);
    $stmt->bindParam(4, $this->category_id);
    $stmt->bindParam(“:image”, $this->image);
    $stmt->bindParam(5, $this->timestamp);

    if($stmt->execute()){

    1. Hello @gerardolermabahena , you should try something like this:


      $stmt->bindParam(":name", $this->name);
      $stmt->bindParam(":price", $this->price);
      $stmt->bindParam(":description", $this->description);
      $stmt->bindParam(":category_id", $this->category_id);
      $stmt->bindParam(":image", $this->image);
      $stmt->bindParam(":created", $this->timestamp);

      1. Gerardo Lerma Bahena Avatar
        Gerardo Lerma Bahena

        ty very much!

        1. You’re welcome @gerardolermabahena!

          Please subscribe for more tutorials: https://www.codeofaninja.com/subscribe

  63. Chester Dale Charles Avatar
    Chester Dale Charles

    conn = $db;
    }

    // used by select drop-down list
    function read(){
    //select all data
    $query = “SELECT
    id, name
    FROM
    ” . $this->table_name . ”
    ORDER BY
    name”;

    $stmt = $this->conn->prepare( $query );
    $stmt->execute();

    return $stmt;
    }
    // used to read category name by its ID
    function readName(){

    $query = “SELECT name FROM ” . $this->table_name . ” WHERE id = ? limit 0,1″;

    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();

    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    $this->name = $row[‘name’];
    }
    ?> ‘, expecting function (T_FUNCTION) in C:xampphtdocsCRUDOOPobjectscategory.php on line 44’

    i just copy and paste your code above and i got this..

    can you help me?

    1. Hi @chesterdalecharles , your:


      conn = $db;

      should be:


      $this->conn = $db;

      Also, please don’t just copy and paste the code, you have to understand it.

      You might miss something during a copy & paste, even one character will cause an error. You might miss the order of placing the source codes as well.

  64. Mohamed Hรผsnรผ Avatar
    Mohamed Hรผsnรผ

    you are very generous

    1. Thanks @mohamed_h_sn! Please share our site to one of your friends if you have time.

      Or subscribe using the following page: https://www.codeofaninja.com/subscribe/

      Thank you!

  65. Gregory Smith Avatar
    Gregory Smith

    why are you echoing so many lines with their own echos?

    1. Hi @disqus_8QO3F20Vmo, thanks for the question. My answer is because it’s my style and I find it easier to understand the code this way. You can choose another style that works for you.

  66. Fatal error: Call to a member function execute() on boolean in C:xampphtdocsIC1COOPcrudobjectsproduct.php on line 71. I have this error i looked into it but can’t fix it, can you help me?

    1. Hi @Yasin, it looks like your database class is not set up properly. Make sure that you pass the $db in your product class.

  67. Adhieresthenes Avatar
    Adhieresthenes

    Hello Mike, first i want to say that this is the best php tutorial i’ve ever seen. Because you giving structural and details for explanation step by step. ๐Ÿ™‚

    And i wish you can help me to correct me in writting your code. Thank you
    i’m getting some error when try to create product, although all files no error.

    I hope it will success posted into database, but why i’ve got warning such “Access forbidden!” You don’t have permission to access the requested object. It is either read-protected or not readable by the server.

    If you think this is a server error, please contact the webmaster.
    Error 403

    I’ve try to setting httpd-xampp.config and vhost , but still doesn’t work. Any idea?

    Best Regards

    1. Hello @adhieresthenes , thanks for the kind words, glad that our tutorial is helping you!

      About the error you described, I never encountered it. But it sounds like an error on the server and not on our script. Do you work in localhost?

      But these links might help you solve it:
      http://stackoverflow.com/a/23594870
      http://stackoverflow.com/a/13408370
      http://stackoverflow.com/a/34037937

  68. Marco Wick Avatar
    Marco Wick

    Hi,
    i donยดt want to set upload dir to chmod 777. But that is the only way it works with this script. How to use maybe 755 or better 644?

    Thanks,
    Marco

  69. HI Just purchased but I’m having an issue with the read one product page. No product information is being displayed and no error just no data from the db. Im using wampserver to test this and learn from it any ideas?

    Apologies my mistake, I used the sql dump from section one in the tutorial rather than the provided file. This is great and really well explained. Is there any specific reason why so many echo’s, not a critic at all just wanted to know the benefit from it?

    1. Hi @kepbem , thanks for purchasing the code! Glad you were able to solve the issue.

      To answer your question about many echos:

      It’s my own style of coding and it is my personal preference. My eyes find it easier to debug with that format. You can use your own coding style as well.

  70. Hi @kepbem , 404 error means page or PHP file does not exist. Make sure category.php exists. Also, make sure you follow the tutorial or raw source code first (and make it work) before making any code customization.

  71. Marco Wick Avatar
    Marco Wick

    Hi, in the source files you use page_dom instead of page_url var for paging. I also need to update them to page_url to get it work … Which is now the correct? And where is the page_dom var set?

    1. Hi @marcowick , the correct one is $page_url, we’ll have to update the code. Thanks for pointing this out!

  72. Wiwin Ari Mulyani Avatar
    Wiwin Ari Mulyani

    when i code the new one, the data on the table not found like this, but i’ve done try your tutorial https://uploads.disquscdn.com/images/6af5647605234a19371857085c07846a7f615f0282ae8f41c3990f47dee8fc9e.png

    1. Hi @wiwinarimulyani , would you tell us the exact error message you encounter? Please described what you are trying to do. Your screenshot above looks like it is not from our tutorial.

  73. Gerardo Lerma Avatar
    Gerardo Lerma

    Hi Mike:
    How is the proper way to add the image update feature to the update_product.php file?

    I’ did this:
    // if the form was submitted
    if($_POST){

    $image=!empty($_FILES[“image”][“name”])
    ? sha1_file($_FILES[‘image’][‘tmp_name’]) . “-” . basename($_FILES[“image”][“name”]) : “”;

    // set product property values
    $product->clave = $_POST[‘clave’];
    $product->producto = $_POST[‘producto’];
    $product->marca = $_POST[‘marca’];
    $product->image = $image;
    $product->description = $_POST[‘description’];
    $product->equipos = $_POST[‘equipos’];
    $product->cant = $_POST[‘cant’];
    $product->category_id = $_POST[‘category_id’];

    // update the product
    if($product->update()){
    echo “”;
    echo “El producto fue actualizado.”;
    echo “”;
    }

    But now, if I’ dont upload any image, the original image gets deleted

    thanks

    1. Hi @disqus_63iTwK0WiM , please carefully follow our section 11.0 above. Do it without your custom fields first. If you made it work, that’s the time you’ll try to add your custom database fields.

      1. Gerardo Lerma Avatar
        Gerardo Lerma

        Thanks Mr. Dalisay!, will you add this feature in the source code?

        1. This feature is already in LEVEL 2 and LEVEL 3 source codes.

          1. Gerardo Lerma Avatar
            Gerardo Lerma

            the update function with image?, I’ll buy it asap!. thanks!

          2. You’re welcome @disqus_63iTwK0WiM ! Sorry for the late reply, yes it has that function.

      1. Hi @johnfsandique, thanks for your comment. Try to put the code inside PHP tags. I updated section 7.7 as well.

    2. Hi @disqus_63iTwK0WiM, the image was not actually deleted, the ‘image’ field in the database table only becomes empty.

      To prevent this from happening, you should check if the user uploaded an image, if there is not, you should use the previous value.

      Use readOne() method to get the previous image value. You can code this the way I described it or you can download our LEVEL 2 source code above.

  74. sebastian Avatar
    sebastian

    i cant delete an register

    1. Hi @sebastian, would you tell us the exact error message you see?

  75. Hi @spencer_gill , as discussed in the email, the solution is:

    In the coutALL_BySearch function, in the WHERE, you need to add โ€˜OR p.description LIKE ?โ€™ and then add a second โ€˜bindParamโ€™ for the second โ€˜?โ€™ and then add an array to the execute โ€“ โ€˜$stmt->execute(array($search_term, $search_term, $search_term));โ€™

  76. @spencer_gill , it looks like there is a misplaced ‘?>’ in your code. Make sure you have the matching PHP tags in category.php

  77. Hi @alfa, that feature is in the LEVEL 2 source code above that you can download.

  78. Jimmy Carmichael Avatar
    Jimmy Carmichael

    Is there an easy way to add validation? Such as checking for duplicates or empty entries? That would be very helpful. Thanks!

    1. Hi @jimmycarmichael , there’s a validation feature on our LEVEL 3 source codes, please check out the features listed above. Thanks!

  79. Hi @osman_forhad, please make sure you initialized the $category object at the beginning of create_product.php

    1. Hi @chelinmariacca, it looks like you missed a closing PHP tag. Also, your database connection is not properly instantiated in your read products page.

  80. hi sir, i have a problem with the create_company.php
    i just added … 5.5 Create a Form under 5.2 section in create_product.php,
    but that form is a html format, i put that form in the create_product.php,, it happens to an error parse error,,, pls help me on this, thank you

    1. Hi Nick, make sure your server is running correctly. Would you show us your code?

  81. Bidush Sarkar Avatar
    Bidush Sarkar

    from where i can get the full source code??

    1. Hi @bidushsarkar, you can follow and complete the whole tutorial to get the source code or download it in section 16.0 above.

  82. DesignStuff Avatar
    DesignStuff

    Is there a way to add a dropdown to change the value of of the $records_per_page?

    1. Hi @spencer_gill, yes, change the variable value in index.php file above. See section 6.3.

  83. Hi @disqus_nzBVscz4Vx, thanks for reaching out! Do you mean our code’s .htaccess rules did not work on your server? You can ask your web hosting provider to enable your server’s rewrite module.

    1. Hi Mike, Do you provide the htaccess rules? Because I checked it and I don’t have them. Where can I find them?

      1. Are you using Windows? The .htaccess file might be hidden. You should show hidden files and folders on your machine. See your folder settings.

      2. Would you send me an email at [email protected], I’ll send the .htaccess file to you as well.

    2. Hi Mike,
      Sorry I though I had answered you before. I’ve checked the document I downloaded and I can’t find the htaccess rules nor on this website. Could you indicate where can I find them?

      1. Hi @disqus_nzBVscz4Vx, are you using Windows? The .htaccess file might be hidden. You should show hidden files and folders on your machine. See your folder settings.

  84. Jason Simon Avatar
    Jason Simon

    Hi, having some problems with the bootbox piece for deletion. I tried testing this with regular javascript and I can get a confirm box to load, but when I try using the code from your tutorial the page freezes and nothing happens. I tried modifying a few pieces but for some reason, it’s not responding. The CDN is definitely there. I can’t seem to get the modal window to come up

    I know it’s not a browser issue because I could see it working on some other sites that use bootbox. I feel like there’s something wrong with my config that I can’t see.

    Here’s what I’m using. This is the button

    echo ““;
    echo ” Delete”;
    echo “
    “;

    And this is the javascript

    $(document).on(‘click’, ‘.delete-object’, function(){

    var id = $(this).attr(‘delete-id’);

    bootbox.confirm({
    message: “Are you sure?”,
    buttons: {
    confirm: {
    label: ‘ Yes’,
    className: ‘btn-danger’
    },
    cancel: {
    label: ‘ No’,
    className: ‘btn-primary’
    }
    },
    callback: function (result) {

    if(result==true){
    $.post(‘delete_instructor.php’, {
    object_id: id
    }, function(data){
    location.reload();
    }).fail(function() {
    alert(‘Unable to delete.’);
    });
    }
    }
    });

    return false;

    Thanks

    1. Hi @jasonsimon, would you tell us the error message you see on your console? Right click your page > Inspect element > Console tab.

  85. Hi @erosbenj, it looks like there’s a syntax error on or before line 54 of your create_product.php file. You can send the file to [email protected] so I can see more clearly.

  86. You’re welcome and I’m glad it was fixed! Would you share how you fixed it?

  87. Erfamae Fermo Avatar
    Erfamae Fermo

    hi i need help

    1. Hi @erfamaefermo, would please be specific? Please send a screenshot of the error message. Thanks.

  88. Erfamae Fermo Avatar
    Erfamae Fermo

    hi i need help the image upload doesn’t go to the folder uploads and it doesn’t display. it says like this; i posted the wrong screenshot on my previous comment https://uploads.disquscdn.com/images/4ea496bf9436e2ce8735d9e59316701a7c5512a368e09a9e81688e824411084b.png

    1. I’m unable to replicate the issue. Would you send the file you’re trying to upload, I’ll replicate the issue here at my end. My email address is [email protected]

  89. Joey Amstaff Avatar
    Joey Amstaff

    Hi Mike,
    I am a beginner and I like your learning program very well, nowhere can I learn so much so fast.
    Have already bought the LoginScript and am satisfied. It really saves me a lot of work.
    Now I’m working on php-object-oriented and have worked through the script.
    A few questions about Level 3:
    -Is there a preview image when creating a new entry?
    -Can I update images when changing an entry?

    Thanks for an answer

    Sincerely, Frank & Joey

    1. Hi Frank & @joeyamstaff, thanks for the kind words about our work! Thank you for purchasing our source code as well!

      To answer your question about the LEVEL 3 source code of PHP OOP CRUD:

      1. Yes, you can see the uploaded image when you create a new record.
      2. Yes, you can “Edit” the record and upload another image so it will be updated.

  90. John F. Sandique Avatar
    John F. Sandique

    “7.7 Code When Form was Submitted ” (oop crud level 1) code.. Mike, where will i insert this in ‘update_product.php’?. thanks

    1. Hi @johnfsandique, thanks for your comment, I updated section 7.5 and 7.7 above so we’ll know where to insert the code.

  91. You’re very good person Sir Mike for sharing your knowledge!

    1. Thanks for using our tutorials and your kind words @disqus_rFPsDA9uDA! Please subscribe for more knowledge https://www.codeofaninja.com/subscribe

  92. Hi @disqus_7v2EbmmN9m, I’m unable to replicate the issue. Make sure you followed section 9.0 carefully. Would you tell me the error message you encountered?

  93. Hi @ivanmanoelteixeira, thanks for sharing your story. I’m glad our work has helped you! Unfortunately, we don’t have a tutorial with MVC yet. We might do it in the near future so please subscribe so we can let you know. Use this form: https://www.codeofaninja.com/subscribe

  94. Category is not selected with when updating the selected product. product id is set as option values on the select box.

    1. Hi @ss, I’m unable to replicate the issue, product ID is not set on that case, the $id here:


      if($product->category_id==$id){

      is not the product ID because we are using


      extract($row_category);

      to make $id as the category ID.

      But thanks for pointing this out, I realized this can be problematic in some cases, so I changed section 7.6 above to the following.


      $stmt = $category->read();

      // put them in a select drop-down
      echo "";

      echo "Please select...";
      while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
      $category_id=$row_category['id'];

      // current category of the product must be selected
      if($product->category_id==$category_id){
      echo "";
      }else{
      echo "";
      }

      echo "$name";
      }
      echo "";

  95. Hi @valentindobric, that function is already on section 10.7 above.

  96. Hi @disqus_UtC3z3a7rl, thanks for the kind words! I’m unable to replicate this issue, did you encounter any error messages?

  97. Thank you for sharing your solution @disqus_UtC3z3a7rl! Many people will find your comment useful. I’ll update the tutorial with this soon.

  98. Hi @jinath, which step are you in the tutorial? Make sure you followed section 11.0 carefully so that the posted values can be read in your product.php file.

  99. @jinath, I don’t believe we have index2.php file on our tutorial. Please follow our tutorial as is. Make sure your readAll() method is inside the product class as well.

  100. Hi @kiran_ojha, would you explain your code?

    1. Kiran Ojha Avatar
      Kiran Ojha

      adding $name = $row_category[‘name’]; would solve the problem so

      1. Oh, yes, I updated the tutorial above as well. Thanks!

  101. Hi @kiran_ojha, thanks for sharing your solution. I updated our blog with this.

  102. Steve Janssen Avatar
    Steve Janssen

    Hi Mike,

    i’m having issues with the delete button, ive gone over the code and everything is where it should be (i think anyway). The Modal pops up but when you click delete nothing happens.

    Only thing i have changed is updating to Bootstrap 4.0.

    https://uploads.disquscdn.com/images/370044c936aa0762a2b9e26043d7bb6c7f2bdd6a9bb7d7e01da63408455c1aef.jpg https://uploads.disquscdn.com/images/af2e1d43702fc4217da630b9525dbe68c3ac3dbe8d632a2410161d73ac5f171d.jpg https://uploads.disquscdn.com/images/cf4e37390b6d407bd95389249ee1a7b0212bb3006bc0e07250253568ce9e0e69.jpg

    1. Hi @disqus_jOu97V5UXn, did it work when you’re using Bootstrap 3? Unfortunately, our tutorial above is not yet tested / updated with Bootstrap 4.

  103. Steve Janssen Avatar
    Steve Janssen

    Hi Mike

    my Delete Button font colour shows as black and wont change, even with CSS, all the others buttons i can change https://uploads.disquscdn.com/images/b0f4d68318245f02a7347099182ea8b498913f66e559a8920aecd055326b9f6e.jpg including inside the modal.

    any ideas?

    1. Hi @disqus_jOu97V5UXn, I think the problem is Bootstrap 4. Please use Bootstrap 3 for now because we haven’t updated the tutorial with Bootstrap 4 yet.

  104. Great! Thanks for sharing how you solved it. ๐Ÿ™‚

  105. Hi @disqus_jOu97V5UXn, you can do this by adding a new button and using a click trigger using jQuery.

    Inside the trigger is an AJAX request that sends the status value to a receiving PHP file that updates your database. This resource could help https://stackoverflow.com/questions/9436534/ajax-tutorial-for-post-and-get

  106. Steve Janssen Avatar
    Steve Janssen

    Hi Mike
    Just noticed ive asked various questions but dont think ive actually commented on how good it is…
    This a great script, gives you everything need with easy instruction, clean/organised scripting and adaptable. So are your other scripts – ive tested a few.
    This tutorial ive adapted and built on to make an online booking system for a beauty therapy business where the owner can confirm the bookings then set to pay status afterwards.
    Ive added additional objects for controlling the different availability dates per location added a transaction ID when confirming payment.
    There will also be an accounts section and email notifications based on status added.

    All built from your foundation tutorial!

    1. Hi @disqus_jOu97V5UXn, thanks for the kind words and sharing your story! I’m glad you were able to develop an online booking system with the help of our tutorials. It feels great to know that the purpose of our site is being served. ๐Ÿ™‚

  107. Hi @zaem_shakkir, I’m unable to replicate the issue. It looks like you misplaced some code. Would you show us the code of your update_product.php?

  108. Hi @disqus_lgoEvxDCrE, you’re welcome! About your question, c.field and p.field is the same as table_name.field_name, it is just a shorter version. the ‘c’ and ‘p’ is like an alias for the table name as you can see on the FROM clause of the query.

  109. Hi @disqus_jOu97V5UXn, please wrap your code in pre and code tags so I can read it better, see an example here: https://help.disqus.com/commenting/what-html-tags-are-allowed-within-comments

    I’m not sure if this will help, but you can try to create a function in your object file like this:

    public function calculateThis($var1, $var2){
    return $var1 – $var2;
    }

    and in your template file, you can do:

    echo $myobject->calculateThis($var1, $var2);

  110. Hi @Begginer, are you sure your readAll() method is inside the product class?

  111. Hi @Coder, make sure your readAll() method is inside the product class, before the last closing curly brace.

  112. Please see my reply to your other comment to fix this.

  113. I’m glad it works now. You’re welcome!

  114. Hi @disqus_ofkS9sarg8, you can do it if you followed, completed and learned from our tutorial above.

  115. Hi @disqus_jOu97V5UXn, your deleteBackup() method should look like this:


    function deleteBackup()
    {

    $this->readOne();
    unlink("uploads/" . $this->image);

    $query = "UPDATE " . $this->table_name . " SET image = NULL WHERE id = ?";

    $stmt = $this->conn->prepare($query);
    $stmt->bindParam(1, $this->id);

    if($result = $stmt->execute()){
    return true;
    }

    return false;
    }

  116. You’re welcome @disqus_hPsdya0GVf! Please share our site to your friends and please subscribe for free: https://www.codeofaninja.com/subscribe

  117. Hi @Stefano, I haven’t try MAMP yet. We are only using XAMPP. Would you try to use XAMPP?

  118. markus aurelius Avatar
    markus aurelius

    OK I FOUND ANSWER: It is in config/core.php
    My question was:
    Hi Mike. Can I ask you a question about your OOP Crud Code?
    Is there a way to increase the amount of rows that are returned.
    I.e. typically one has a pagination class and within that one can set the number of rows that are returned per page.
    Your code returns 5 rows per page.
    I want it to return 10 rows per page.

    Hope you can help me.
    Looked everywhere through your code and cannot see the setting to do this.
    Thanks Mark.

  119. Tresna Dwipayana Avatar
    Tresna Dwipayana

    hi, how about another type of file, not an image, but doc file, pdf file, xls file, etc. Thank you sensei

  120. Ermanno Lo Cascio Avatar
    Ermanno Lo Cascio

    Hi Mike!
    First, thank you for this interesting tutorial. I would need your help if possible.
    I have a little problem with seciton 7.3 with: // get ID of the product to be edited
    $id = isset($_GET[‘id’]) ? $_GET[‘id’] : die(‘ERROR: missing ID.’);

    I get always the “ERROR: missing ID”.
    I have tried to look and look again in the code, and everything is working, but if I set this line of code, I get that err.
    Do you have an idea of how to solve this? Thank you!

  121. Abdullahi Ibrahim Avatar
    Abdullahi Ibrahim

    I have from read records and create new product

    I cant found what i did wrong.

    1. Hi @disqus_SXOkL0xP9w, can you show us a screenshot of the error message you encountered?

Leave a Reply

Your email address will not be published. Required fields are marked *