Query Builder: SELECT

Выборка данных — основа работы с базой. Конструктор запросов предоставляет множество методов для гибкой выборки данных из базы. В зависимости от задачи вы можете получить одну запись, список, конкретное значение или выполнить сложный запрос с условиями и соединениями.

Выборка данных

get($columns = ['*'])

Выполняет запрос и возвращает массив объектов stdClass или, если указана модель, массив объектов модели.

use Imhotep\Facades\DB;

// Возвращает все колонки (*)
$users = DB::table('users')->get();

// Выборка определенных колонок
$users = DB::table('users')->get(['name', 'email']);
PHP

first($columns = ['*'])

Возвращает первую запись из результата или null.

$user = DB::table('users')->where('id', 1)->first();
PHP

firstOr($columns = ['*'], Closure $callback)

Возвращает первую запись или выполняет callback, если запись не найдена.

$user = DB::table('users')->firstOr(['name', 'email'], function () {
    return 'Default User';
});
PHP

firstOrFail($columns = ['*'], string $message = null)

Возвращает первую запись или выбрасывает исключение RecordNotFoundException.

$user = DB::table('users')->where('email', 'test@test.com')->firstOrFail();
PHP

sole($columns = ['*'])

Возвращает единственную запись. Если найдено более одной записи, выбрасывается исключение MultipleRecordsFoundException. Если записей нет — RecordNotFoundException.

$user = DB::table('users')->where('email', 'unique@test.com')->sole();
PHP

find($id, $columns = ['*'])

Находит запись по первичному ключу (по колонке id).

$user = DB::table('users')->find(1);
PHP

findOr($id, $columns = ['*'], Closure $callback)

Находит запись по ключу или выполняет callback.

$user = DB::table('users')->findOr(1, function () {
    return 'User not found';
});
PHP

findOrFail($id, $columns = ['*'], string $message = null)

Находит запись по ключу или выбрасывает исключение RecordNotFoundException.

$user = DB::table('users')->findOrFail(1);
PHP

value($column, $default = null)

Возвращает значение указанной колонки из первой записи.

$email = DB::table('users')->where('id', 1)->value('email');
PHP

soleValue($column)

Возвращает значение колонки из единственной записи или выбрасывает исключение.

$email = DB::table('users')->where('email', 'unique@test.com')->soleValue('email');
PHP

pluck($column, $key = null)

Возвращает массив значений указанной колонки. Можно указать колонку для ключей массива.

$names = DB::table('users')->pluck('name');
// ['John', 'Jane', ...]

$users = DB::table('users')->pluck('name', 'id');
// [1 => 'John', 2 => 'Jane', ...]
PHP

rawValue($expression, $bindings = [], $default = null)

Выполняет сырое SQL-выражение и возвращает его значение.

$count = DB::table('users')->rawValue('COUNT(*)');
PHP

exists()

Проверяет, существуют ли записи, соответствующие запросу.

$exists = DB::table('users')->where('active', true)->exists();
PHP

missing()

Проверяет, отсутствуют ли записи, соответствующие запросу (противоположность exists).

$missing = DB::table('users')->where('deleted_at', 'IS NOT NULL')->missing();
PHP

Агрегатные функции

  • count($column) — возвращает количество записей
  • min($column) — возвращает минимальное значение поля
  • max($column) — возвращает максимальное значение поля
  • avg($column) — возвращает среднее значение поля
  • sum($column) — возвращает сумму значений поля

Выборка колонок

Метод select позволяет указать, какие именно столбцы должны быть включены в результат запроса, это позволит:

  • Уменьшить объём передаваемых данных
  • Исключить конфиденциальные поля (пароли, токены)

Укажите нужные колонки в виде массива или через запятую:

// SELECT name, email, age FROM users
$users = DB::table('users')->select(['name', 'email', 'age'])->get();

// SELECT name, email, age FROM users
$users = DB::table('users')->select('name', 'email', 'age')->get();
PHP

Алиасы колонок

Алиасы позволяют переименовывать столбцы в результатах выборки. Это полезно, когда исходное имя колонки слишком длинное или неудобное:

