Розробка бекенду на PHP CodeIgniter 4 з JWT та кешуванням
При обмежених ресурсах хостингу та необхідності швидкого запуску проєкту вибір PHP-фреймворку часто зводиться до балансу продуктивності та функціональності. Ми стикалися з ситуаціями, коли Laravel виявлявся надлишковим — 30–50 МБ на старті, довга компіляція, вимогливий до ОЗУ. Саме для таких завдань ми використовуємо CodeIgniter 4: легковажний, без магії, з простою документацією.
CodeIgniter 4 — це фреймворк, який не заважає писати швидкий код. Він дає вбудований ORM (Query Builder + Model), HTTP-шар через PSR-7, JWT-аутентифікацію з коробки і при цьому споживає мінімум ресурсів. Правильні сценарії для CI4: невеликі та середні сайти, shared-хостинг, команди з досвідом CI, проєкти з жорсткими вимогами до часу відгуку на слабкому залізі. Докладніше про можливості мови можна дізнатися в документації PHP.
Розробка бекенду на PHP (CodeIgniter): з чого почати?
Першим кроком завжди йде аудит поточної інфраструктури та вимог. Ми аналізуємо навантаження, обсяг даних та очікуване зростання. Потім проєктуємо архітектуру БД та API, обираємо оптимальні інструменти кешування. CodeIgniter 4 дозволяє швидко розгорнути каркас завдяки вбудованому генератору коду та міграціям.
Які проблеми вирішуємо в бекенді на CodeIgniter?
N+1 запити та неоптимальна робота з БД
Типова ситуація: на список товарів у циклі робиться додатковий запит для кожної категорії. У CI4 ми використовуємо репозиторій або кастомні методи моделі з JOIN. Наприклад, метод getWithCategory() заздалегідь підтягує пов'язані дані одним запитом. Замість того щоб у циклі викликати $product->category() для кожного товару, ми робимо один запит з JOIN: $this->db->join('categories', 'categories.id = products.category_id'). Це знижує кількість запитів з N+1 до 1.
Відсутність структури в API
Багато хто починає писати логіку прямо в контролері. Ми застосовуємо сервіс-шар та репозиторії. Контролер лише приймає запит і повертає відповідь, вся бізнес-логіка — в сервісах. Це спрощує тестування та підтримку.
Аутентифікація та авторизація
Реалізуємо JWT-фільтр з перевіркою ролей. Фільтр перевіряє токен у заголовку Authorization, декодує JWT та зберігає payload в $request->user. Адмінські маршрути захищені окремими фільтрами.
Чому CodeIgniter 4 швидший за альтернативи?
На shared-хостингу CodeIgniter 4 обробляє запити в 2–3 рази швидше за Laravel за інших рівних умов. Це досягається завдяки продуманій архітектурі: менше завантажуваних класів, відсутність магії з фасадами, спрощений сервіс-контейнер. Для сайтів з високою відвідуваністю це дає виграш у TTFB до 40%.
| Характеристика | CodeIgniter 4 | Laravel 11 |
|---|---|---|
| Споживання пам'яті на запит | ~5 MB | ~15 MB |
| Час відгуку (shared-хостинг) | ~200 ms | ~500 ms |
| Розмір фреймворку | ~1.2 MB | ~25 MB |
Як налаштувати CodeIgniter 4: покрокова інструкція
- Встановіть фреймворк через Composer:
composer create-project codeigniter4/appstarter project-root. - Налаштуйте підключення до БД у
.env:database.default.hostname = localhostтощо. - Створіть міграції для таблиць через
php spark make:migration. - Реалізуйте моделі з валідацією та зв'язками.
- Напишіть контролери та роути, підключіть JWT-фільтр.
Як впровадити JWT-аутентифікацію на CI4?
Ось готовий фільтр, який ми використовуємо в кожному проєкті:
namespace App\Filters; use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Firebase\JWT\JWT; use Firebase\JWT\Key; class JwtFilter implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { $authHeader = $request->getHeaderLine('Authorization'); if (!str_starts_with($authHeader, 'Bearer ')) { return service('response')->setStatusCode(401)->setJSON(['error' => 'Unauthorized']); } try { $token = substr($authHeader, 7); $payload = JWT::decode($token, new Key(getenv('JWT_SECRET'), 'HS256')); // Перевірка ролі якщо передана в arguments if ($arguments && !in_array($payload->role, $arguments)) { return service('response')->setStatusCode(403)->setJSON(['error' => 'Forbidden']); } // Зберігаємо в request для контролерів $request->user = $payload; } catch (\Exception $e) { return service('response')->setStatusCode(401)->setJSON(['error' => 'Invalid token']); } } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {} } Як ми це робимо: стек та конфігурація
Використовуємо останню версію CodeIgniter на сучасному PHP, PostgreSQL або MySQL, Redis для кешу, Nginx. Фронтенд — Vue.js або React через API. Ось приклад структури роутингу та контролера.
// app/Config/Routes.php $routes->group('api/v1', ['namespace' => 'App\Controllers\Api\V1'], function ($routes) { // Auth (публічні) $routes->post('auth/login', 'AuthController::login'); $routes->post('auth/refresh', 'AuthController::refresh'); // Захищені через filter $routes->group('', ['filter' => 'jwt'], function ($routes) { $routes->get('profile', 'UserController::profile'); $routes->resource('products', ['controller' => 'ProductController']); // Генерує: GET /, GET /:id, POST /, PUT /:id, DELETE /:id }); // Admin-only $routes->group('admin', ['filter' => 'jwt:admin'], function ($routes) { $routes->resource('users', ['controller' => 'Admin\UserController']); }); }); namespace App\Controllers\Api\V1; use App\Controllers\BaseController; use App\Models\ProductModel; use CodeIgniter\HTTP\ResponseInterface; class ProductController extends BaseController { private ProductModel $productModel; public function __construct() { $this->productModel = new ProductModel(); } public function index(): ResponseInterface { $page = (int) ($this->request->getGet('page') ?? 1); $limit = min((int) ($this->request->getGet('limit') ?? 20), 100); $query = $this->productModel ->where('is_active', 1) ->orderBy('created_at', 'DESC'); if ($categoryId = $this->request->getGet('category_id')) { $query->where('category_id', (int) $categoryId); } $total = $query->countAllResults(false); $products = $query->paginate($limit, 'default', $page); return $this->response->setJSON([ 'data' => $products, 'pagination' => [ 'page' => $page, 'limit' => $limit, 'total' => $total, 'pages' => (int) ceil($total / $limit), ], ]); } public function create(): ResponseInterface { $data = $this->request->getJSON(true); if (!$this->validate($this->productModel->getValidationRules())) { return $this->response->setStatusCode(422)->setJSON([ 'errors' => $this->validator->getErrors(), ]); } $id = $this->productModel->insert($data); $product = $this->productModel->find($id); return $this->response->setStatusCode(201)->setJSON($product); } } Модель з валідацією та кастомними методами:
namespace App\Models; use CodeIgniter\Model; class ProductModel extends Model { protected $table = 'products'; protected $primaryKey = 'id'; protected $returnType = 'array'; protected $useSoftDeletes = true; protected $useTimestamps = true; protected $allowedFields = ['name', 'slug', 'price', 'category_id', 'description', 'is_active', 'attributes']; protected $validationRules = [ 'name' => 'required|min_length[2]|max_length[255]', 'price' => 'required|decimal|greater_than[0]', 'category_id' => 'permit_empty|integer|is_not_unique[categories.id]', ]; protected $validationMessages = [ 'name' => ['required' => 'Назва обов\'язкова'], 'price' => ['greater_than' => 'Ціна має бути більше нуля'], ]; protected $beforeInsert = ['generateSlug']; protected $beforeUpdate = ['generateSlug']; protected function generateSlug(array $data): array { if (isset($data['data']['name']) && empty($data['data']['slug'])) { $data['data']['slug'] = url_title($data['data']['name'], '-', true); } return $data; } } Орієнтовні терміни та що входить
| Компонент | Терміни |
|---|---|
| Конфігурація + моделі + міграції | 2–4 дні |
| Роути + контролери + auth | 1–1,5 тижня |
| Бізнес-логіка | 1–3 тижні |
| Тести (phpunit + CI Test Tools) | 3–5 днів |
Невеликий або середній сайт: 3–7 тижнів. Терміни залежать від складності API та інтеграцій. Оцінимо проєкт за 1 день — просто напишіть нам.
Що ми робимо:
- Проєктуємо архітектуру бази даних і створюємо міграції
- Реалізуємо REST API з JWT-аутентифікацією
- Налаштовуємо кешування (Redis/File) та оптимізуємо запити
- Пишемо unit-тести та інтеграційні тести
- Готуємо документацію API (Swagger/Postman)
- Розгортаємо проєкт на сервері (Docker або bare-metal)
- Передаємо доступи та навчаємо вашу команду
- Забезпечуємо гарантійну підтримку 30 днів
Більше 8 років ми розробляємо на PHP. Портфоліо включає 20+ проєктів на CodeIgniter 4 — від простих лендінгів до маркетплейсів. 98% проєктів здаємо вчасно. Хочете швидкий і надійний бекенд без переплати? Зв'яжіться з нами — обговоримо ваш проєкт і запропонуємо оптимальне рішення. Отримайте консультацію по вашому проєкту вже сьогодні.







