Простой REST API в PHP — Пошаговое руководство

В данной статье вы узнаете, как создать простой REST API в PHP.

  • 1. Обзор проекта
  • 1.1 Что такое REST API?
  • 1.2 Зачем нужен REST API?
  • 1.3 Где используется REST API?
  • 2. Файловая структура
  • 3. Настройка базы данных
  • 3.1 Создание таблицы категорий
  • 3.2 Дамп данных для таблицы категорий
  • 3.3 Создание таблицы товаров
  • 3.4 Дамп данных для таблицы товаров
  • 3.5 Подключение к базе данных
  • 4. Получение товаров
  • 4.1 Создание объекта Product
  • 4.2 Создание файла для чтения товаров
  • 4.3 Подключение к базе данных и таблице товаров
  • 4.4 Чтение товаров из базы данных
  • 4.5 Создание метода read()
  • 4.6 Уведомление пользователя о том, что товары не найдены
  • 5. Создание товаров
  • 5.1 Создание файла create.php
  • 5.2 Создание метода create()
  • 6. Получение одного товара
  • 6.1 Создание файла read_one.php
  • 6.2 Создание метода readOne()
  • 7. Обновление товара
  • 7.1 Создание файла update.php
  • 7.2 Создание метода update()
  • 8. Удаление товара
  • 8.1 Создание файла delete.php
  • 8.2 Создание метода delete()
  • 9. Поиск товаров
  • 9.1 Создание файла search.php
  • 9.2 Создание метода search()
  • 10. Пагинация товаров
  • 10.1 Создание файла read_paging.php
  • 10.2 Создание файла core.php
  • 10.3 Создание метода readPaging()
  • 10.4 Создание метода count()
  • 10.5 Получение массива пагинации
  • 11. Получение категорий
  • 11.1 Создание класса Category
  • 11.2 Создание метода readAll()
  • 11.3 Создание файла read.php

1. Обзор проекта

1.1 Что такое REST API?

REST — это концепция (архитектура) для организации взаимодействия между независимыми объектами (приложениями) посредством протокола HTTP. Включает в себя набор принципов (рекомендаций) взаимодействия клиент-серверных приложений. Обычно он представлен в формате JSON.

API — интерфейс взаимодействия с каким-либо объектом (программой, приложением), включающий в себя набор правил, которые позволяют одному приложению общаться с другим. Эти «правила» могут включать в себя операции создания, чтения, обновления и удаления. Примером API может служить всем известная библиотека jQuery.

REST API позволяет вашему приложению взаимодействовать с одним или несколькими различными приложениями, используя концепции REST.

RESP API PHP

1.2 Зачем нужен REST API?

Во многих приложениях REST API необходим, потому что это самый легкий способ создания, чтения, обновления или удаления информации между различными приложениями через Интернет или протокол HTTP. Эта информация представляется пользователю в одно мгновение, особенно если вы используете JavaScript для отображения данных на веб-странице.

1.3 Где используется REST API?

REST API может использоваться любым приложением, которое может подключаться к Интернету. Если данные из приложения могут быть созданы, прочитаны, обновлены или удалены с помощью другого приложения, это обычно означает, что используется REST API.

2. Файловая структура

  • api/
    • config/
      • core.php
      • database.php
    • objects/
      • product.php
      • category.php
    • product/
      • create.php/
      • delete.php/
      • read.php/
      • read_paging.php/
      • read_one.php/
      • update.php/
      • search.php/
    • category/
      • read.php
    • shared/
      • utilities.php

3. Настройка базы данных

Используя PhpMyAdmin, создайте новую базу данных api_db. После этого выполните следующие SQL-запросы, чтобы создать новые таблицы с образцами данных.

3.1 Создание таблицы категорий

