Початок роботи · Laravel
Вступ
Майже кожен сучасний вебзастосунок працює з базою даних. Laravel робить цю роботу надзвичайно простою для різних підтримуваних баз: через сирий SQL, плавний конструктор запитів і Eloquent ORM. Наразі Laravel має власну підтримку пʼятьох баз даних:
- MariaDB 10.3+ (політика версій)
- MySQL 5.7+ (політика версій)
- PostgreSQL 10.0+ (політика версій)
- SQLite 3.26.0+
- SQL Server 2017+ (політика версій)
Крім того, 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 і обираєте 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' => [
'driver' => 'mysql',
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'192.168.1.3',
],
],
'sticky' => true,
'port' => env('DB_PORT', '3306'),
'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([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \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 буде хостом для зʼєднання «read», а 192.168.1.3 - для зʼєднання «write». Облікові дані, префікс, кодування та всі інші опції з основного масиву mysql будуть спільними для обох зʼєднань. Коли в масиві конфігурації host кілька значень, для кожного запиту хост бази даних обирається випадково.
Опція sticky
Опція sticky - це необовʼязкове значення, яке дозволяє одразу читати записи, збережені в базу протягом поточного циклу запиту. Якщо sticky увімкнено і під час поточного циклу запиту було виконано операцію «write», усі подальші операції «read» використовуватимуть зʼєднання «write». Це гарантує, що дані, записані протягом циклу запиту, можна одразу прочитати з бази в межах того самого запиту. Чи бажана така поведінка для вашого застосунку, вирішувати вам.
Пулінг зʼєднань PostgreSQL
Багато керованих провайдерів PostgreSQL пропонують пулінг зʼєднань у режимі транзакцій через сервіси на кшталт PgBouncer або через проксіювання зʼєднань. Такі пулери ідеальні для запитів застосунку, але деякі операції зі схемою, міграції та команди обслуговування потребують прямого зʼєднання з базою.
Щоб використовувати транзакційний пулер із PostgreSQL, налаштуйте пулингове зʼєднання як звичайно і вкажіть параметри прямого зʼєднання в опції конфігурації direct:
'pgsql' => [
'driver' => 'pgsql',
// ...
'pooled' => env('DB_POOLED', false),
'direct' => array_filter([
'host' => env('DB_DIRECT_HOST'),
'port' => env('DB_DIRECT_PORT'),
'username' => env('DB_DIRECT_USERNAME'),
'password' => env('DB_DIRECT_PASSWORD'),
'sslmode' => env('DB_DIRECT_SSLMODE'),
]),
],
Коли зʼєднання PostgreSQL налаштоване як пулингове, Laravel автоматично вмикає для нього емульовані підготовлені вирази. Пряме зʼєднання успадковує всі опції, явно не визначені в конфігурації direct, і за замовчуванням використовує нативні підготовлені вирази.
Laravel автоматично використовує пряме зʼєднання для міграцій, дампів і відновлення схеми, db:wipe, db:show та db:table. Команда db також за замовчуванням використовує пряме зʼєднання, коли увімкнено пулинговий режим і налаштоване пряме зʼєднання; щоб підключитися саме до пулингового зʼєднання, передайте опцію --pooled:
php artisan db --pooled
Якщо вам потрібно явно скористатися прямим зʼєднанням у застосунку, додайте до назви зʼєднання суфікс ::direct:
DB::connection('pgsql::direct')->statement('create extension if not exists "uuid-ossp"');
Виконання SQL-запитів
Після налаштування зʼєднання з базою даних ви можете виконувати запити за допомогою фасаду DB. Фасад DB надає методи для кожного типу запиту: select, update, insert, delete і statement.
Виконання запиту Select
Щоб виконати базовий запит SELECT, скористайтеся методом select фасаду DB:
<?php
namespace App\Http\Controllers;
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 завжди повертає array результатів. Кожен результат у масиві - це PHP-обʼєкт stdClass, що представляє запис із бази даних:
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-інʼєкцій. Ніколи не допускайте значень, контрольованих користувачем, у непідготовлений вираз.
Неявні коміти
Користуючись методами 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
{
/**
* Зареєструвати будь-які сервіси застосунку.
*/
public function register(): void
{
// ...
}
/**
* Ініціалізувати будь-які сервіси застосунку.
*/
public function boot(): void
{
DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
// Повідомити команду розробки...
});
}
}
Транзакції бази даних
Щоб виконати набір операцій у межах транзакції бази даних, скористайтеся методом transaction фасаду DB. Якщо всередині замикання транзакції буде кинуто виняток, транзакція автоматично відкотиться, а виняток буде кинуто повторно. Якщо замикання відпрацює успішно, транзакція автоматично закомітиться. З методом transaction вам не треба перейматися ручним відкотом чи комітом:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});
Обробка взаємних блокувань
Метод transaction приймає необовʼязковий другий аргумент, який визначає, скільки разів транзакцію треба повторити при взаємному блокуванні (deadlock). Коли ці спроби вичерпано, буде кинуто виняток:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, attempts: 5);
Ручне керування транзакціями
Якщо ви хочете почати транзакцію вручну і повністю контролювати відкоти й коміти, скористайтеся методом beginTransaction фасаду DB:
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
Відкотити транзакцію можна методом rollBack:
DB::rollBack();
Нарешті, закомітити транзакцію можна методом commit:
DB::commit();
Методи транзакцій фасаду
DBкерують транзакціями і для конструктора запитів, і для Eloquent ORM.
Підключення до CLI бази даних
Якщо ви хочете підключитися до CLI вашої бази даних, скористайтеся Artisan-командою db:
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
Якщо ви хочете додати у вивід команди кількість рядків у таблицях і деталі представлень (view) бази, передайте відповідно опції --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
));
});
}
Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.