Разработка бэкенда сайта на PHP (CodeIgniter)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка бэкенда сайта на PHP (CodeIgniter)
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка бэкенда сайта на PHP (CodeIgniter)

CodeIgniter — самый лёгкий полноценный PHP-фреймворк. CodeIgniter 4 полностью переписан под PHP 8, получил PSR-совместимость, встроенный ORM (Query Builder + Model), HTTP-слой через PSR-7, и при этом сохранил главное преимущество: минимальный overhead, простую документацию, отсутствие магии.

Правильные сценарии для CI4: небольшие и средние сайты, хостинг с ограничениями (shared hosting, устаревшая инфраструктура), команды с опытом CI, проекты с жёсткими требованиями по производительности на слабом железе.

Структура и роутинг

// 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);
    }
}

Model и Query Builder

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;
    }

    // Кастомные методы
    public function getActiveByCategory(int $categoryId, int $limit = 20, int $offset = 0): array
    {
        return $this->where('category_id', $categoryId)
                    ->where('is_active', 1)
                    ->orderBy('created_at', 'DESC')
                    ->findAll($limit, $offset);
    }

    public function getWithCategory(): array
    {
        return $this->db->table($this->table . ' p')
            ->select('p.*, c.name as category_name')
            ->join('categories c', 'c.id = p.category_id', 'left')
            ->where('p.is_active', 1)
            ->get()
            ->getResultArray();
    }
}

JWT фильтр аутентификации

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) {}
}

Кеш

// Через cache() helper
$cacheKey = "products_cat_{$categoryId}_page_{$page}";
$products = cache($cacheKey);

if ($products === null) {
    $products = $this->productModel->getActiveByCategory($categoryId, 20, ($page - 1) * 20);
    cache()->save($cacheKey, $products, 300); // 5 минут
}

// Тегированный кеш — только Redis driver
$cache = \Config\Services::cache();
$cache->save("product_{$id}", $product, 3600, ['products', "category_{$product['category_id']}"]);
// Инвалидация по тегу
$cache->deleteMatching('products*'); // wildcard для Redis

Загрузка файлов

public function upload(): ResponseInterface
{
    $file = $this->request->getFile('image');

    if (!$file->isValid()) {
        return $this->response->setStatusCode(400)->setJSON(['error' => $file->getErrorString()]);
    }

    $rules = [
        'image' => 'uploaded[image]|max_size[image,10240]|is_image[image]|mime_in[image,image/jpg,image/jpeg,image/png,image/webp]',
    ];

    if (!$this->validate($rules)) {
        return $this->response->setStatusCode(422)->setJSON(['errors' => $this->validator->getErrors()]);
    }

    $newName = $file->getRandomName();
    $file->move(WRITEPATH . 'uploads', $newName);

    // Или в S3 через custom storage
    $url = $this->uploadToS3($file->getTempName(), $newName);

    return $this->response->setJSON(['url' => $url]);
}

Сроки разработки

CodeIgniter 4 стартует быстро:

  • Конфигурация + модели + миграции — 2–4 дня
  • Роуты + контроллеры + auth — 1–1,5 недели
  • Бизнес-логика — 1–3 недели
  • Тесты (phpunit + CI Test Tools) — 3–5 дней

Небольшой или средний сайт: 3–7 недель. CI4 — прагматичный выбор, когда нужен рабочий бэкенд без лишних зависимостей и высокого порога входа.