Работа с базой данных

5 / 21
20 мин чтения
Введение
О чём говорим

В прошлой части мы разобрали маршрутизацию и увидели ключевую разницу: в Laravel роутинг — каркас приложения, а в Битриксе роутинг — стратегия (исторически “страницы+компоненты”, сейчас — D7‑роуты и контроллеры).

Теперь тема, где “каркас” встречается с реальностью проекта: база данных.

Здесь важно заранее проговорить честный нюанс сравнения:

  • Laravel почти всегда воспринимает БД как вашу модель данных: вы проектируете таблицы, связи, ограничения, миграции.
  • Битрикс часто воспринимает БД как часть платформы: существенный объём данных живёт в таблицах ядра (инфоблоки, пользователи, заказы, права, настройки). А ваши таблицы — это дополнение к платформе.

В этой части разберём:

  • как настроить подключения и несколько соединений,
  • как выглядят запросы (Query Builder / D7 Query),
  • как устроены ORM (Eloquent vs D7 ORM),
  • и почему миграции — это “точка боли” в Битрикс‑проектах (и как её закрывают на практике).

Подключение и конфигурация

Laravel: .env + config/database.php (и почему это удобно)

В Laravel настройки подключения обычно живут в двух местах:

  • .env — значения окружения (секреты и инфраструктура),
  • config/database.php — карта соединений и опции драйверов.

Типовой минимум в .env:

        DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=app
DB_USERNAME=app
DB_PASSWORD=secret

    

Практический смысл такого разделения:

  • один и тот же код разворачивается в dev/stage/prod без “правок в репозитории”,
  • можно держать несколько соединений (mysql, pgsql, sqlite) и выбирать их по задаче,
  • можно включать “read/write splitting” (master/replica), не ломая прикладной код.

В реальных проектах сразу приучайте себя к дисциплине:

  • хранить секреты в окружении,
  • фиксировать настройки через config:* кэш (на production),
  • избегать “магии” вида env() из бизнес‑кода (в Laravel это плохая практика — env() предназначен для конфигов).

Битрикс: .settings.php / .settings_extra.php и соединения

В Битриксе параметры соединения обычно лежат в /bitrix/.settings.php (или дополняются в /bitrix/.settings_extra.php, а в кастомных проектах часто — и в /local/.settings*.php, если вы так договорились внутри команды).

Концептуально вы задаёте:

  • тип подключения (MysqlConnection / PgsqlConnection),
  • хост/пользователь/пароль/базу,
  • и имя соединения (обычно default).

Дальше в D7 вы работаете через Connection:

        use Bitrix\Main\Application;

$connection = Application::getConnection(); // default
// $connection->query('SELECT 1'); // примитивный пример

    

Ключевая разница по “ощущению”:

  • в Laravel “вы управляете БД проекта”,
  • в Битриксе вы часто “подключаетесь к БД платформы”, где уже есть огромный пласт таблиц и соглашений.

Отсюда практический совет: фиксируйте соглашения команды (где живут настройки, как называются соединения, есть ли реплика/кластер, как вы переносите настройки между окружениями).

Query Builder: писать запросы без “сырого SQL”

Laravel: Query Builder (через DB::table)

Laravel Query Builder — это способ писать SQL‑логикой, оставаясь в PHP‑API:

        use Illuminate\Support\Facades\DB;

$rows = DB::table('users')
    ->select(['id', 'name', 'email'])
    ->where('active', 1)
    ->orderByDesc('id')
    ->limit(10)
    ->get();

    

Где это особенно полезно:

  • сложные выборки без создания “моделей”,
  • отчёты и агрегаты (groupBy, having, selectRaw),
  • быстрые админские экраны,
  • аккуратная работа с транзакциями через DB::transaction.

Битрикс: D7 Query (через ORM Query API)

В D7 Query чаще всего вы стартуете от *Table::query() и дальше “собираете” запрос:

        use Bitrix\Main\UserTable;

$rows = UserTable::query()
    ->setSelect(['ID', 'NAME', 'LAST_NAME', 'EMAIL'])
    ->where('ACTIVE', 'Y')
    ->setOrder(['ID' => 'DESC'])
    ->setLimit(10)
    ->fetchAll();

    

Если вам ближе «объектный» стиль, можно вызвать fetchCollection() вместо fetchAll() — тогда вы получите ORM‑коллекцию Битрикса.

Важно это будет ORM‑коллекция Битрикса, а не Illuminate\Support\Collection, и при больших объёмах данных «гидрация объектов» может быть дороже, чем fetchAll() с массивами.

Сильная сторона D7‑подхода: запрос привязан к описанию сущности (таблица, поля, связи), поэтому меньше “магии строк” по проекту.