// SELECT name as full_name, email as user_email FROM users
$users = DB::table('users')
    ->select('name as full_name', 'email as user_email')
    ->get();

// SELECT name, YEAR(created_at) as year FROM users
$users = DB::table('users')
    ->select('name', DB::raw('YEAR(created_at) as year'))
    ->get();
PHP

Выражения в SELECT

Для использования SQL-выражений, агрегатных функций или вычисляемых полей применяйте selectRaw или DB::raw:

use Imhotep\Facades\DB;

// SELECT COUNT(*) as total, YEAR(created_at) as year FROM users GROUP BY year
$users = DB::table('users')
    ->selectRaw('COUNT(*) as total, YEAR(created_at) as year')
    ->groupBy('year')
    ->get();

// SELECT id, name, age > 18 as is_adult FROM users
$users = DB::table('users')
    ->selectRaw('id, name, age > ? as is_adult', [18])
    ->get();
PHP

Использование привязок (?) защищает от SQL-инъекций — это обязательная практика при подстановке пользовательских данных.

Подзапросы в SELECT

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

Метод selectSub принимает два аргумента, первым сам запрос в виде замыкания или экземпляр Query Builder, вторым название колонки в результатах выборки:

use Imhotep\Facades\DB;

$users = DB::table('users')
    ->select('name')
    ->selectSub(function ($query) {
        $query->from('posts')
            ->whereColumn('posts.user_id', 'users.id')
            ->selectRaw('COUNT(*)');
    }, 'posts_count')
    ->get();

// SELECT name, (SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) as posts_count FROM users
PHP

С использованием готового запроса:

$subQuery = DB::table('orders')
    ->where('status', 'completed')
    ->whereColumn('orders.user_id', 'users.id')
    ->selectRaw('COUNT(*)');

$users = DB::table('users')
    ->select('id', 'name')
    ->selectSub($subQuery, 'completed_orders')
    ->get();

// SELECT id, name, (SELECT COUNT(*) FROM orders WHERE status = 'completed' AND orders.user_id = users.id) as completed_orders FROM users
PHP

Уникальные значения (DISTINCT)

Метод distinct исключает дублирующиеся строки в результатах выборки. Особенно полезен, когда нужно получить список уникальных значений из колонки, содержащей повторяющиеся данные:

// SELECT DISTINCT city FROM users
$cities = DB::table('users')->distinct()->select('city')->get();

// SELECT DISTINCT city, country FROM users
$locations = DB::table('users')->distinct()->select('city', 'country')->get();
PHP

Условия выборки (WHERE)

Для фильтрации данных существует набор методов where, позволяющих гибко задавать условия:

Метод Описание
where() Базовое условие сравнения
whereRaw() Произвольное SQL-выражение
whereColumn() Сравнение двух колонок
whereNull(), whereNotNull() Проверка на NULL
whereTrue(), whereFalse() Проверка на true/false (1 или 0)
whereIn(), whereNotIn() Проверка вхождения в массив
whereLike(), whereNotLike() Поиск по шаблону
whereDate(), whereYear(), whereMonth(), whereDay(), whereTime(), whereHour(), whereMinute(), whereSecond() Сравнение с датой и временем
whereBetween(), whereNotBetween() Проверка вхождения в диапазон
whereBetweenColumns(), whereNotBetweenColumns() Проверка между колонками
whereExists(), whereNotExists() Проверка существования в подзапросе

Каждый метод имеет парный метод orWhere для создания составных условий с логическим ИЛИ:

$users = DB::table('users')
    ->where('active', true)
    ->orWhereNull('deleted_at')
    ->orWhereIn('role', ['admin', 'moderator'])
    ->orWhereLike('name', '%guest%')
    ->orWhereBetween('age', [18, 25])
    ->orWhereExists(function($query) {
        $query->from('subscriptions')
              ->whereColumn('users.id', 'subscriptions.user_id');
    })
    ->get();

