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();