Важно не спутать: сам синтаксис запроса в D7 Query вполне сопоставим с Laravel (и особенно с Laravel Query Builder). “Вес” и “многословность” Битрикса обычно проявляются не на select/where/limit, а в двух местах:

  1. Объектность результата. В Laravel Model::query()->get() возвращает Illuminate\Support\Collection из моделей (Active Record). В D7 по умолчанию вы чаще получаете массивы (fetchAll()), а если хотите объектный слой — используете fetchObject()/fetchCollection(). Это нормально, просто другой “дефолт” и другой API коллекций (в Bitrix это ORM‑коллекции, а не Laravel Collection).

  2. Сложные запросы и связи. Когда вы выходите за рамки простого фильтра/сортировки и начинаете делать join’ы, вычисляемые поля, runtime‑reference и т.п., в D7 обычно появляется больше “сопутки” (карта сущностей, Reference, runtime‑поля). В Eloquent часть этого прячется за отношениями и eager loading.

ORM: Eloquent vs Bitrix D7 ORM (и почему это разные философии)

Laravel Eloquent: Active Record “как основной путь”

Eloquent — Active Record ORM: модель одновременно и “запись”, и “логика работы с таблицей”.

Минимальный пример:

        namespace App\Models;

use Illuminate\Database\Eloquent\Model;

final class Post extends Model
{
    protected $fillable = ['title', 'body', 'author_id'];
}

    

Связи (самое важное “в реальной жизни”):

        namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

final class Post extends Model
{
    public function author(): BelongsTo
    {
        return $this->belongsTo(User::class, 'author_id');
    }
}

    

Проблема №1 у новичков — N+1 запрос. Лечится eager loading:

        $posts = Post::query()
    ->with('author')
    ->latest('id')
    ->paginate(20);

    

Что делает Eloquent сильным:

  • связи “из коробки” (и инструменты борьбы с N+1),
  • скоупы, касты, события моделей,
  • очень быстрый “time to market”.

Где нужно быть осторожным:

  • Eloquent легко соблазняет “всю бизнес‑логику положить в модель”,
  • сложные доменные правила лучше держать в сервисах/доменном слое, а не в Active Record.

Bitrix D7 ORM: DataManager и карта сущности

В Битриксе D7 ORM построена вокруг “табличных” классов (*Table) на базе DataManager.

Схематично (пример “как это выглядит” на уровне идеи):

        use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;

final class PostTable extends DataManager
{
    public static function getTableName(): string
    {
        return 'b_my_post';
    }

    public static function getMap(): array
    {
        return [
            (new IntegerField('ID'))->configurePrimary(true)->configureAutocomplete(true),
            (new StringField('TITLE'))->configureRequired(true),
        ];
    }
}

    

Дальше вы работаете не с “объектом‑записью” (как в Eloquent), а с таблицей/сущностью:

        $result = PostTable::add([
    'TITLE' => 'Привет, D7 ORM',
]);

$id = $result->getId();

    

Философски это ближе к Data Mapper: сущность описывается отдельно, а операции выполняются через менеджер.

Сильные стороны в контексте Битрикса:

  • хорошо “ложится” на модульную архитектуру (таблицы модуля),
  • явная карта полей и валидация,
  • единый стиль запросов (query()/getList()), который встречается и в ядре.

Ограничения и реальность:

  • огромное количество данных в Битриксе живёт не в ваших таблицах, а в “платформенных” сущностях (инфоблоки, корзина/заказы, права),
  • исторически в проектах часто смешиваются: старое API (CIBlockElement, $DB) + D7 + прямой SQL. Это нормально для legacy, но для нового кода лучше выбирать D7 как стандарт.

Миграции и версионирование схемы

Laravel: migrations — стандарт де‑факто

В Laravel миграции — это “истина о схеме БД в репозитории”. Типовой цикл:

  • php artisan make:migration ...
  • php artisan migrate
  • (для разработки) php artisan migrate:fresh --seed

Пример миграции:

        use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body')->nullable();
            $table->foreignId('author_id')->constrained('users');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

    

Это одновременно:

  • документация схемы,
  • способ доставки изменений,
  • база для тестов/сидов/фабрик.

Битрикс: “из коробки” нет единого стандарта миграций (и поэтому важно договориться)

В ядре Битрикса нет универсального механизма миграций уровня Laravel, который “вшит в процесс разработки” во всех проектах.

На практике встречаются подходы:

  • миграции через сторонние инструменты (самый популярный путь в продакшене),
  • SQL/скрипты деплоя (ручная дисциплина),
  • установка/обновление модуля как “точка применения” изменений (частично решает задачу, но хуже подходит для частых итераций).

Из реально распространённых инструментов в Битрикс‑мире:

  • sprint.migration (часто используют в проектах с инфоблоками и платформенными сущностями),
  • arrilot/bitrix-migrations (подход ближе к “миграциям как коду”, но устарел и не обновляется).
Важно В Битриксе миграции — это не “фича фреймворка”, а инженерный процесс, который команда обязана выбрать и закрепить.