// SELECT * FROM users 
// WHERE active = 1 
// OR deleted_at IS NULL 
// OR role IN ('admin', 'moderator') 
// OR name LIKE '%guest%' 
// OR age BETWEEN 18 AND 25 
// OR EXISTS (SELECT * FROM subscriptions WHERE users.id = subscriptions.user_id)
PHP

Базовые условия

С помощью метода where вы можете задавать базовые условия фильтрации в различных форматах:

Формат 1: Строка с условием (парсится автоматически)

$users = DB::table('users')->where('city = Moscow')->get();
$users = DB::table('users')->where('age > 18')->get();
PHP

Формат 2: Два параметра (колонка + значение, оператор =)

$users = DB::table('users')->where('active', true)->get();
$users = DB::table('users')->where('city', 'Moscow')->get();
PHP

Формат 3: Три параметра (колонка + оператор + значение)

$users = DB::table('users')->where('age', '>', 18)->get();
$users = DB::table('users')->where('name', 'LIKE', '%John%')->get();
$users = DB::table('users')->where('created_at', '>=', '2023-01-01')->get();
PHP

Формат 4: Массив условий

$users = DB::table('users')
    ->where([
        'active' => true,  // точное сравнение (=)
        ['age', '>', 18],  // сравнение с оператором
        'city' => 'Moscow' // точное сравнение (=)
    ])
    ->get();
PHP

Сырые условия

Если необходимо передать сложное условие в виде обычного SQL, используйте метод whereRaw:

// Простое сырое условие
DB::table('users')->whereRaw('YEAR(age) = 2000')->get();

// С подстановкой параметров (безопасно)
DB::table('users')->whereRaw('YEAR(age) = ?', [2000])->get();

// Несколько параметров
DB::table('users')->whereRaw('age > ? AND city = ?', [18, 'Moscow'])->get();

// Альтернативный способ через DB::raw()
DB::table('users')->where(DB::raw('YEAR(age) = 2000'))->get();
PHP

Группировка условий

Для группировки условий в одно логическое выражение используется замыкание внутри where:

$users = DB::table('users')
    ->where('active', true)
    ->where(function ($query) {
        $query->where('age', '<', 18)
              ->orWhere('age', '>', 65);
    })
    ->get();

// SELECT * FROM users WHERE active = 1 AND (age < 18 OR age > 65)
PHP

Пример с вложенной группировкой:

$users = DB::table('users')
    ->where(function ($query) {
        $query->where('active', true)
              ->where(function ($subQuery) {
                  $subQuery->where('age', '>', 18)
                           ->where('city', 'Moscow');
              });
    })
    ->orWhere('role', 'admin')
    ->get();

// SELECT * FROM users 
// WHERE (active = 1 AND (age > 18 AND city = 'Moscow')) OR role = 'admin'
PHP

Условия с подзапросом

Метод where (с передачей замыкания в качестве значения) позволяет использовать подзапросы:

// Сравнение с результатом подзапроса
$users = DB::table('users')
    ->where('age', function($query) {
        $query->from('settings')
              ->select('value')
              ->where('key', 'default_age');
    })
    ->get();

// или
$users = DB::table('users')
    ->where('age', '>', function($query) {
        $query->from('settings')
              ->select('value')
              ->where('key', 'default_age');
    })
    ->get();
PHP

Сравнение колонок

Метод whereColumn сравнивает значения двух колонок между собой:

// Простое сравнение колонок
$users = DB::table('users')
    ->whereColumn('created_at', 'updated_at')
    ->get();

// Сравнение с оператором
$users = DB::table('users')
    ->whereColumn('price', '>', 'discount_price')
    ->get();

// Массив сравнений
$users = DB::table('users')
    ->whereColumn([
        ['created_at', '>=', 'updated_at'],
        ['stock', '>', 'min_stock']
    ])
    ->get();
PHP

Between условия

Методы whereBetween и whereNotBetween проверяют вхождение значения в диапазон:

// Проверка вхождения в диапазон
$users = DB::table('users')
    ->whereBetween('age', [18, 65])
    ->get();

// Проверка вне диапазона
$users = DB::table('users')
    ->whereNotBetween('age', [18, 65])
    ->get();

