1. База данных
  2. База данных: Конструктор запросов

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

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

Конструктор запросов Laravel использует привязку параметров PDO для защиты вашего приложения от атак SQL-инъекций. Нет необходимости очищать или санитаризировать строки, передаваемые построителю запросов, как привязки запросов.

Внимание PDO не поддерживает привязку имен столбцов. Поэтому никогда не допускайте ввод пользовательских данных для определения имен столбцов, на которые ссылаются ваши запросы, включая столбцы "order by".

Выполнение запросов к базе данных

Получение всех строк из таблицы

Вы можете использовать метод table предоставленный фасадом DB для начала запроса. Метод table возвращает экземпляр построителя запросов для данной таблицы, позволяя вам добавлять более многочисленные ограничения к запросу, а затем, наконец, извлекать результаты запроса, используя метод get:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Показать список всех пользователей приложения.
*/
public function index(): View
{
$users = DB::table('users')->get();
 
return view('user.index', ['users' => $users]);
}
}

Метод get возвращает экземпляр Illuminate\Support\Collection, содержащий результаты запроса, где каждый результат представляет собой экземпляр объекта PHP stdClass. Вы можете получить доступ к значению каждого столбца, обращаясь к столбцу как к свойству объекта:

use Illuminate\Support\Facades\DB;
 
$users = DB::table('users')->get();
 
foreach ($users as $user) {
echo $user->name;
}

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

Получение одной строки или одного столбца из таблицы

Если вам просто нужно извлечь одну строку из таблицы базы данных, вы можете использовать метод first фасада DB. Этот метод вернет один объект stdClass:

$user = DB::table('users')->where('name', 'John')->first();
 
return $user->email;

Если вам не нужна вся строка, вы можете извлечь единственное значение из записи с использованием метода value. Этот метод вернет значение столбца напрямую:

$email = DB::table('users')->where('name', 'John')->value('email');

Для получения одной строки по значению столбца id используйте метод find:

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

Получение списка значений столбца

Если вы хотите получить экземпляр Illuminate\Support\Collection, содержащий значения одного столбца, вы можете использовать метод pluck. В этом примере мы получим коллекцию заголовков пользователей:

use Illuminate\Support\Facades\DB;
 
$titles = DB::table('users')->pluck('title');
 
foreach ($titles as $title) {
echo $title;
}

Вы можете указать столбец, который должен использоваться в качестве ключей результирующей коллекции, предоставив второй аргумент методу pluck:

$titles = DB::table('users')->pluck('title', 'name');
 
foreach ($titles as $name => $title) {
echo $title;
}

Получение результатов пакетами

Если вам нужно работать с тысячами записей в базе данных, рассмотрите использование метода chunk, предоставляемого фасадом DB. Этот метод извлекает небольшой кусок результатов за раз и передает каждый кусок в замыкание для обработки. Например, давайте извлечем все записи из таблицы users порциями по 100 записей за раз:

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
 
DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) {
foreach ($users as $user) {
// ...
}
});

Вы можете предотвратить обработку дополнительных порций, вернув false из замыкания:

DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) {
// Обработка записей...
 
return false;
});

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

DB::table('users')->where('active', false)
->chunkById(100, function (Collection $users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});

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

Получение результатов потоком

Метод lazy работает аналогично методу chunk в том смысле, что выполняет запрос порциями. Однако вместо передачи каждой порции в обратный вызов, метод lazy() возвращает LazyCollection, который позволяет вам взаимодействовать с результатами как с одним потоком:

use Illuminate\Support\Facades\DB;
 
DB::table('users')->orderBy('id')->lazy()->each(function (object $user) {
// ...
});

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

DB::table('users')->where('active', false)
->lazyById()->each(function (object $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
});

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

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

Конструктор запросов также предоставляет разнообразные методы для извлечения агрегированных значений, таких как count, max, min, avg и sum. Вы можете вызвать любой из этих методов после построения вашего запроса:

use Illuminate\Support\Facades\DB;
 
$users = DB::table('users')->count();
 
$price = DB::table('orders')->max('price');

Конечно же, вы можете комбинировать эти методы с другими условиями для тонкой настройки того, как рассчитывается ваше агрегированное значение:

$price = DB::table('orders')
->where('finalized', 1)
->avg('price');

Определение наличия записей

Вместо использования метода count для определения того, существуют ли записи, соответствующие вашим условиям запроса, вы можете использовать методы exists и doesntExist:

if (DB::table('orders')->where('finalized', 1)->exists()) {
// ...
}
 
if (DB::table('orders')->where('finalized', 1)->doesntExist()) {
// ...
}

Операторы SELECT

Указание выражения SELECT

Возможно, вам не всегда нужно выбирать все столбцы из таблицы базы данных. Используя метод select, вы можете указать пользовательский "select" клоз для запроса:

use Illuminate\Support\Facades\DB;
 
$users = DB::table('users')
->select('name', 'email as user_email')
->get();

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

$users = DB::table('users')->distinct()->get();

Если у вас уже есть экземпляр построителя запросов и вы хотите добавить столбец к его существующему "select" клозу, вы можете использовать метод addSelect:

$query = DB::table('users')->select('name');
 
$users = $query->addSelect('age')->get();

Сырые выражения

Иногда вам может потребоваться вставить произвольную строку в запрос. Для создания сырого строкового выражения вы можете использовать метод raw, предоставленный фасадом DB:

$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();

Внимание Сырые операторы будут внедрены в запрос в виде строк, поэтому вы должны быть чрезвычайно осторожны, чтобы избежать создания уязвимостей для SQL-инъекций.

Сырые методы

Вместо использования метода DB::raw, вы также можете использовать следующие методы для вставки сырого выражения в различные части вашего запроса. Помните, что Laravel не может гарантировать, что любой запрос с использованием сырых выражений защищен от уязвимостей SQL-инъекций.

selectRaw

Метод selectRaw можно использовать вместо addSelect(DB::raw(/* ... */)). Этот метод принимает необязательный массив привязок в качестве второго аргумента:

$orders = DB::table('orders')
->selectRaw('price * ? as price_with_tax', [1.0825])
->get();

whereRaw / orWhereRaw

Методы whereRaw и orWhereRaw могут быть использованы для вставки сырого "where" выражения в ваш запрос. Эти методы принимают необязательный массив привязок в качестве своего второго аргумента:

$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();

havingRaw / orHavingRaw

Методы havingRaw и orHavingRaw могут быть использованы для предоставления сырой строки в качестве значения выражения "having". Эти методы также принимают необязательный массив привязок в качестве второго аргумента:

$orders = DB::table('orders')
->select('department', DB::raw('SUM(price) as total_sales'))
->groupBy('department')
->havingRaw('SUM(price) > ?', [2500])
->get();

orderByRaw

Метод orderByRaw может быть использован для предоставления сырой строки в качестве значения выражения "order by":

$orders = DB::table('orders')
->orderByRaw('updated_at - created_at DESC')
->get();

groupByRaw

Метод groupByRaw может быть использован для предоставления сырой строки в качестве значения выражения group by:

$orders = DB::table('orders')
->select('city', 'state')
->groupByRaw('city, state')
->get();

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

Оператор Inner Join

Конструктор запросов также может использоваться для добавления операторов соединения к вашим запросам. Для выполнения базового "inner join" вы можете использовать метод join на экземпляре построителя запросов. Первый аргумент, переданный методу join, - это имя таблицы, к которой вы хотите присоединиться, а оставшиеся аргументы указывают ограничения столбцов для соединения. Вы можете даже присоединить несколько таблиц в одном запросе:

use Illuminate\Support\Facades\DB;
 
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();

Операторы Left Join / Right Join

Если вы хотите выполнить "left join" или "right join" вместо "inner join", используйте методы leftJoin или rightJoin. Эти методы имеют тот же формат, что и метод join:

$users = DB::table('users')
->leftJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
 
$users = DB::table('users')
->rightJoin('posts', 'users.id', '=', 'posts.user_id')
->get();

Оператор Cross Join

Метод crossJoin может быть использован для выполнения "cross join". Cross join создает декартово произведение между первой таблицей и присоединенной таблицей:

$sizes = DB::table('sizes')
->crossJoin('colors')
->get();

Расширенные операторы Join

Вы также можете указать более сложные условия соединения. Для начала передайте замыкание в качестве второго аргумента методу join. Замыкание получит экземпляр Illuminate\Database\Query\JoinClause, который позволяет указать ограничения в выражении "join":

DB::table('users')
->join('contacts', function (JoinClause $join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */);
})
->get();

Если вы хотите использовать условие "where" для ваших соединений, вы можете использовать методы where и orWhere, предоставленные экземпляром JoinClause. Вместо сравнения двух столбцов эти методы сравнивают столбец с значением:

DB::table('users')
->join('contacts', function (JoinClause $join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();

Подзапросы в соединениях

Методы joinSub, leftJoinSub и rightJoinSub могут быть использованы для присоединения запроса к подзапросу. Каждый из этих методов принимает три аргумента: подзапрос, его псевдоним таблицы и замыкание, которое определяет связанные столбцы. В этом примере мы получим коллекцию пользователей, где каждая запись пользователя также содержит метку времени created_at последней опубликованной статьи блога пользователя:

$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
->where('is_published', true)
->groupBy('user_id');
 
$users = DB::table('users')
->joinSub($latestPosts, 'latest_posts', function (JoinClause $join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();

Unions (Объединения)

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

use Illuminate\Support\Facades\DB;
 
$first = DB::table('users')
->whereNull('first_name');
 
$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();

В дополнение к методу union Конструктор запросов предоставляет метод unionAll. Запросы, объединенные с использованием метода unionAll, не будут иметь удаленных повторяющихся результатов. Метод unionAll имеет тот же формат, что и метод union.

Основные условия Where

Условия Where

Вы можете использовать метод where построителя запросов для добавления выражений "where" в запрос. Самый базовый вызов метода where требует три аргумента. Первый аргумент - это имя столбца. Второй аргумент - оператор, который может быть любым из поддерживаемых базой данных операторов. Третий аргумент - это значение, с которым будет сравниваться значение столбца.

Например, следующий запрос извлекает пользователей, где значение столбца votes равно 100, а значение столбца age больше 35:

$users = DB::table('users')
->where('votes', '=', 100)
->where('age', '>', 35)
->get();

Для удобства, если вы хотите проверить, что столбец = данному значению, вы можете передать значение вторым аргументом методу where. Laravel предположит, что вы хотите использовать оператор =:

$users = DB::table('users')->where('votes', 100)->get();

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

$users = DB::table('users')
->where('votes', '>=', 100)
->get();
 
$users = DB::table('users')
->where('votes', '<>', 100)
->get();
 
$users = DB::table('users')
->where('name', 'like', 'T%')
->get();

Вы также можете передать массив условий функции where. Каждый элемент массива должен быть массивом, содержащим три аргумента, как правило, передаваемых методу where:

$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();

Внимание PDO не поддерживает привязку имен столбцов. Поэтому никогда не допускайте ввод пользовательских данных для определения имен столбцов, на которые ссылаются ваши запросы, включая столбцы "order by".

Или Условия Where

При последовательном вызове метода where построителя запросов выражения "where" будут объединяться с использованием оператора and. Однако вы можете использовать метод orWhere, чтобы объединить выражение с запросом, используя оператор or. Метод orWhere принимает те же аргументы, что и метод where:

$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();

Если вам нужно сгруппировать условие "or" в скобки, вы можете передать замыкание в качестве первого аргумента методу orWhere:

$users = DB::table('users')
->where('votes', '>', 100)
->orWhere(function (Builder $query) {
$query->where('name', 'Abigail')
->where('votes', '>', 50);
})
->get();

В приведенном выше примере будет создан следующий SQL-запрос:

select * from users where votes > 100 or (name = 'Abigail' and votes > 50)

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

Условия Where Not

Методы whereNot и orWhereNot могут быть использованы для отрицания заданной группы ограничений запроса. Например, следующий запрос исключает продукты, которые находятся на распродаже или у которых цена меньше десяти:

$products = DB::table('products')
->whereNot(function (Builder $query) {
$query->where('clearance', true)
->orWhere('price', '<', 10);
})
->get();

Условия Where для JSON

Laravel также поддерживает запросы к типам столбцов JSON в базах данных, которые предоставляют поддержку типов столбцов JSON. В настоящее время это включает MySQL 5.7+, PostgreSQL, SQL Server 2016 и SQLite 3.39.0 (с расширением JSON1). Для запроса к столбцу JSON используйте оператор ->:

$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();

Вы можете использовать whereJsonContains для запроса массивов JSON. Эта функция не поддерживается версиями базы данных SQLite менее 3.38.0:

$users = DB::table('users')
->whereJsonContains('options->languages', 'en')
->get();

Если ваше приложение использует базы данных MySQL или PostgreSQL, вы можете передать массив значений методу whereJsonContains:

$users = DB::table('users')
->whereJsonContains('options->languages', ['en', 'de'])
->get();

Метод whereJsonLength можно использовать для запроса массивов JSON по их длине:

$users = DB::table('users')
->whereJsonLength('options->languages', 0)
->get();
 
$users = DB::table('users')
->whereJsonLength('options->languages', '>', 1)
->get();

Дополнительные условия Where

whereBetween / orWhereBetween

Метод whereBetween проверяет, что значение столбца находится между двумя значениями:

$users = DB::table('users')
->whereBetween('votes', [1, 100])
->get();

whereNotBetween / orWhereNotBetween

Метод whereNotBetween проверяет, что значение столбца находится вне двух значений:

$users = DB::table('users')
->whereNotBetween('votes', [1, 100])
->get();

whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns

Метод whereBetweenColumns проверяет, что значение столбца находится между двумя значениями двух столбцов в одной строке таблицы:

$patients = DB::table('patients')
->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight'])
->get();

Метод whereNotBetweenColumns проверяет, что значение столбца находится вне двух значений двух столбцов в одной строке таблицы:

$patients = DB::table('patients')
->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight'])
->get();

whereIn / whereNotIn / orWhereIn / orWhereNotIn

Метод whereIn проверяет, что значение заданного столбца содержится в заданном массиве:

$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();

Метод whereNotIn проверяет, что значение заданного столбца не содержится в заданном массиве:

$users = DB::table('users')
->whereNotIn('id', [1, 2, 3])
->get();

Вы также можете предоставить объект запроса вторым аргументом метода whereIn:

$activeUsers = DB::table('users')->select('id')->where('is_active', 1);
 
$users = DB::table('comments')
->whereIn('user_id', $activeUsers)
->get();

Приведенный выше пример приведет к следующему SQL-запросу:

select * from comments where user_id in (
select id
from users
where is_active = 1
)

Внимание Если вы добавляете большой массив целочисленных привязок к вашему запросу, вы можете использовать методы whereIntegerInRaw или whereIntegerNotInRaw, чтобы значительно снизить использование памяти.

whereNull / whereNotNull / orWhereNull / orWhereNotNull

Метод whereNull проверяет, что значение заданного столбца равно NULL:

$users = DB::table('users')
->whereNull('updated_at')
->get();

Метод whereNotNull проверяет, что значение столбца не равно NULL:

$users = DB::table('users')
->whereNotNull('updated_at')
->get();

whereDate / whereMonth / whereDay / whereYear / whereTime

Метод whereDate может быть использован для сравнения значения столбца с датой:

$users = DB::table('users')
->whereDate('created_at', '2016-12-31')
->get();

Метод whereMonth может быть использован для сравнения значения столбца с определенным месяцем:

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

Метод whereDay может быть использован для сравнения значения столбца с определенным днем месяца:

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

Метод whereYear может быть использован для сравнения значения столбца с определенным годом:

$users = DB::table('users')
->whereYear('created_at', '2016')
->get();

Метод whereTime может быть использован для сравнения значения столбца с определенным временем:

$users = DB::table('users')
->whereTime('created_at', '=', '11:20:45')
->get();

whereColumn / orWhereColumn

Метод whereColumn может быть использован для проверки равенства двух столбцов:

$users = DB::table('users')
->whereColumn('first_name', 'last_name')
->get();

Вы также можете передать оператор сравнения методу whereColumn:

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

Также вы можете передать массив сравнений столбцов методу whereColumn. Эти условия будут объединены с использованием оператора and:

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

Логическая группировка

Иногда вам может потребоваться сгруппировать несколько условий "where" в скобках, чтобы добиться нужной логической группировки запроса. Фактически, вы должны обязательно группировать вызовы метода orWhere в скобках, чтобы избежать непредвиденного поведения запроса. Для этого вы можете передать замыкание методу where:

$users = DB::table('users')
->where('name', '=', 'John')
->where(function (Builder $query) {
$query->where('votes', '>', 100)
->orWhere('title', '=', 'Admin');
})
->get();

Как видно из примера выше, передача замыкания методу where указывает построителю запросов начать группу ограничений. Замыкание получит экземпляр построителя запросов, который вы можете использовать для установки ограничений, которые должны находиться внутри группы скобок. Приведенный выше пример приведет к следующему SQL-запросу:

select * from users where name = 'John' and (votes > 100 or title = 'Admin')

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

Расширенные условия Where

Условия Where Exists

Метод whereExists позволяет вам создавать SQL-выражения "where exists". Метод whereExists принимает замыкание, которое получит экземпляр построителя запросов, позволяя вам определить запрос, который должен находиться внутри выражения "exists":

$users = DB::table('users')
->whereExists(function (Builder $query) {
$query->select(DB::raw(1))
->from('orders')
->whereColumn('orders.user_id', 'users.id');
})
->get();

В качестве альтернативы вы можете предоставить объект запроса методу whereExists вместо замыкания:

$orders = DB::table('orders')
->select(DB::raw(1))
->whereColumn('orders.user_id', 'users.id');
 
$users = DB::table('users')
->whereExists($orders)
->get();

Оба приведенных выше примера приведут к следующему SQL-запросу:

select * from users
where exists (
select 1
from orders
where orders.user_id = users.id
)

Подзапросы в условиях Where

Иногда вам может потребоваться построить условие "where", которое сравнивает результаты подзапроса с заданным значением. Вы можете сделать это, передав замыкание и значение методу where. Например, следующий запрос извлечет всех пользователей с последней "membership" заданного типа:

use App\Models\User;
use Illuminate\Database\Query\Builder;
 
$users = User::where(function (Builder $query) {
$query->select('type')
->from('membership')
->whereColumn('membership.user_id', 'users.id')
->orderByDesc('membership.start_date')
->limit(1);
}, 'Pro')->get();

Или вам может потребоваться построить условие "where", которое сравнивает столбец с результатами подзапроса. Вы можете сделать это, передав столбец, оператор и замыкание методу where. Например, следующий запрос извлечет все записи о доходах, где сумма меньше средней;

use App\Models\Income;
use Illuminate\Database\Query\Builder;
 
$incomes = Income::where('amount', '<', function (Builder $query) {
$query->selectRaw('avg(i.amount)')->from('incomes as i');
})->get();

Условия Where для полнотекстового поиска

Внимание Как на данный момент полные текстовые операторы where поддерживаются только MySQL и PostgreSQL.

Методы whereFullText и orWhereFullText могут быть использованы для добавления полнотекстовых условий "where" в запрос для столбцов, имеющих полнотекстовые индексы. Эти методы будут преобразованы в соответствующий SQL для используемой базы данных Laravel. Например, для приложений, использующих MySQL, будет сгенерировано условие MATCH AGAINST:

$users = DB::table('users')
->whereFullText('bio', 'web developer')
->get();

Сортировка, группировка, ограничение и смещение

Сортировка

Метод orderBy

Метод orderBy позволяет сортировать результаты запроса по заданному столбцу. Первый аргумент, принимаемый методом orderBy, должен быть столбцом, по которому вы хотите сортировать, в то время как второй аргумент определяет направление сортировки и может быть либо asc, либо desc:

$users = DB::table('users')
->orderBy('name', 'desc')
->get();

Для сортировки по нескольким столбцам вы можете просто вызвать orderBy столько раз, сколько необходимо:

$users = DB::table('users')
->orderBy('name', 'desc')
->orderBy('email', 'asc')
->get();

Методы latest и oldest

Методы latest и oldest позволяют легко упорядочивать результаты по дате. По умолчанию результат будет упорядочен по столбцу created_at таблицы. Или вы можете передать имя столбца, по которому вы хотите упорядочить:

$user = DB::table('users')
->latest()
->first();

Случайный порядок

Метод inRandomOrder может быть использован для случайной сортировки результатов запроса. Например, вы можете использовать этот метод, чтобы получить случайного пользователя:

$randomUser = DB::table('users')
->inRandomOrder()
->first();

Удаление существующих порядков

Метод reorder удаляет все предыдущие "order by" условия, которые ранее были применены к запросу:

$query = DB::table('users')->orderBy('name');
 
$unorderedUsers = $query->reorder()->get();

Вы можете передать столбец и направление при вызове метода reorder, чтобы удалить все существующие условия "order by" и применить к запросу совершенно новый порядок:

$query = DB::table('users')->orderBy('name');
 
$usersOrderedByEmail = $query->reorder('email', 'desc')->get();

Группировка

Методы groupBy и having

Как вы могли бы ожидать, методы groupBy и having могут быть использованы для группировки результатов запроса. Сигнатура метода having аналогична сигнатуре метода where:

$users = DB::table('users')
->groupBy('account_id')
->having('account_id', '>', 100)
->get();

Вы можете использовать метод havingBetween для фильтрации результатов в заданном диапазоне:

$report = DB::table('orders')
->selectRaw('count(id) as number_of_orders, customer_id')
->groupBy('customer_id')
->havingBetween('number_of_orders', [5, 15])
->get();

Вы можете передать несколько аргументов методу groupBy, чтобы группировать по нескольким столбцам:

$users = DB::table('users')
->groupBy('first_name', 'status')
->having('account_id', '>', 100)
->get();

Для создания более сложных выражений having см. метод havingRaw.

Ограничение и смещение

Методы skip и take

Вы можете использовать методы skip и take для ограничения количества результатов, возвращаемых запросом, или для пропуска заданного числа результатов в запросе:

$users = DB::table('users')->skip(10)->take(5)->get();

В качестве альтернативы вы можете использовать методы limit и offset. Эти методы функционально эквивалентны методам take и skip соответственно:

$users = DB::table('users')
->offset(10)
->limit(5)
->get();

Условия при определенных обстоятельствах

Иногда вам может потребоваться, чтобы некоторые условия запроса применялись в зависимости от другого условия. Например, вам может потребоваться применить оператор where только в том случае, если задано определенное значение входного параметра HTTP-запроса. Вы можете сделать это с использованием метода when:

$role = $request->string('role');
 
$users = DB::table('users')
->when($role, function (Builder $query, string $role) {
$query->where('role_id', $role);
})
->get();

Метод when выполняет переданное замыкание только в том случае, если первый аргумент равен true. Если первый аргумент равен false, замыкание не будет выполнено. Таким образом, в приведенном выше примере замыкание, переданное методу when, будет вызвано только в том случае, если поле role присутствует во входящем запросе и оценивается как true.

Вы можете передать другое замыкание в качестве третьего аргумента методу when. Это замыкание будет выполнено только в том случае, если первый аргумент оценивается как false. Для иллюстрации того, как можно использовать эту функцию, мы будем использовать ее для настройки сортировки запроса по умолчанию:

$sortByVotes = $request->boolean('sort_by_votes');
 
$users = DB::table('users')
->when($sortByVotes, function (Builder $query, bool $sortByVotes) {
$query->orderBy('votes');
}, function (Builder $query) {
$query->orderBy('name');
})
->get();

Запросы на вставку

Поставщик запросов также предоставляет метод insert, который можно использовать для вставки записей в таблицу базы данных. Метод insert принимает массив названий столбцов и значений:

DB::table('users')->insert([
'email' => '[email protected]',
'votes' => 0
]);

Вы можете вставить несколько записей сразу, передав массив массивов. Каждый массив представляет собой запись, которую следует вставить в таблицу:

DB::table('users')->insert([
['email' => '[email protected]', 'votes' => 0],
['email' => '[email protected]', 'votes' => 0],
]);

Метод insertOrIgnore будет игнорировать ошибки при вставке записей в базу данных. Используя этот метод, вы должны знать, что ошибки дублирования записей будут проигнорированы, а другие типы ошибок также могут быть проигнорированы в зависимости от используемого движка базы данных. Например, insertOrIgnore будет обходить строгий режим MySQL:

DB::table('users')->insertOrIgnore([
['id' => 1, 'email' => '[email protected]'],
['id' => 2, 'email' => '[email protected]'],
]);

Метод insertUsing вставит новые записи в таблицу, используя подзапрос для определения данных, которые следует вставить:

DB::table('pruned_users')->insertUsing([
'id', 'name', 'email', 'email_verified_at'
], DB::table('users')->select(
'id', 'name', 'email', 'email_verified_at'
)->where('updated_at', '<=', now()->subMonth()));

Автоинкрементные идентификаторы

Если таблица имеет автоинкрементный идентификатор, используйте метод insertGetId для вставки записи и получения затем идентификатора:

$id = DB::table('users')->insertGetId(
['email' => '[email protected]', 'votes' => 0]
);

Внимание При использовании PostgreSQL метод insertGetId ожидает, что автоинкрементный столбец будет назван id. Если вы хотите извлечь ID из другой "последовательности", вы можете передать имя столбца вторым параметром методу insertGetId.

Обновление существующих записей или вставка новых

Метод upsert вставляет записи, которых не существует, и обновляет записи, которые уже существуют, новыми значениями, которые вы можете указать. Первый аргумент метода состоит из значений для вставки или обновления, в то время как второй аргумент перечисляет столбцы, которые уникальным образом идентифицируют записи в связанной таблице. Третий и последний аргумент метода - это массив столбцов, которые должны быть обновлены, если соответствующая запись уже существует в базе данных:

DB::table('flights')->upsert(
[
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
],
['departure', 'destination'],
['price']
);

В приведенном выше примере Laravel попытается вставить две записи. Если запись уже существует с теми же значениями столбцов departure и destination, Laravel обновит столбец price этой записи.

Внимание Все базы данных, кроме SQL Server, требуют, чтобы столбцы второго аргумента метода upsert имели индекс "первичный" или "уникальный". Кроме того, драйвер базы данных MySQL игнорирует второй аргумент метода upsert и всегда использует индексы "первичный" и "уникальный" таблицы для обнаружения существующих записей.

Запросы на обновление

Помимо вставки записей в базу данных, Конструктор запросов также может обновлять существующие записи с использованием метода update. Метод update, как и метод insert, принимает массив пар столбец-значение, указывающих столбцы для обновления. Метод update возвращает количество затронутых строк. Вы можете ограничить запрос update с использованием условий where:

$affected = DB::table('users')
->where('id', 1)
->update(['votes' => 1]);

Обновление или вставка

Иногда вам может потребоваться обновить существующую запись в базе данных или создать ее, если соответствующей записи не существует. В этом случае можно использовать метод updateOrInsert. Метод updateOrInsert принимает два аргумента: массив условий, по которым будет найдена запись, и массив пар столбец-значение, указывающих столбцы для обновления.

Метод updateOrInsert попытается найти соответствующую запись в базе данных, используя пары столбец-значение первого аргумента. Если запись существует, она будет обновлена значениями второго аргумента. Если запись не может быть найдена, будет вставлена новая запись со слиянием атрибутов обоих аргументов:

DB::table('users')
->updateOrInsert(
['email' => '[email protected]', 'name' => 'John'],
['votes' => '2']
);

Обновление столбцов с типом данных JSON

При обновлении JSON-столбца вы должны использовать синтаксис -> для обновления соответствующего ключа в JSON-объекте. Эта операция поддерживается в MySQL 5.7+ и PostgreSQL 9.5+:

$affected = DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);

Инкремент и декремент

Конструктор запросов также предоставляет удобные методы для увеличения или уменьшения значения заданного столбца. Оба этих метода принимают как минимум один аргумент: столбец для модификации. Второй аргумент может быть предоставлен для указания суммы, на которую следует увеличить или уменьшить столбец:

DB::table('users')->increment('votes');
 
DB::table('users')->increment('votes', 5);
 
DB::table('users')->decrement('votes');
 
DB::table('users')->decrement('votes', 5);

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

DB::table('users')->increment('votes', 1, ['name' => 'John']);

Кроме того, вы можете увеличивать или уменьшать значения нескольких столбцов одновременно с использованием методов incrementEach и decrementEach:

DB::table('users')->incrementEach([
'votes' => 5,
'balance' => 100,
]);

Запросы на удаление

Метод delete построителя запросов может быть использован для удаления записей из таблицы. Метод delete возвращает количество затронутых строк. Вы можете ограничить операторы delete, добавив условия "where" перед вызовом метода delete:

$deleted = DB::table('users')->delete();
 
$deleted = DB::table('users')->where('votes', '>', 100)->delete();

Если вы хотите усечь всю таблицу, удалив все записи из таблицы и сбросив автоматическое инкрементирование идентификатора на ноль, вы можете использовать метод truncate:

DB::table('users')->truncate();

Очистка Таблицы и PostgreSQL

При усечении базы данных PostgreSQL применяется поведение CASCADE. Это означает, что будут удалены все записи, связанные с внешним ключом в других таблицах.

Оптимистичная блокировка

Кроме того, Конструктор запросов включает несколько функций для реализации "пессимистической блокировки" при выполнении ваших операторов select. Для выполнения оператора с "разделяемой блокировкой" вы можете вызвать метод sharedLock. Разделяемая блокировка предотвращает изменение выбранных строк до фиксации вашей транзакции:

DB::table('users')
->where('votes', '>', 100)
->sharedLock()
->get();

В качестве альтернативы можно использовать метод lockForUpdate. Блокировка "for update" предотвращает изменение выбранных записей или их выбор с другой разделяемой блокировкой:

DB::table('users')
->where('votes', '>', 100)
->lockForUpdate()
->get();

Отладка

Вы можете использовать методы dd и dump при построении запроса для вывода текущих привязок и SQL-запроса. Метод dd отобразит отладочную информацию и затем остановит выполнение запроса. Метод dump отобразит отладочную информацию, но позволит выполнению запроса продолжиться:

DB::table('users')->where('votes', '>', 100)->dd();
 
DB::table('users')->where('votes', '>', 100)->dump();

Методы dumpRawSql и ddRawSql могут быть вызваны для вывода SQL-запроса с правильно подставленными всеми параметрами привязки:

DB::table('users')->where('votes', '>', 100)->dumpRawSql();
 
DB::table('users')->where('votes', '>', 100)->ddRawSql();