База даних: Початок роботи

Вступ

Майже кожен сучасний веб-застосунок взаємодіє з базою даних. Laravel робить взаємодію з базами даних надзвичайно простою для різноманітних підтримуваних баз даних, використовуючи сирий SQL, гнучкий конструктор запитів та Eloquent ORM. Наразі Laravel надає підтримку першої сторони для п'яти баз даних:

Крім того, 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 installer для створення вашого 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:example@example.com/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 буде використано як хост для з'єднання "read", тоді як 192.168.1.3 буде використано для з'єднання "write". Облікові дані бази даних, префікс, набір символів та всі інші параметри в основному масиві mysql будуть спільними для обох з'єднань. Коли в конфігураційному масиві host існує декілька значень, хост бази даних буде випадково обрано для кожного запиту.

Опція sticky

Опція sticky є необов'язковим значенням, яке можна використовувати для дозволу негайного читання записів, що були записані в базу даних під час поточного циклу запиту. Якщо опція sticky увімкнена і операція "запису" була виконана в базу даних під час поточного циклу запиту, будь-які подальші операції "читання" використовуватимуть з'єднання "запису". Це забезпечує, що будь-які дані, записані під час циклу запиту, можуть бути негайно прочитані з бази даних під час цього ж запиту. Вам вирішувати, чи є це бажаною поведінкою для вашого застосунку.

Виконання 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');
});

Обробка Deadlocks

Метод 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.

Підключення до бази даних 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

Якщо ви хочете включити кількість рядків таблиці та деталі представлення бази даних у вихідні дані команди, ви можете надати параметри --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', 'example@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}