// Сравнение колонки с диапазоном между двумя другими колонками
$users = DB::table('users')
    ->whereBetweenColumns('age', ['min_age', 'max_age'])
    ->orWhereBetweenColumns('price', ['discount_price', 'regular_price'])
    ->get();

// С OR-условиями
$users = DB::table('users')
    ->whereBetween('age', [20, 25])
    ->orWhereBetween('age', [30, 35])
    ->get();
PHP

Проверка на NULL

// Проверка на NULL
$users = DB::table('users')->whereNull('deleted_at')->get();

// Проверка на NOT NULL
$users = DB::table('users')->whereNotNull('email_verified_at')->get();

// Проверка нескольких колонок
$users = DB::table('users')
    ->whereNull(['deleted_at', 'archived_at']) 
    ->get();

// OR условия
$users = DB::table('users')
    ->whereNull('deleted_at')
    ->orWhereNotNull('archived_at')
    ->get();
PHP

Проверка на true/false

Для булевых значений можно использовать краткую запись:

// Проверка на true
$users = DB::table('users')->where('active', true)->get();
$users = DB::table('users')->whereTrue('active')->get();

// Проверка на false
$users = DB::table('users')->where('active', false)->get();
$users = DB::table('users')->whereFalse('active')->get();
PHP

Поиск по списку

Метод whereIn позволяет выполнять поиск по переданному списку значений или подзапросу:

// С массивом значений
$users = DB::table('users')
    ->whereIn('role', ['admin', 'moderator'])
    ->get();

// С подзапросом
$users = DB::table('users')
    ->whereIn('id', function($query) {
        $query->from('orders')
              ->select('user_id')
              ->where('status', 'active');
    })
    ->get();
PHP

Поиск по тексту

Методы whereLike и whereNotLike выполняют текстовый поиск с поддержкой регистра:

// Регистронезависимый поиск (по умолчанию)
$users = DB::table('users')
    ->whereLike('name', '%john%')
    ->get();

// Регистрозависимый поиск
$users = DB::table('users')
    ->whereLike('name', '%John%', true)
    ->get();

// Поиск с точным совпадением начала строки
$users = DB::table('users')
    ->whereLike('name', 'John%')
    ->get();

// Исключение по шаблону
$users = DB::table('users')
    ->whereNotLike('name', '%admin%')
    ->get();

// OR условие с LIKE
$users = DB::table('users')
    ->where('active', true)
    ->orWhereLike('name', '%guest%')
    ->get();
PHP

Дата и время

Специализированные методы для работы с датами и временем:

Сравнение даты

$users = DB::table('users')
    ->whereDate('created_at', '2023-01-01')
    ->orWhereDate('updated_at', '>', '2023-01-05')
    ->get();
PHP

Сравнение года

$users = DB::table('users')
    ->whereYear('created_at', '2023')
    ->orWhereYear('created_at', '>=', '2020')
    ->get();
PHP

Сравнение месяца

$users = DB::table('users')
    ->whereMonth('created_at', '>', 6)
    ->get();
PHP

Сравнение дня

$users = DB::table('users')
    ->whereDay('created_at', 15)
    ->get();
PHP

Сравнение времени

// C учетом секунд
$users = DB::table('users')
    ->whereTime('created_at', '>', '12:00:00')
    ->get();

// Без учета секунд
$users = DB::table('users')
    ->whereTime('created_at', '=', '20:00')
    ->get();
PHP

Сравнение часа

$users = DB::table('users')
    ->whereHour('created_at', '>=', 9)
    ->get();
PHP

Сравнение минуты

$users = DB::table('users')
    ->whereMinute('created_at', 30)
    ->get();
PHP

Сравнение секунды

$users = DB::table('users')
    ->whereSecond('created_at', 0)
    ->get();
PHP

Проверка наличия (Exists)

Методы whereExists и whereNotExists проверяют существование записей в подзапросе:

// Проверка существования связанных записей
$users = DB::table('users')
    ->whereExists(function($query) {
        $query->from('orders')
              ->whereColumn('users.id', 'orders.user_id')
              ->where('orders.status', 'active');
    })
    ->get();

