База данных · Начало работы
- Введение
- Конфигурирование
- Соединения для чтения и записи
- Выполнение SQL-запросов
- Использование нескольких соединений к базе данных
- Прослушивание событий запроса
- Мониторинг общего времени выполнения запроса
- Транзакции базы данных
- Подключение к базе данных с помощью интерфейса командной строки Artisan
- Инспектирование базы данных
- Мониторинг баз данных
Введение
Почти каждое современное веб-приложение взаимодействует с базой данных. Laravel делает взаимодействие с базами данных чрезвычайно простым через поддержку множества баз данных, используя либо сырой SQL построителя запросов, либо Eloquent ORM. В настоящее время Laravel обеспечивает поддержку пяти баз данных:
- MariaDB 10.3+ (Version Policy)
- MySQL 5.7+ (Version Policy)
- PostgreSQL 10.0+ (Version Policy)
- SQLite 3.26.0+
- SQL Server 2017+ (Version Policy)
Кроме того, MongoDB поддерживается через пакет mongodb/laravel-mongodb
, который официально поддерживается MongoDB. Дополнительную информацию можно найти в документации Laravel MongoDB.
Конфигурирование
Конфигурация служб баз данных Laravel находится в конфигурационном файле config/database.php
вашего приложения. В этом файле вы можете определить все соединения к базе данных, а также указать, какое соединение должно использоваться по умолчанию. Большинство параметров конфигурации в этом файле определяется значениями переменных окружения вашего приложения. В этом файле представлены примеры для большинства систем баз данных, поддерживаемых Laravel.
По умолчанию пример конфигурации окружения Laravel готов к использованию с Laravel Sail, который представляет собой конфигурацию Docker для разработки приложений Laravel на вашем локальном компьютере. Однако вы можете изменить конфигурацию своей базы данных по мере необходимости для своей локальной базы данных.
Конфигурация SQLite
Базы данных SQLite содержатся в одном файле вашей файловой системы. Вы можете создать новую базу данных SQLite, используя команду touch
в консоли: touch database/database.sqlite
. После создания базы данных вы можете легко настроить переменные окружения так, чтобы они указывали на эту базу данных, указав абсолютный путь к базе данных в переменной DB_DATABASE
окружения:
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
По умолчанию ограничения внешнего ключа включены для соединений SQLite. Если вы хотите отключить их, вам следует установить для переменной среды DB_FOREIGN_KEYS
значение false
:
DB_FOREIGN_KEYS=false
Если вы используете установщик Laravel для создания приложения Laravel и выбираете SQLite в качестве базы данных, Laravel автоматически создаст
database/database.sqlite
и запустит для вас стандартную миграцию базы данных.
Конфигурация Microsoft SQL Server
Чтобы использовать базу данных Microsoft SQL Server, вы должны убедиться, что у вас установлены расширения PHP sqlsrv
и pdo_sqlsrv
, а также любые зависимости, которые могут им потребоваться, например, драйвер Microsoft SQL ODBC.
Конфигурация с использованием URL
Обычно соединения с базой данных конфигурируются с использованием нескольких значений, таких как host
, database
, username
, password
и т.д. Каждое из этих значений имеет свою собственную соответствующую переменную окружения. Это означает, что при указании информации о соединении с базой данных на рабочем веб-сервере вам необходимо управлять несколькими переменными окружения.
Некоторые поставщики СУБД, такие, как AWS и Heroku, предоставляют единый «URL» базы данных, который содержит всю информацию о соединении в одной строке. Пример URL-адреса базы данных может выглядеть так:
mysql://root:password@127.0.0.1/forge?charset=UTF-8
Эти URL обычно следуют соглашению стандартной схемы:
driver://username:password@host:port/database?options
Для удобства Laravel поддерживает эти URL-адреса в качестве альтернативы настройке базы данных с несколькими параметрами конфигурации. Если присутствует параметр конфигурации url
(или соответствующая переменная DB_URL
окружения), то он будет использоваться для получения информации о соединении с базой данных и об учетных данных.
Соединения для чтения и записи
По желанию можно использовать одно соединение с базой данных для операторов SELECT
, а другое – для операторов INSERT
, UPDATE
и DELETE
. Laravel упрощает эту задачу, и всегда будут использоваться соответствующие соединения, независимо от того, используете ли вы сырые запросы построителя запросов или Eloquent ORM.
Чтобы увидеть, как должны быть настроены соединения для чтения / записи, давайте посмотрим на этот пример:
'mysql' => [
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'196.168.1.3',
],
],
'sticky' => true,
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
Обратите внимание, что в массив конфигурации были добавлены три ключа: read
, write
и sticky
. Ключи read
и write
имеют значения массива, содержащие один ключ: host
. Остальные параметры базы данных для соединений read
и write
будут объединены из основного массива конфигурации mysql
.
В массивы read
и write
вам нужно помещать только те элементы, значения которых вы хотите переопределить из основного массива mysql
. Таким образом, в этом случае 192.168.1.1
будет использоваться в качестве хоста для соединения «чтение», а 192.168.1.3
– для соединения «запись». Учетные данные БД, префикс, набор символов и все другие параметры из основного массива mysql
будут совместно использоваться обоими соединениями. Если в массиве конфигурации host
существует несколько значений, то для каждого запроса хост базы данных будет выбран случайным образом.
Параметр sticky
Параметр sticky
– это необязательное значение, которое может использоваться для разрешения немедленного чтения записей, которые были записаны в базу данных во время текущего цикла запроса. Если опция sticky
включена и в текущем цикле запроса к базе данных была выполнена операция «записи», то любые дальнейшие операции «чтения» будут использовать соединение «запись». Это гарантирует, что любые данные, записанные во время цикла запроса, могут быть немедленно обратно прочитаны из базы данных во время того же запроса. Вам решать, является ли это желаемым поведением для вашего приложения.
Выполнение SQL-запросов
После того как вы настроили соединение с базой данных, вы можете выполнять запросы, используя фасад DB
. Фасад DB
содержит методы для каждого типа запроса: select
, update
, insert
, delete
, и statement
.
Выполнение Select-запроса
Чтобы выполнить базовый запрос SELECT
, вы можете использовать метод select
фасада DB
:
<?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::select('select * from users where active = ?', [1]);
return view('user.index', ['users' => $users]);
}
}
Первым аргументом, переданным методу select
, является SQL-запрос, а вторым аргументом – любые привязки параметров, необходимые для запроса. Обычно это значения ограничений выражений where
. Привязка параметров обеспечивает защиту от SQL-инъекций.
Метод select
всегда возвращает «массив» результатов. Каждый результат в массиве будет объектом stdClass
PHP, представляющим запись из базы данных:
use Illuminate\Support\Facades\DB;
$users = DB::select('select * from users');
foreach ($users as $user) {
echo $user->name;
}
Выбор скалярных значений
Иногда ваш запрос к базе данных может вернуть единственное скалярное значение. Вместо того чтобы получать скалярный результат запроса из объекта записи, Laravel позволяет вам получать это значение напрямую с использованием метода scalar
:
$burgers = DB::scalar(
"select count(case when food = 'burger' then 1 end) as burgers from menu"
);
Выбор нескольких наборов результатов
Если ваше приложение вызывает хранимые процедуры, возвращающие несколько наборов результатов, вы можете использовать метод selectResultSets
для получения всех наборов результатов, возвращенных хранимой процедурой:
[$options, $notifications] = DB::selectResultSets(
"CALL get_user_options_and_notifications(?)", $request->user()->id
);
Использование именованных псевдопеременных
Вместо использования символа ?
для связывания параметров вы можете выполнить запрос, используя именованные привязки:
$results = DB::select('select * from users where id = :id', ['id' => 1]);
Выполнение Insert-запроса
Чтобы выполнить запрос с INSERT
, вы можете использовать метод insert
фасада DB
. Как и select
, этот метод принимает запрос SQL в качестве первого аргумента, а привязки – в качестве второго аргумента:
use Illuminate\Support\Facades\DB;
DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
Выполнение Update-запроса
Метод update
следует использовать для обновления существующих записей в базе данных. Количество затронутых выражением строк будут возвращены этим методом:
use Illuminate\Support\Facades\DB;
$affected = DB::update(
'update users set votes = 100 where name = ?',
['Anita']
);
Выполнение Delete-запроса
Для удаления записей из базы данных следует использовать метод delete
. Как и update
, количество затронутых выражением строк будут возвращены этим методом:
use Illuminate\Support\Facades\DB;
$deleted = DB::delete('delete from users');
Выполнение запроса общего типа
Некоторые операторы базы данных не возвращают никакого значения. Для этих типов операций вы можете использовать метод statement
фасада DB
:
DB::statement('drop table users');
Выполнение неподготовленного запроса
По желанию может потребоваться выполнить запрос SQL без привязки каких-либо значений. Для этого используйте метод unprepared
фасада DB
:
DB::unprepared('update users set votes = 100 where name = "Dries"');
Поскольку неподготовленные запросы не связывают параметры, они могут быть уязвимы для SQL-инъекций. Вы никогда не должны пропускать в неподготовленное выражение значения, управляемые пользователем.
Неявные фиксации (implicit commit)
При использовании в транзакциях методов statement
и unprepared
фасада DB
вы должны быть осторожны, чтобы избежать операторов, которые вызывают неявные фиксации. Эти операторы заставят ядро базы данных косвенно зафиксировать всю транзакцию, в результате чего Laravel не будет знать об уровне транзакции базы данных. Примером такого оператора является создание таблицы базы данных:
DB::unprepared('create table a (col varchar(1) null)');
Пожалуйста, обратитесь к руководству по MySQL для ознакомления со списком всех операторов, которые выполняют неявные фиксации.
Использование нескольких соединений к базе данных
Если ваше приложение определяет несколько соединений в конфигурационном файле config/database.php
, то вы можете получить доступ к каждому соединению с помощью метода connection
фасада DB
. Имя соединения, передаваемое методу connection
, должно соответствовать одному из подключений, перечисленных в вашем конфигурационном файле config/database.php
, включая переопределенные с помощью глобального помощника config
во время выполнения скрипта:
use Illuminate\Support\Facades\DB;
$users = DB::connection('sqlite')->select(/* ... */);
Вы можете получить доступ к сырому, базовому экземпляру PDO текущего соединения, используя метод getPdo
экземпляра соединения:
$pdo = DB::connection()->getPdo();
Прослушивание событий запроса
По желанию можно указать замыкание, которое будет вызываться для каждого SQL-запроса, выполняемого вашим приложением, используя метод listen
фасада DB
. Этот метод может быть полезен для логирования запросов или их отладки. Вы можете зарегистрировать замыкание слушателя запросов в методе boot
поставщика служб:
<?php
namespace App\Providers;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Регистрация любых служб приложения.
*/
public function register(): void
{
// ...
}
/**
* Загрузка любых служб приложения.
*/
public function boot(): void
{
DB::listen(function (QueryExecuted $query) {
// $query->sql;
// $query->bindings;
// $query->time;
// $query->toRawSql();
});
}
}
Мониторинг общего времени выполнения запроса
Одной из обычных узких точек производительности современных веб-приложений является время, которое они затрачивают на выполнение запросов к базе данных. К счастью, Laravel может вызвать замыкание или обратный вызов по вашему выбору, когда время выполнения запросов к базе данных в течение одного запроса становится слишком велико. Для начала укажите порог времени выполнения запроса (в миллисекундах) и замыкание для метода whenQueryingForLongerThan
. Вы можете вызвать этот метод в методе boot
Сервис-провайдера::
<?php
namespace App\Providers;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Events\QueryExecuted;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
// Notify development team...
});
}
}
Транзакции базы данных
Вы можете использовать метод transaction
фасада DB
, для выполнения набора операций в транзакции базы данных. Если при закрытии транзакции возникает исключение, то транзакция автоматически откатывается, а исключение генерируется повторно. Если замыкание выполнено успешно, то транзакция будет автоматически зафиксирована. Вам не нужно беспокоиться о ручном откате или фиксации при использовании метода transaction
:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});
Обработка взаимоблокировок
Метод transaction
принимает необязательный второй аргумент, который определяет, сколько раз транзакция должна быть повторена при возникновении взаимоблокировок. Как только эти попытки будут исчерпаны, будет выброшено исключение:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, 5);
Использование транзакций вручную
Если вы хотите вручную начать транзакцию и иметь полный контроль над откатами и фиксациями, то вы можете использовать метод beginTransaction
фасада DB
:
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
Вы можете откатить транзакцию с помощью метода rollBack
:
DB::rollBack();
Наконец, вы можете зафиксировать транзакцию с помощью метода commit
:
DB::commit();
Методы транзакций фасада
DB
контролируют транзакции как для построителя запросов, так и для Eloquent ORM.
Подключение к базе данных с помощью интерфейса командной строки Artisan
Если вы хотите подключиться к своей базе данных с помощью интерфейса командной строки, то вы можете использовать команду db
Artisan:
php artisan db
При необходимости, вы можете указать имя соединения для подключения к базе данных, не являющееся соединением по умолчанию:
php artisan db mysql
Инспектирование базы данных
С помощью команд Artisan db:show
и db:table
вы можете получить ценную информацию о вашей базе данных и ее связанных таблицах. Для просмотра обзора вашей базы данных, включая ее размер, тип, количество открытых соединений и сводку по ее таблицам, вы можете использовать команду db:show
:
php artisan db:show
Вы можете указать, какое соединение с базой данных следует использовать, передав имя соединения с помощью опции --database
:
php artisan db:show --database=pgsql
Если вы хотите включить количество строк в таблицах и подробности о представлениях базы данных в выводе команды, вы можете указать соответственно опции --counts
и –-views
. На больших базах данных получение количества строк и сведений о представлениях может занять много времени:
php artisan db:show --counts --views
Кроме того, вы можете использовать следующие методы Schema
для проверки вашей базы данных:
use Illuminate\Support\Facades\Schema;
$tables = Schema::getTables();
$views = Schema::getViews();
$columns = Schema::getColumns('users');
$indexes = Schema::getIndexes('users');
$foreignKeys = Schema::getForeignKeys('users');
Если вы хотите проверить соединение с базой данных, которое не является соединением вашего приложения по умолчанию, вы можете использовать метод connection
:
$columns = Schema::connection('sqlite')->getColumns('users');
Обзор таблиц
Если вы хотите получить обзор отдельной таблицы в вашей базе данных, вы можете выполнить команду Artisan db:table
. Эта команда предоставляет общий обзор таблицы базы данных, включая ее столбцы, типы, атрибуты, ключи и индексы:
php artisan db:table users
Мониторинг баз данных
Используя команду Artisan db:monitor
, вы можете поручить Laravel отправить Illuminate\Database\Events\DatabaseBusy
, если ваша база данных управляет большим количеством открытых соединений, чем задано.
Для начала вам следует запланировать выполнение команды db:monitor
каждую минуту. Команда принимает имена конфигураций подключений к базе данных, которые вы хотите мониторить, а также максимальное количество открытых соединений, которые допустимы до отправки события:
php artisan db:monitor --databases=mysql,pgsql --max=100
Одного планирования этой команды недостаточно для отправки уведомления о количестве открытых соединений. Когда команда обнаруживает базу данных с количеством открытых соединений, превышающим ваш порог, будет отправлено событие DatabaseBusy
. Вы должны прослушивать это событие в файле AppServiceProvider
вашего приложения, чтобы отправить уведомление вам или вашей команде разработки:
use App\Notifications\DatabaseApproachingMaxConnections;
use Illuminate\Database\Events\DatabaseBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
/**
* Запуск любых служб приложения.
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', 'dev@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}