PDO не поддерживет связывание имён колонок. Поэтому, вам нужно никогда не позволять
пользовательскому вводу диктовать имена колонок в привязке к вашим запросам,
включая такие как "order by" и другие. Если есть необходимость позволить
пользователям выбрать конкретные колонки для запросов, нужно убедитсья в том,
что имя включено в список разрешённых.
Построение запросов
Содержание:
- Введение
- Получение результатов
- Selects
- Выражения
- Joins
- Unions
- Условия Where
- Упорядочивание, группировка, лимиты и смещение
- Условный пункт
- Вставка
- Обновление
- Удаления
- Блокировка
- Отладка
Введение
Конструктор запросов для базы данных Ларавел предоставляет удобный интерфейс для создания и выполнения запросов базы данных. Его можно использовать для большинства операций базы данных в вашем приложении. Кроме того, конструктор успешно работает со всеми поддерживаемыми системами баз данных.
Конструктор запросов Laravel привязку параметров PDO для защиты вашего приложения от SQL инъекций. Нет необходимости чистить строки, передаваемые в качестве привязки.
Получение результатов
Получение всех столбцов из таблицы
Вы можете использовать метод table
для фасада DB
,
чтобы начать запрос. Метод table
возвращает экземпляр конструктора запросов
для данной таблицы, позволяющий использовать другие выражения для запроса и наконец
получить результаты используя метод get
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
/**
* Show a list of all of the application's users.
*
* @return Response
*/
public function index()
{
$users = DB::table('users')->get();
return view('user.index', ['users' => $users]);
}
}
Метод get
возвращает экземпляр Illuminate\Support\Collection
содержащий
результаты запроса, где каждый результат является экземпляром объекта PHP stdClass
.
Вы можете получить доступ к значению каждой колонки через свойство объекта:
foreach ($users as $user) {
echo $user->name;
}
Получение строки / колонки из таблицы
Если вам просто необходимо получить строку из таблицы бд, вы можете использовать
метод first
. Этот метод вернёт одиночный объект stdClass
:
$user = DB::table('users')->where('name', 'John')->first();
echo $user->name;
Если вам не нужны все значения строки, вы можете получить одиночное значение из записи
используя метод value
. Этот метод вернёт значение напрямую:
$email = DB::table('users')->where('name', 'John')->value('email');
Используйте метод find
, чтобы получить один ряд по значению id
:
$user = DB::table('users')->find(3);
Получение списка значений колонки
Если вы хотите получить Коллекцию содержащую значения одной колонки, вы можете использовать
метод pluck
. В этом примере, мы получим Коллекцию заголовков роли:
$titles = DB::table('roles')->pluck('title');
foreach ($titles as $title) {
echo $title;
}
Вы можете также указать пользовательский ключ колонки для возвращаемой Коллекции:
$roles = DB::table('roles')->pluck('title', 'name');
foreach ($roles as $name => $title) {
echo $title;
}
Деление результатов на части
Если вам необходимо работать с тысячами записей базы данных, убедитесь подумайте об использовании
метод chunk
. Этот метод получает необольшие куски результатов и передаёт каждый кусок
в Closure
для обработки. Этот метод очень полезен для написания команд
Artisan, которые обрабатывают сотни записей.
Например, давайте будем работать со всей таблицей users
, по частям со 100 записями
одновременно:
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
foreach ($users as $user) {
//
}
});
Вы можете остановить дальнейшую обработку чанков путём возврата false
из замыкания:
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
// Process the records...
return false;
});
Если вы обновляете записи базы данных во время деления результатов на части, ваши чанки
могут измениться непредсказуемо. Поэтому, при обновлении записей во время деления на части,
лучше всего использовать метод chunkById
. Этот метод будет проводить разбиение
результатов основываясь на первичном ключе записи:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
Агрегатные функции
Конструктор запросов также предоставляет набор агрегатных методов, таких как
count
, max
, min
, avg
, and sum
.
Вы можете вызвать любой из этих методов после построения вашего запроса:
$users = DB::table('users')->count();
$price = DB::table('orders')->max('price');
Вы также сможете комбинировать эти методы с другими классами:
$price = DB::table('orders')
->where('finalized', 1)
->avg('price');
Определение существования записи
Вместо использования метода count
для определения, что подходящая запись существует,
вы можете использовать метод exists
и doesntExist
:
return DB::table('orders')->where('finalized', 1)->exists();
return DB::table('orders')->where('finalized', 1)->doesntExist();
Работа с Select
Указаний условий Select
Вы можете не всегда хотеть выбирать все колонки из таблицы базы данных. Используя метод select
вы можете указать ваш выбор select
для запроса:
$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();
Выражения
Иногда вам может потребовать использовать чистые выражения для формирования запроса. Для создания выражения
вы можете использовать метод DB::raw
:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
Другие методы работы с чистыми выражениями
Вместо использования DB::raw
, вы также можете использовать следующие методы
для вставки чистых выражений в различные части вашего запроса.
selectRaw
Метод selectRaw
можете быть использован вместо select(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();
Присоединения
Inner Join
Конструктор запросов может также использоваться для написания выражений с join. Для работы с
типовым "inner join" можно использовать метод join
на экземпляр конструктора.
Первый аргумент, передаваемый в метод join
, является именем таблицы, которую нжуно присоединить.
Остальные аргументы нужны для указания условий присоединения колонок. Вы можете даже присоединить несколько
таблиц в простом запросе:
$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
Если вам необходимо работать с "left join" и "right 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". Метод принимает название таблицы, которую необходимо
присоединить. crossJoin
генерирует декартово произведение между первой таблицей и
добавленной таблицей:
$users = DB::table('sizes')
->crossJoin('colours')
->get();
Расширенное использование Join
Вы можете указать более сложное использованеи присоединений. Для начала передайте
Closure
в качестве второго аргумента в метод join
.
Closure
получит объект JoinClause
, который позволит вам
указать диапазон ограничений для join
:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);
})
->get();
Если вы хотите использовать "where" для ваших joins, вас могу заинтересовать
методы where
и orWhere
для join. Вместо сравнения двух колонок,
эти методы будут сравнивать столбец со значением:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();
Sub-Query Joins
Вы можете использовать методы joinSub
, leftJoinSub
, и rightJoinSub
для присоединения запроса в подзапрос. Каждый из этих методов принимает три аргумента:
подзапрос, псевдоним таблицы и замыкание, которое определяет связанные колонки:
$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 ($join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();
Unions
Конструктор запросов также представляет возможность быстро объединить два запроса вместе.
Например, вы можете создать начальный запрос и использовать метод union
для объединения со вторым запросом:
$first = DB::table('users')
->whereNull('first_name');
$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();
Условия Where
Простое условие Where
Вы можете использовать метод where
на экзепляр конструктора запросов,
чтобы добавить условие where
в запрос. Типовой вызов where
включает в состав 3 аргумента. Первый аргумент это имя колонки. Второй аргумент это оператор,
который может быть любым поддерживаемым базой данных оператором. Наконец, третий аргумент
это значение для сравнения со столбцом.
Например, представлен запрос, который проводит сравнивнение значения столбца и 100:
$users = DB::table('users')->where('votes', '=', 100)->get();
Для уверенности, можно просто не указывать оператор, а просто указать значение
в качестве второго аргумента для метода where
:
$users = DB::table('users')->where('votes', 100)->get();
Вы можете использовать набор операций при написании условий where
:
$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
:
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
Выражения с Or
Вы можете указывать ограничения цепочкой путём добавления or
в запрос.
Метод orWhere
принимает такие же аргументы как и метод
where
:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->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();
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();
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();
The whereMonth
method may be used to compare a column's value against a specific month
of a year:
$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();
Вы также можете передать оператор сравнения в метод:
$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, такие как "where exists" или сгруппированными параметрами. Конструтор запросов Laravel может справиться и с такими задачами. Для начала, давайте взглянем на пример группирования условий в скобках:
$users = DB::table('users')
->where('name', '=', 'John')
->where(function ($query) {
$query->where('votes', '>', 100)
->orWhere('title', '=', 'Admin');
})
->get();
Как вы можете заметить, передача Closure
в метод where
укажет
конструктору запросов о начале размещения сгруппированных параметров. Замыкание получит
экземпляр конструтора запросов, который вы можете использовать, чтобы установить ограничения,
располагающиеся внутри скобок. Указанный выше пример в конечном счёте произведёт
следующий SQL:
select * from users where name = 'John' and (votes > 100 or title = 'Admin')
Условие Where Exists
Метод whereExists
позволяет вам писать случай SQL where exists
.
Метод whereExists
принимает Замыкание в качестве аргумента, которое получит
экземпляр конструктора запроса. Это позволяет определить запрос, который расположен внутри
"exists":
$users = DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Для примера выше получим следующий SQL:
select * from users
where exists (
select 1 from orders where orders.user_id = users.id
)
Условие JSON Where
Ларавел также поддерживает проведение запросов для столбцов типа JSON для баз данных,
которые поддерживают такой тип. На текущий момент включены includes MySQL 5.7,
PostgreSQL, SQL Server 2016, и SQLite 3.9.0 (с
JSON1 расширением).
Нужно использовать оператор ->
для запросов JSON столбца:
$users = DB::table('users')
->where('options->language', 'en')
->get();
$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();
Вы можете использовать whereJsonContains
для проведения запросов в массивы JSON
(не поддерживается SQLite):
$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();
Упорядочивание, группировка, лимиты и смещение
orderBy
Метод orderBy
позволяет вам отсортировать результаты запроса по выбранному столбцу.
Первый аргумент для метод orderBy
является именем столбца, в то время как второй
аргумент отвечает за направлени сортировки и может быть
asc
или desc
:
$users = DB::table('users')
->orderBy('name', 'desc')
->get();
latest / oldest
Метод latest
и oldest
позволяют сортировать результаты по дате.
По умолчанию, результаты сортируются по столбцу created_at
. Или, вы можете передать
имя колонки, которую вы хотите отсортировать:
$user = DB::table('users')
->latest()
->first();
inRandomOrder
Метод inRandomOrder
может быть использован для сортировки результатов запроса
в случайном порядке. Например, вы можете использовать этот для получения случайного пользователя:
$randomUser = DB::table('users')
->inRandomOrder()
->first();
groupBy / having
Методы groupBy
и having
могут использоваться для группировки
результатов запросов. Механизм работы метода having
схож с
методом where
:
$users = DB::table('users')
->groupBy('account_id')
->having('account_id', '>', 100)
->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
:
$users = DB::table('users')
->offset(10)
->limit(5)
->get();
Условный пункт
Иногда вы можете захотеть разрешить выполнение запроса только в том случае, если что-то истино.
Выражение where
будет запущено только при условии, что данное значение вввода
присутсвует во входящем запросе. Вы можете решить такую задачу используя метод when
:
$role = $request->input('role');
$users = DB::table('users')
->when($role, function ($query, $role) {
return $query->where('role_id', $role);
})
->get();
Метод when
будет выполнять данное замыкание при условии, что первый параметр
true
. Если первый параметр false
, замыкание выполняться не будет.
Вы также можете передать другое замыкание в качестве третьего параметра в метод when
.
Это выражение будет выполняться в случае если первый параметр false
.
Для иллюстрации того, как это может быть использовано, мы будем использовать это для настройки
типового запроса сортировки:
$sortBy = null;
$users = DB::table('users')
->when($sortBy, function ($query, $sortBy) {
return $query->orderBy($sortBy);
}, function ($query) {
return $query->orderBy('name');
})
->get();
Вставка
Конструктор запросов также предоставляет метод insert
для вставки записей
в таблицу базы данных. Метод insert
принимает массив названий столбцов и значений:
DB::table('users')->insert(
['email' => 'kayla@example.com', 'votes' => 0]
);
Вы также можете вставить несколько записей в таблицу с одиночным вызовом insert
путём передачи двумерного массива. Каждый массив представляет строку,
которую необходимо вставить в таблицу:
DB::table('users')->insert([
['email' => 'picard@example.com', 'votes' => 0],
['email' => 'janeway@example.com', 'votes' => 0]
]);
Метод insertOrIgnore
будет игнорировать ошибки о повторах записей
во время вставки в базу данных:
DB::table('users')->insertOrIgnore([
['id' => 1, 'sisko@example.com'],
['id' => 2, 'email' => 'archer@example.com']
]);
Автоматическое увеличение идентификатора
Если таблица имеет идентификатор с отметкой "автоинкремент", используйте метод insertGetId
для вставки записи и получения идентификатора:
$id = DB::table('users')->insertGetId(
['email' => 'john@example.com', 'votes' => 0]
);
Обновление
Дополнительно к вставке записей в базу данных, конструктор запросов может также обновлять существующие
записи, используя метод update
. Метод update
, как и метод insert
принимает массив пар с названием колонки и значением. Вы можете построить запрос update
используя условия where
:
$affected = DB::table('users')
->where('id', 1)
->update(['votes' => 1]);
Обновить или вставить
Иногда вы можете захотеть обновить существующую запись в базе даннных или создать запись,
если такой ещё нет. В этом случае можно использовать метод updateOrInsert
.
Метод updateOrInsert
принимает два аргумент: массив условий, по которым
проводится поиск записи, и массив пар столбец / значение для обновления соотвествующих столбцов.
Метод updateOrInsert
в первую очередь будет искать соответвующую запись в бд
используя колонку пар и значений первого аргумента. Если такая запись существует,
то запись будет обновлена значениями из второго аргумента. Если запись не удалось найти,
новая запись будет вставлена с соединёнными аргументами из двух аргументов:
DB::table('users')
->updateOrInsert(
['email' =>'john@example.com', 'name' => 'John'],
['votes' => '2']
);
Обновление JSON колонок
При обновлении JSON столбца, вы должны использовать синтаксис с ->
для доступа
к ключе в объекте JSON. Эта операция поддерживается для MySQL 5.7+ и PostgreSQL 9.5+:
$affected = DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);
Инкремент и декремент
Конструктор запросов также предоставляет удобные методы для автоматического увеличения
или уменьшения значения данной колонки. Эти методы отличаются простотой и лаконичностью
по сравнению с написанием традиционного метода update
.
Оба эти метода принимают как минимум один аргумент: название столбца для изменения. Необязательный второй аргумент указаывает число, на которое необходимо увеличить или уменьшить иходное значение:
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']);
Удаления
Конструктор запросов может также использоваться для удаления записей из таблицы через
метод delete
. Вы можете указать точные условия удаления путём добавления
условий where
перед вызовом самого метода delete
:
DB::table('users')->delete();
DB::table('users')->where('votes', '>', 100)->delete();
Если вы хотите удалить все записи таблицы полностью, а значение автоинкремента установить как 0,
вы можете использовать метод truncate
:
DB::table('users')->truncate();
Пессимистическая блокировка
Конструктор запросов также включает несколько функций, которые могут помочь вам наложить
"пессимистическая блокировку" для вашего выражения select
. Для выполнения операции
с совместной блокировкой () "shared lock"), вы можете использовать метод
sharedLock
для запроса. Такая блокировка предотвращает выделенные строки
от внесения изменений до потверждения транзакции:
DB::table('users')->where('votes', '>', 100)->sharedLock()->get();
Как альтернатива, вы можете использовать метод lockForUpdate
. Данная блокировка
предотвращает внесение изменение или выделения другой совместной блокировки:
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();