// Проверка отсутствия связанных записей
$users = DB::table('users')
    ->whereNotExists(function($query) {
        $query->from('orders')
              ->whereColumn('users.id', 'orders.user_id');
    })
    ->get();

// OR EXISTS
$users = DB::table('users')
    ->where('active', true)
    ->orWhereExists(function($query) {
        $query->from('subscriptions')
              ->whereColumn('users.id', 'subscriptions.user_id')
              ->where('subscriptions.active', true);
    })
    ->get();
PHP

Работа с JSON

Polymorphic условия (Morph)

Метод whereMorph упрощает работу с полиморфными связями, в качестве значения принимает объект модели:

$post = Post::find(1);
$anyPost = Post::find(2);

$users = DB::table('comments')
    ->whereMorph('commentable', $post)
    ->orWhereMorph('commentable', $anyPost)
    ->get();
PHP

Соединения (JOIN)

Соединения позволяют объединять данные из нескольких таблиц. Поддерживаются все основные типы JOIN:

Тип Метод Описание
INNER JOIN join() Возвращает только совпадающие записи
LEFT JOIN leftJoin() Все записи из левой таблицы + совпадающие из правой
RIGHT JOIN rightJoin() Все записи из правой таблицы + совпадающие из левой
OUTER JOIN outerJoin() Все записи из обеих таблиц

Примеры использования:

// INNER JOIN
$users = DB::table('users')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->select('users.name', 'posts.title')
    ->get();

// LEFT JOIN с несколькими условиями
$users = DB::table('users')
    ->leftJoin('orders', function($join) {
        $join->on('users.id', '=', 'orders.user_id')
             ->where('orders.status', '=', 'active');
    })
    ->select('users.*', 'orders.total')
    ->get();

// RIGHT JOIN
$users = DB::table('users')
    ->rightJoin('roles', 'users.role_id', '=', 'roles.id')
    ->get();

// OUTER JOIN (FULL OUTER JOIN)
$users = DB::table('users')
    ->outerJoin('profiles', 'users.id', '=', 'profiles.user_id')
    ->get();
PHP

Сложные условия JOIN

// JOIN с подзапросом
$users = DB::table('users')
    ->join('orders', function($join) {
        $join->on('users.id', '=', 'orders.user_id')
             ->where('orders.created_at', '>', '2023-01-01');
    })
    ->get();

// JOIN с несколькими условиями
$users = DB::table('users')
    ->join('orders', function($join) {
        $join->on('users.id', '=', 'orders.user_id')
             ->on('users.status', '=', 'orders.status');
    })
    ->get();

// OR условие в JOIN
$users = DB::table('users')
    ->join('orders', function($join) {
        $join->on('users.id', '=', 'orders.user_id')
             ->orOn('users.id', '=', 'orders.created_by');
    })
    ->get();
PHP

JOIN с подзапросом

// JOIN с подзапросом вместо таблицы
$latestOrders = DB::table('orders')
    ->select('user_id', DB::raw('MAX(created_at) as latest_order'))
    ->groupBy('user_id');

$users = DB::table('users')
    ->joinSub($latestOrders, 'latest_orders', function($join) {
        $join->on('users.id', '=', 'latest_orders.user_id');
    })
    ->select('users.*', 'latest_orders.latest_order')
    ->get();

// Или через замыкание
$users = DB::table('users')
    ->joinSub(function($query) {
        $query->from('orders')
              ->select('user_id', DB::raw('MAX(created_at) as latest_order'))
              ->groupBy('user_id');
    }, 'latest_orders', function($join) {
        $join->on('users.id', '=', 'latest_orders.user_id');
    })
    ->get();
PHP

Сортировка данных (ORDER BY)

Метод orderBy сортирует результаты по указанному столбцу, по возрастанию:

// Сортировка по возрастанию
$users = DB::table('users')
    ->orderBy('name')
    ->get();