Транзакции и консистентность данных

Laravel: DB::transaction()

        use Illuminate\Support\Facades\DB;

DB::transaction(function () use ($data) {
    // несколько записей, которые должны “либо все, либо ни одной”
});

    

Битрикс: транзакции через Connection

        use Bitrix\Main\Application;

$connection = Application::getConnection();
$connection->startTransaction();

try {
    // операции add/update/delete
    $connection->commitTransaction();
} catch (\Throwable $e) {
    $connection->rollbackTransaction();
    throw $e;
}

    

Практический смысл одинаковый: если вы меняете несколько сущностей (например, заказ + позиции + остатки), транзакции — это не “оптимизация”, а базовая защита от поломанного состояния.

Примечание Подробнее о транзакциях в Битрикс можно прочитать в нашей статье Транзакции в Битрикс: атомарные операции без «полусохранений»

“Где умирает производительность”: N+1, джойны и кэш

Laravel: N+1 обычно лечится with()

Если видите цикл по моделям и внутри — обращение к связи ($post->author->name), почти всегда нужно:

  • добавить with('author'),
  • проверить количество запросов,
  • при необходимости — withCount, select и индексы.

Битрикс: N+1 часто появляется на стыке “платформа ↔ ваш код”

В Битриксе N+1 часто возникает, когда:

  • вы получаете список элементов (инфоблок/HL/таблица),
  • а потом в цикле дотягиваете свойства/связанные данные отдельными вызовами.

Лечится комбинацией:

  • D7 Query с Reference/join (когда вы работаете с таблицами),
  • правильной выборкой полей/свойств (когда вы работаете с инфоблоками),
  • и кэшем (в Битриксе кэш — это часть “нормального пути”, особенно на витринах).

Сравнительная таблица: работа с БД

Шкала (как и в прошлых частях): от -2 до +2.

Критерий Laravel (баллы) Битрикс (баллы) Комментарий
Миграции и версионирование схемы +2 -1 В Laravel это стандарт ядра и культуры. В Битриксе нужен выбранный инструмент и дисциплина команды.
ORM “по‑умолчанию” и удобство чтения +2 +1 Eloquent очень дружелюбен. D7 ORM мощный, но тяжелее по синтаксису.
Работа с “платформенными данными” (каталог/заказы/права) 0 +2 В Laravel это вы строите сами или через пакеты. В Битриксе — часть платформы.
Контроль над SQL и гибкость сложных запросов +1 +1 Оба умеют и Query Builder, и “сырые” запросы при необходимости.
Риск деградации слоя данных в legacy 0 -1 В Битриксе часто смешиваются старое API + D7 + SQL: нужна архитектурная политика “что можно/нельзя”.
Тестовые данные (сидеры/фабрики как стандарт) +2 0 В Laravel это часть DX. В Битриксе обычно делают кастомно.
Итого за статью +7 +2
Общий счёт (накопительный) +37 +16 Счёт накапливается по мере выхода статей.

Практика (60–120 минут): “одна и та же предметка” в двух стеках

Цель: почувствовать разницу подходов, а не написать “идеальную архитектуру”.

Задание для Laravel

  1. Сделайте миграции для: authors (id, name) и posts (id, author_id, title, body, timestamps).
  2. Опишите модели Author и Post + связи.
  3. Заполните тестовыми данными через seed/factory (хотя бы 20 постов).
  4. Сделайте endpoint (или страницу), который отдаёт список постов с автором, без N+1, с пагинацией.

Задание для Битрикса

Вариант “по‑взрослому” (рекомендуется):

  1. Создайте модуль vendor.module и заведите таблицы b_vendor_author, b_vendor_post (через выбранный миграционный инструмент).
  2. Опишите AuthorTable и PostTable (D7 ORM).
  3. Сделайте выборку 20 постов с авторами через один запрос (join/Reference).
  4. Отдайте результат через D7‑роутинг или D7‑контроллер (JSON).

Вариант “как часто в проектах” (не привыкайте так делать... только, чтобы понимать как может встретиться на проекте):

  1. Описать таблицы и *Table в local/lib/,
  2. но зафиксировать в проекте правило миграций и структуру кода, чтобы не превратить /local в свалку.

Вывод: Laravel даёт “культуру данных из коробки”, Битрикс — “данные платформы”

Laravel сильнее там, где вы строите приложение вокруг своей модели данных: миграции, Eloquent, сиды/фабрики — это единый поток разработки.

Битрикс сильнее там, где проект опирается на платформенные сущности и коробочные модули (контент, права, магазин) — но для “инженерного” слоя данных (ваши таблицы) вам нужно выбрать и закрепить процесс миграций и правила D7.

В следующей части перейдём к тому, как данные превращаются в UI: шаблонизация и представления (Blade vs шаблоны Битрикса и компонентный подход).

Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.