CREATE TABLE IF NOT EXISTS `categories` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(256) NOT NULL,
    `description` text NOT NULL,
    `created` datetime NOT NULL,
    `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=19 ;

3.2 Дамп данных для таблицы категорий

INSERT INTO `categories` (`id`, `name`, `description`, `created`, `modified`) VALUES
(1, "Fashion", "Category for anything related to fashion.", "2014-06-01 00:35:07", "2014-05-30 17:34:33"),
(2, "Electronics", "Gadgets, drones and more.", "2014-06-01 00:35:07", "2014-05-30 17:34:33"),
(3, "Motors", "Motor sports and more", "2014-06-01 00:35:07", "2014-05-30 17:34:54"),
(5, "Movies", "Movie products.", "2019-05-20 10:22:05", "2019-08-20 10:30:15"),
(6, "Books", "Kindle books, audio books and more.", "2018-03-14 08:05:25", "2019-05-20 11:29:11"),
(13, "Sports", "Drop into new winter gear.", "2016-01-09 02:24:24", "2016-01-09 01:24:24");

3.3 Создание таблицы товаров

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `description` text NOT NULL,
  `price` decimal(10,0) NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=65 ;

3.4 Дамп данных для таблицы товаров

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"),
(26, "Another product", "Awesome product!", "555", 2, "2014-11-22 19:07:34", "2014-11-21 20:07:34"),
(28, "Wallet", "You can absolutely use this one!", "799", 6, "2014-12-04 21:12:03", "2014-12-03 22:12:03"),
(31, "Amanda Waller Shirt", "New awesome shirt!", "333", 1, "2014-12-13 00:52:54", "2014-12-12 01:52:54"),
(42, "Nike Shoes for Men", "Nike Shoes", "12999", 3, "2015-12-12 06:47:08", "2015-12-12 05:47:08"),
(48, "Bristol Shoes", "Awesome shoes.", "999", 5, "2016-01-08 06:36:37", "2016-01-08 05:36:37"),
(60, "Rolex Watch", "Luxury watch.", "25000", 1, "2016-01-11 15:46:02", "2016-01-11 14:46:02");

3.5 Подключение к базе данных

Приведенный ниже код показывает учетные данные базы данных и метод для получения подключения к базе данных с помощью PDO.

Создайте папку api и откройте её. Создайте папку config и в ней создайте файл database.php со следующим кодом.

<?php

class Database
{
    // укажите свои учетные данные базы данных
    private $host = "localhost";
    private $db_name = "api_db";
    private $username = "root";
    private $password = "";
    public $conn;

    // получаем соединение с БД
    public function getConnection()
    {
        $this->conn = null;

        try {
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
            $this->conn->exec("set names utf8");
        } catch (PDOException $exception) {
            echo "Ошибка подключения: " . $exception->getMessage();
        }

        return $this->conn;
    }
}

4. Получение товаров

4.1 Создание объекта Product

Код ниже содержит класс с именем Product и несколько свойств. Также показан метод конструктора, который принимает соединение с базой данных.

Мы будем использовать этот класс для чтения данных из базы. Откройте папку api. Создайте папку objects. Откройте папку её и создайте файл product.php. Поместите в него следующий код.

<?php

class Product
{
    // подключение к базе данных и таблице "products"
    private $conn;
    private $table_name = "products";

    // свойства объекта
    public $id;
    public $name;
    public $description;
    public $price;
    public $category_id;
    public $category_name;
    public $created;

    // конструктор для соединения с базой данных
    public function __construct($db)
    {
        $this->conn = $db;
    }

    // здесь будет метод read()
}

4.2 Создание файла для чтения товаров

Код ниже содержит заголовки о том, кто может читать этот файл и какой тип содержимого он будет возвращать.

В данном случае наш файл read.php может быть прочитан кем угодно (звездочка * означает все) и вернет данные в формате JSON.

Откройте папку api. Создайте в ней папку product. Откройте её и создайте файл read.php со следующим кодом.

<?php

// необходимые HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение к базе данных будет здесь

4.3 Подключение к базе данных и таблице товаров

В приведенном ниже коде мы подключаем файлы database.php и product.php. Это файлы, которые мы создали ранее.

Нам нужно использовать метод getConnection() класса Database для получения соединения с базой данных. Мы передаем это подключение классу Product.

Замените комментарий // подключение к базе данных будет здесь в файле read.php следующим кодом.

// подключение базы данных и файл, содержащий объекты
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();

// инициализируем объект
$product = new Product($db);
 
// чтение товаров будет здесь

4.4 Чтение товаров из базы данных

В приведенном ниже коде мы используем метод read() класса Product для получения данных из базы. Через переменную $num мы проверяем, найдены ли записи.

Если найдены записи, мы перебираем их с помощью цикла while, и добавляем каждую запись в массив $products_arr, устанавливаем код ответа 200 OK и показываем его пользователю в формате JSON.

Замените комментарий // чтение товаров будет здесь в файле read.php следующим кодом.

// запрашиваем товары
$stmt = $product->read();
$num = $stmt->rowCount();

// проверка, найдено ли больше 0 записей
if ($num > 0) {
    // массив товаров
    $products_arr = array();
    $products_arr["records"] = array();

    // получаем содержимое нашей таблицы
    // fetch() быстрее, чем fetchAll()
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        // извлекаем строку
        extract($row);
        $product_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );
        array_push($products_arr["records"], $product_item);
    }

    // устанавливаем код ответа - 200 OK
    http_response_code(200);

    // выводим данные о товаре в формате JSON
    echo json_encode($products_arr);
}

// "товары не найдены" будет здесь

4.5 Создание метода read()

Мы использовали метод read() в предыдущем разделе, но он пока ещё не существует в классе Product. Нам нужно добавить этот метод. С помощью кода ниже, мы делаем запрос для получения записей из базы данных.

Откройте папку objects. Откройте файл product.php. Поместите следующий код в класс Product перед последней закрывающей фигурной скобкой вместо комментария // здесь будет метод read().

// метод для получения товаров
function read()
{
    // выбираем все записи
    $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
    ORDER BY
        p.created DESC";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // выполняем запрос
    $stmt->execute();
    return $stmt;
}

4.6 Уведомление пользователя о том, что товары не найдены

Если переменная $num имеет нулевое или отрицательное значение, это означает, что из базы данных не возвращено никаких записей. Мы должны сообщить пользователю об этом.

В приведенном ниже коде мы устанавливаем код ответа 404 — Не найдено и сообщение, что Товары не найдены.

Замените комментарий // «товары не найдены» будет здесь в файле read.php следующим кодом.

else {
    // установим код ответа - 404 Не найдено
    http_response_code(404);

    // сообщаем пользователю, что товары не найдены
    echo json_encode(array("message" => "Товары не найдены."), JSON_UNESCAPED_UNICODE);
}

5. Создание товаров

5.1 Создание файла create.php

Откройте папку product и создайте в ней файл create.php со следующим содержимым.

<?php

// необходимые HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// получаем соединение с базой данных
include_once "../config/database.php";

// создание объекта товара
include_once "../objects/product.php";
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);

// получаем отправленные данные
$data = json_decode(file_get_contents("php://input"));

// убеждаемся, что данные не пусты
if (
    !empty($data->name) &&
    !empty($data->price) &&
    !empty($data->description) &&
    !empty($data->category_id)
) {
    // устанавливаем значения свойств товара
    $product->name = $data->name;
    $product->price = $data->price;
    $product->description = $data->description;
    $product->category_id = $data->category_id;
    $product->created = date("Y-m-d H:i:s");

    // создание товара
    if ($product->create()) {
        // установим код ответа - 201 создано
        http_response_code(201);

        // сообщим пользователю
        echo json_encode(array("message" => "Товар был создан."), JSON_UNESCAPED_UNICODE);
    }
    // если не удается создать товар, сообщим пользователю
    else {
        // установим код ответа - 503 сервис недоступен
        http_response_code(503);

        // сообщим пользователю
        echo json_encode(array("message" => "Невозможно создать товар."), JSON_UNESCAPED_UNICODE);
    }
}
// сообщим пользователю что данные неполные
else {
    // установим код ответа - 400 неверный запрос
    http_response_code(400);

    // сообщим пользователю
    echo json_encode(array("message" => "Невозможно создать товар. Данные неполные."), JSON_UNESCAPED_UNICODE);
}

5.2 Создание метода create()

Откройте папку objects. Откройте файл product.php и добавьте следующий код внутри класса Product (objects / product.php).

// метод для создания товаров
function create()
{
    // запрос для вставки (создания) записей
    $query = "INSERT INTO
            " . $this->table_name . "
        SET
            name=:name, price=:price, description=:description, category_id=:category_id, created=:created";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $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->created = htmlspecialchars(strip_tags($this->created));

    // привязка значений
    $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->created);

    // выполняем запрос
    if ($stmt->execute()) {
        return true;
    }
    return false;
}

6. Получение одного товара

6.1 Создание файла read_one.php

Откройте папку product, создайте в ней файл read_one.php со следующим содержимым.

<?php

// необходимые HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Allow-Credentials: true");
header("Content-Type: application/json");

// подключение файла для соединения с базой и файл с объектом
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();

// подготовка объекта
$product = new Product($db);

// установим свойство ID записи для чтения
$product->id = isset($_GET["id"]) ? $_GET["id"] : die();

// получим детали товара
$product->readOne();

if ($product->name != null) {

    // создание массива
    $product_arr = array(
        "id" =>  $product->id,
        "name" => $product->name,
        "description" => $product->description,
        "price" => $product->price,
        "category_id" => $product->category_id,
        "category_name" => $product->category_name
    );

    // код ответа - 200 OK
    http_response_code(200);

    // вывод в формате json
    echo json_encode($product_arr);
} else {
    // код ответа - 404 Не найдено
    http_response_code(404);

    // сообщим пользователю, что такой товар не существует
    echo json_encode(array("message" => "Товар не существует"), JSON_UNESCAPED_UNICODE);
}

6.2 Создание метода readOne()

Откройте папку objects. Откройте файл product.php и добавьте следующий код внутри класса Product.

// метод для получения конкретного товара по ID
function readOne()
{
    // запрос для чтения одной записи (товара)
    $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.id = ?
        LIMIT
            0,1";
            
    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // привязываем id товара, который будет получен
    $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->category_name = $row["category_name"];
}

7. Обновление товара

7.1 Создание файла update.php

Откройте папку product, создайте в ней файл update.php и поместите в него следующий код.

<?php

// HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// подключаем файл для работы с БД и объектом Product
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();

// подготовка объекта
$product = new Product($db);

// получаем id товара для редактирования
$data = json_decode(file_get_contents("php://input"));

// установим id свойства товара для редактирования
$product->id = $data->id;

// установим значения свойств товара
$product->name = $data->name;
$product->price = $data->price;
$product->description = $data->description;
$product->category_id = $data->category_id;

// обновление товара
if ($product->update()) {
    // установим код ответа - 200 ok
    http_response_code(200);

    // сообщим пользователю
    echo json_encode(array("message" => "Товар был обновлён"), JSON_UNESCAPED_UNICODE);
}
// если не удается обновить товар, сообщим пользователю
else {
    // код ответа - 503 Сервис не доступен
    http_response_code(503);

    // сообщение пользователю
    echo json_encode(array("message" => "Невозможно обновить товар"), JSON_UNESCAPED_UNICODE);
}

7.2 Создание метода update()

В папке objects откройте файл product.php и добавьте новый метод update() внутри класса Product.

// метод для обновления товара
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);

    // очистка
    $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));

    // привязываем значения
    $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);

    // выполняем запрос
    if ($stmt->execute()) {
        return true;
    }
    return false;
}

8. Удаление товара

8.1 Создание файла delete.php

Откройте папку product и создайте файл delete.php со следующим содержимым.

<?php

// HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// подключим файл для соединения с базой и объектом Product
include_once "../config/database.php";
include_once "../objects/product.php";

// получаем соединение с БД
$database = new Database();
$db = $database->getConnection();

// подготовка объекта
$product = new Product($db);

// получаем id товара
$data = json_decode(file_get_contents("php://input"));

// установим id товара для удаления
$product->id = $data->id;

// удаление товара
if ($product->delete()) {
    // код ответа - 200 ok
    http_response_code(200);

    // сообщение пользователю
    echo json_encode(array("message" => "Товар был удалён"), JSON_UNESCAPED_UNICODE);
}
// если не удается удалить товар
else {
    // код ответа - 503 Сервис не доступен
    http_response_code(503);

    // сообщим об этом пользователю
    echo json_encode(array("message" => "Не удалось удалить товар"));
}

8.2 Создание метода delete()

В папке objects откройте файл product.php и добавьте новый метод в класс Product.

// метод для удаления товара
function delete()
{
    // запрос для удаления записи (товара)
    $query = "DELETE FROM " . $this->table_name . " WHERE id = ?";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $this->id = htmlspecialchars(strip_tags($this->id));

    // привязываем id записи для удаления
    $stmt->bindParam(1, $this->id);

    // выполняем запрос
    if ($stmt->execute()) {
        return true;
    }
    return false;
}

9. Поиск товаров

Теперь реализуем поиск записей в базе данных, в нашем случае товаров.

9.1 Создание файла search.php

В папке product создайте файл search.php со следующим кодом.

<?php

// HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение необходимых файлов
include_once "../config/core.php";
include_once "../config/database.php";
include_once "../objects/product.php";

// создание подключения к БД
$database = new Database();
$db = $database->getConnection();

// инициализируем объект
$product = new Product($db);

// получаем ключевые слова
$keywords = isset($_GET["s"]) ? $_GET["s"] : "";

// запрос товаров
$stmt = $product->search($keywords);
$num = $stmt->rowCount();

// проверяем, найдено ли больше 0 записей
if ($num > 0) {
    // массив товаров
    $products_arr = array();
    $products_arr["records"] = array();

    // получаем содержимое нашей таблицы
    // fetch() быстрее чем fetchAll()
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

        // извлечём строку
        extract($row);
        $product_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );
        array_push($products_arr["records"], $product_item);
    }
    // код ответа - 200 OK
    http_response_code(200);

    // покажем товары
    echo json_encode($products_arr);
} else {
    // код ответа - 404 Ничего не найдено
    http_response_code(404);

    // скажем пользователю, что товары не найдены
    echo json_encode(array("message" => "Товары не найдены."), JSON_UNESCAPED_UNICODE);
}

9.2 Создание метода search()

В папке objects откройте product.php и добавьте метод search().

// метод для поиска товаров
function search($keywords)
{
    // поиск записей (товаров) по "названию товара", "описанию товара", "названию категории"
    $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 ? OR c.name LIKE ?
        ORDER BY
            p.created DESC";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // очистка
    $keywords = htmlspecialchars(strip_tags($keywords));
    $keywords = "%{$keywords}%";

    // привязка
    $stmt->bindParam(1, $keywords);
    $stmt->bindParam(2, $keywords);
    $stmt->bindParam(3, $keywords);

    // выполняем запрос
    $stmt->execute();

    return $stmt;
}

10. Пагинация товаров

10.1 Создание файла read_paging.php

В папке product создайте файл read_paging.php со следующим кодом.

<?php

// установим HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение файлов
include_once "../config/core.php";
include_once "../shared/utilities.php";
include_once "../config/database.php";
include_once "../objects/product.php";

// utilities
$utilities = new Utilities();

// создание подключения
$database = new Database();
$db = $database->getConnection();

// инициализация объекта
$product = new Product($db);

// запрос товаров
$stmt = $product->readPaging($from_record_num, $records_per_page);
$num = $stmt->rowCount();

// если больше 0 записей
if ($num > 0) {

    // массив товаров
    $products_arr = array();
    $products_arr["records"] = array();
    $products_arr["paging"] = array();

    // получаем содержимое нашей таблицы
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

        // извлечение строки
        extract($row);
        $product_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description),
            "price" => $price,
            "category_id" => $category_id,
            "category_name" => $category_name
        );
        array_push($products_arr["records"], $product_item);
    }

    // подключим пагинацию
    $total_rows = $product->count();
    $page_url = "{$home_url}product/read_paging.php?";
    $paging = $utilities->getPaging($page, $total_rows, $records_per_page, $page_url);
    $products_arr["paging"] = $paging;

    // установим код ответа - 200 OK
    http_response_code(200);

    // вывод в json-формате
    echo json_encode($products_arr);
} else {

    // код ответа - 404 Ничего не найдено
    http_response_code(404);

    // сообщим пользователю, что товаров не существует
    echo json_encode(array("message" => "Товары не найдены"), JSON_UNESCAPED_UNICODE);
}

10.2 Создание файла core.php

Этот файл содержит нашу базовую конфигурацию, такую как базовый URL и переменные пагинации.

Откройте папку config и создайте в ней файл core.php со следующим содержимым.

<?php

// показывать сообщения об ошибках
ini_set("display_errors", 1);
error_reporting(E_ALL);

// URL домашней страницы
$home_url = "http://localhost/api/";

// страница указана в параметре URL, страница по умолчанию одна
$page = isset($_GET["page"]) ? $_GET["page"] : 1;

// установка количества записей на странице
$records_per_page = 5;

// расчёт для запроса предела записей
$from_record_num = ($records_per_page * $page) - $records_per_page;

10.3 Создание метода readPaging()

В папке objects откройте файл product.php и добавьте метод readPaging(). Этот метод вернет список записей, ограниченный тем, что мы установили в $records_per_page файла core.php.

// получение товаров с пагинацией
public function readPaging($from_record_num, $records_per_page)
{
    // выборка
    $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
        ORDER BY p.created DESC
        LIMIT ?, ?";

    // подготовка запроса
    $stmt = $this->conn->prepare($query);

    // свяжем значения переменных
    $stmt->bindParam(1, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(2, $records_per_page, PDO::PARAM_INT);

    // выполняем запрос
    $stmt->execute();

    // вернём значения из базы данных
    return $stmt;
}

10.4 Создание метода count()

Так же в классе Product (файл product.php) добавьте метод count() для подсчёта количества товаров.

// данный метод возвращает кол-во товаров
public function count()
{
    $query = "SELECT COUNT(*) as total_rows FROM " . $this->table_name . "";

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

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

    return $row["total_rows"];
}

10.5 Получение массива пагинации

В корне создайте папку shared, в ней файл utilities.php со следующим кодом.

<?php

class Utilities
{
    public function getPaging($page, $total_rows, $records_per_page, $page_url)
    {
        // массив пагинации
        $paging_arr = array();

        // кнопка для первой страницы
        $paging_arr["first"] = $page > 1 ? "{$page_url}page=1" : "";

        // подсчёт всех товаров в базе данных для подсчета общего количества страниц
        $total_pages = ceil($total_rows / $records_per_page);

        // диапазон ссылок для показа
        $range = 2;

        // отображать диапазон ссылок вокруг текущей страницы
        $initial_num = $page - $range;
        $condition_limit_num = ($page + $range) + 1;
        $paging_arr["pages"] = array();
        $page_count = 0;

        for ($x = $initial_num; $x < $condition_limit_num; $x++) {
            // убедимся, что $x > 0 И $x <= $total_pages
            if (($x > 0) && ($x <= $total_pages)) {
                $paging_arr["pages"][$page_count]["page"] = $x;
                $paging_arr["pages"][$page_count]["url"] = "{$page_url}page={$x}";
                $paging_arr["pages"][$page_count]["current_page"] = $x == $page ? "yes" : "no";
                $page_count++;
            }
        }

        // кнопка для последней страницы
        $paging_arr["last"] = $page < $total_pages ? "{$page_url}page={$total_pages}" : "";

        // формат json
        return json_encode($paging_arr);
    }
}

11. Получение категорий

11.1 Создание класса Category

Откройте папку objects и создайте новый файл category.php со следующим кодом.

<?php

class Category
{
    // соединение с БД и таблицей "categories"
    private $conn;
    private $table_name = "categories";

    // свойства объекта
    public $id;
    public $name;
    public $description;
    public $created;

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

    // здесь будет метод для получение всех категорий товаров
}

11.2 Создание метода readAll()

С помощью данного метода мы получим все категории товаров из базы данных. замените комментарий // здесь будет метод для получение всех категорий товаров следующим кодом:

// метод для получения всех категорий товаров
public function readAll()
{
    $query = "SELECT
                id, name, description
            FROM
                " . $this->table_name . "
            ORDER BY
                name";

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

    return $stmt;
}

11.3 Создание файла read.php

Создайте новую папку category в корне, и в ней файл read.php со следующим кодом.

<?php

// установим HTTP-заголовки
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

// подключение файлов для соединения с БД и файл с объектом Category
include_once "../config/database.php";
include_once "../objects/category.php";

// создание подключения к базе данных
$database = new Database();
$db = $database->getConnection();

// инициализация объекта
$category = new Category($db);

// получаем категории
$stmt = $category->readAll();
$num = $stmt->rowCount();

// проверяем, найдено ли больше 0 записей
if ($num > 0) {

    // массив для записей
    $categories_arr = array();
    $categories_arr["records"] = array();

    // получим содержимое нашей таблицы
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

        // извлекаем строку
        extract($row);
        $category_item = array(
            "id" => $id,
            "name" => $name,
            "description" => html_entity_decode($description)
        );
        array_push($categories_arr["records"], $category_item);
    }
    // код ответа - 200 OK
    http_response_code(200);

    // покажем данные категорий в формате json
    echo json_encode($categories_arr);
} else {

    // код ответа - 404 Ничего не найдено
    http_response_code(404);

    // сообщим пользователю, что категории не найдены
    echo json_encode(array("message" => "Категории не найдены"), JSON_UNESCAPED_UNICODE);
}

API готов к использованию!

Frontend часть данного приложения (продолжение) — jQuery + AJAX + JSON.

Если вам понравилась данная статья, рекомендую к прочтению создание регистрации и авторизации в php с использованием JWT.