// Множественная сортировка
$users = DB::table('users')
    ->orderBy('last_name')
    ->orderBy('first_name')
    ->orderBy('created_at', 'desc')
    ->get();

// Сортировка по выражению
$users = DB::table('users')
    ->orderBy(DB::raw('LENGTH(name)'))
    ->get();
PHP

Для сортировки по убыванию, передайте вторым аргументом 'desc' или используйте метод orderByDesc:

$users = DB::table('users')
    ->orderByDesc('created_at')
    ->orderByDesc(['updated_at', 'created_at'])
    ->get();

// Эквивалентно
$users = DB::table('users')
    ->orderBy('created_at', 'desc')
    ->get();
PHP

Ограничение записей (LIMIT и OFFSET)

// Ограничение количества записей
$users = DB::table('users')
    ->limit(10)
    ->get();

// Пропуск записей (OFFSET)
$users = DB::table('users')
    ->offset(20)
    ->limit(10)
    ->get();

// Взятие первых N записей (take)
$users = DB::table('users')
    ->take(5)
    ->get();
PHP

Группировка и HAVING

Группировка позволяет агрегировать данные по определенным критериям, а HAVING — фильтровать результаты после группировки.

Группировка (GROUP BY)

// Простая группировка
$users = DB::table('users')
    ->select('city', DB::raw('COUNT(*) as total'))
    ->groupBy('city')
    ->get();

// Группировка по нескольким колонкам
$users = DB::table('users')
    ->select('city', 'status', DB::raw('COUNT(*) as total'))
    ->groupBy('city', 'status')
    ->get();

// Группировка через массив
$users = DB::table('users')
    ->select('city', DB::raw('AVG(age) as avg_age'))
    ->groupBy(['city'])
    ->get();
PHP

Для сложных выражений используйте groupByRaw:

$users = DB::table('users')
    ->select(DB::raw('YEAR(created_at) as year'), DB::raw('COUNT(*) as total'))
    ->groupByRaw('YEAR(created_at)')
    ->get();

// С привязками параметров
$users = DB::table('users')
    ->select(DB::raw('DATE_FORMAT(created_at, ?) as month'), DB::raw('COUNT(*) as total'))
    ->groupByRaw('DATE_FORMAT(created_at, ?)', ['%Y-%m'])
    ->get();
PHP

Фильтрация групп (HAVING)

HAVING позволяет фильтровать результаты после группировки, в отличие от WHERE, который фильтрует до группировки:

// Базовое условие HAVING
$users = DB::table('users')
    ->select('city', DB::raw('COUNT(*) as total'))
    ->groupBy('city')
    ->having('total', '>', 10)
    ->get();

// HAVING с OR условием
$users = DB::table('orders')
    ->select('user_id', DB::raw('COUNT(*) as count_orders'), DB::raw('SUM(amount) as total'))
    ->groupBy('user_id')
    ->having('count_orders', '>', 5)
    ->orHaving('total', '>', 10000)
    ->get();
PHP

Для сложных условий используйте havingRaw:

// Сырое выражение
$users = DB::table('orders')
    ->select('user_id', DB::raw('SUM(amount) as total'))
    ->groupBy('user_id')
    ->havingRaw('SUM(amount) > ?', [1000])
    ->get();

// С OR условием
$users = DB::table('orders')
    ->select('user_id', DB::raw('COUNT(*) as count_orders'))
    ->groupBy('user_id')
    ->havingRaw('COUNT(*) > 10')
    ->orHavingRaw('COUNT(*) = 0')
    ->get();
PHP

Сложные логические условия группируются через замыкание:

$users = DB::table('orders')
    ->select('user_id', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count_orders'))
    ->groupBy('user_id')
    ->having(function($query) {
        $query->having('total', '>', 1000)
              ->orHaving('count_orders', '>', 10);
    })
    ->get();

// Вложенные условия с AND/OR
$users = DB::table('orders')
    ->select('user_id', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count_orders'))
    ->groupBy('user_id')
    ->having(function($query) {
        $query->having('total', '>', 5000)
              ->having(function($subQuery) {
                  $subQuery->having('count_orders', '>', 3)
                           ->orHaving('count_orders', '<', 1);
              });
    })
    ->get();
PHP

UNION: Объединение запросов

UNION позволяет объединить результаты двух или более запросов в один результирующий набор. Методы union и unionAll дают гибкую возможность комбинировать данные из разных таблиц или запросов.

use Imhotep\Facades\DB;

// Можно передать конструктор запроса
$users = DB::table('users')
    ->select('name', 'email')
    ->where('status', 'active')
    ->union(
        DB::table('users_archived')
            ->select('name', 'email')
            ->where('status', 'active')
    )
    ->get();

// Или через замыкание
$users = DB::table('users')
    ->select('name', 'email')
    ->unionAll(function ($query) {
        $query->from('users_archived')->select('name', 'email')
    })
    ->get();

// Для сложных случаев можно передать готовый SQL
$users = DB::table('users')
    ->select('name', 'email')
    ->where('active', true)
    ->union(
        "SELECT name, email FROM archived_users WHERE active = ?",
        [true] // Привязки параметров
    )
    ->get();
PHP

Блокировки (Locking)

Блокировки используются для управления конкурентным доступом к данным в транзакциях. Они помогают предотвратить состояние гонки (race conditions) при одновременной записи и чтении данных.

Важно: Блокировки работают только внутри транзакций. Убедитесь, что вы обернули запросы с блокировками в DB::transaction() или используете их внутри транзакции базы данных.

lockForUpdate() — пессимистичная блокировка на запись

Захватывает блокировку для строк, которые будут обновляться. Другие транзакции не смогут изменить или заблокировать эти строки до завершения текущей транзакции.

Посмотреть пример...
use Imhotep\Facades\DB;

DB::transaction(function () {
    // Получаем пользователя с блокировкой для обновления
    $user = DB::table('users')
        ->where('id', 1)
        ->lockForUpdate()
        ->first();

    // Теперь мы можем безопасно обновить данные
    DB::table('users')
        ->where('id', $user->id)
        ->update(['balance' => $user->balance - 100]);
});
PHP

sharedLock() — общая блокировка на чтение

Захватывает блокировку для чтения данных, которые не должны измениться во время обработки. Другие транзакции также могут читать эти строки, но не могут изменять их до снятия блокировки.

Посмотреть пример...
use Imhotep\Facades\DB;

DB::transaction(function () {
    // Получаем данные с общей блокировкой
    $report = DB::table('orders')
        ->where('status', 'pending')
        ->sharedLock()
        ->get();

    // Формируем отчёт на основе актуальных данных
    // Другие транзакции не смогут их изменить, пока мы читаем
});
PHP

lock() — пользовательская блокировка

Позволяет передать произвольное выражение блокировки, специфичное для вашей БД.

Посмотреть пример...
use Imhotep\Facades\DB;

DB::transaction(function () {
    // MySQL / PostgreSQL: блокировка с ожиданием
    $users = DB::table('users')
        ->where('active', true)
        ->lock('FOR UPDATE NOWAIT')  // Не ждать, если строка заблокирована
        ->get();
    
    // PostgreSQL: пропустить заблокированные строки
    $users = DB::table('users')
        ->where('status', 'pending')
        ->lock('FOR UPDATE SKIP LOCKED')
        ->get();
    
    // MySQL: блокировка только на чтение
    $users = DB::table('users')
        ->lock('LOCK IN SHARE MODE')
        ->get();
    
    // Обработка данных...
});
PHP

lock(false) — отключение блокировки

Явно отключает любую блокировку для запроса. Используется для переопределения блокировок, установленных через модель или глобально.

Посмотреть пример...
use Imhotep\Facades\DB;

DB::transaction(function () {
    // Базовый запрос с блокировкой
    $baseQuery = DB::table('users')->lockForUpdate();
    
    // Для получения статистики блокировка не нужна
    $stats = $baseQuery->lock(false)->selectRaw('count(*) as total')->first();
    
    // Для обновления используем блокировку из базового запроса
    $users = $baseQuery->where('active', true)->get();
});
PHP