Использование модели базы данных Model (стиль Laravel)
Модель webman основана на Eloquent ORM. Каждая таблица базы данных имеет соответствующую «модель» для взаимодействия с этой таблицей. Вы можете использовать модель для запроса данных в таблице, а также для вставки новых записей в таблицу.
Прежде чем начать, убедитесь, что вы настроили подключение к базе данных в файле config/database.php
.
Внимание: для поддержки наблюдателей модели Eloquent ORM требуется дополнительный импорт
composer require "illuminate/events"
пример
Пример модели базы данных
<?php
namespace app\model;
use support\Model;
class User extends Model
{
/**
* Имя таблицы, с которой связана модель
*
* @var string
*/
protected $table = 'user';
/**
* Переопределение первичного ключа, по умолчанию это id
*
* @var string
*/
protected $primaryKey = 'uid';
/**
* Указывает, следует ли автоматически поддерживать временные метки
*
* @var bool
*/
public $timestamps = false;
}
Имя таблицы
Вы можете указать настраиваемую таблицу данных, определив свойство table в модели:
class User extends Model
{
/**
* Имя таблицы, с которой связана модель
*
* @var string
*/
protected $table = 'user';
}
Первичный ключ
Eloquent также предполагает, что каждой таблице базы данных соответствует столбец первичного ключа с именем id. Вы можете определить защищенное свойство $primaryKey для переопределения соглашения.
class User extends Model
{
/**
* Переопределение первичного ключа, по умолчанию это id
*
* @var string
*/
protected $primaryKey = 'uid';
}
Eloquent предполагает, что первичный ключ является целочисленным значением с автогенерацией, что означает, что по умолчанию первичный ключ будет автоматически преобразован в тип int. Если вы хотите использовать непостоянный или нек числовой первичный ключ, вам нужно установить публичное свойство $incrementing на false.
class User extends Model
{
/**
* Указывает, является ли первичный ключ модели инкрементируемым
*
* @var bool
*/
public $incrementing = false;
}
Если ваш первичный ключ не является целым числом, вам нужно установить защищенное свойство $keyType модели как string:
class User extends Model
{
/**
* Тип для автоматически увеличиваемого ID.
*
* @var string
*/
protected $keyType = 'string';
}
Временные метки
По умолчанию Eloquent ожидает, что в вашей таблице данных будут присутствовать поля created_at и updated_at. Если вы не хотите, чтобы Eloquent автоматически управлял этими двумя полями, установите свойство $timestamps в модели на false:
class User extends Model
{
/**
* Указывает, следует ли автоматически поддерживать временные метки
*
* @var bool
*/
public $timestamps = false;
}
Если вам необходимо настроить формат временных меток, установите свойство $dateFormat в вашей модели. Это свойство определяет порядок хранения атрибутов даты в базе данных, а также формат, в котором модель сериализуется в массив или JSON:
class User extends Model
{
/**
* Формат хранения временных меток
*
* @var string
*/
protected $dateFormat = 'U';
}
Если вам необходимо настроить имена полей для хранения временных меток, вы можете установить значения констант CREATED_AT и UPDATED_AT в модели:
class User extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
Подключение к базе данных
По умолчанию модели Eloquent будут использовать подключение к базе данных по умолчанию, настроенное в вашем приложении. Если вы хотите указать для модели другое подключение, установите свойство $connection:
class User extends Model
{
/**
* Имя подключения модели
*
* @var string
*/
protected $connection = 'connection-name';
}
Значения по умолчанию для свойств
Если вы хотите задать значения по умолчанию для некоторых свойств модели, вы можете определить свойство $attributes в модели:
class User extends Model
{
/**
* Значения по умолчанию для модели.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
Извлечение модели
После создания модели и ее связанной таблицы базы данных вы можете запрашивать данные из базы данных. Представьте себе каждую модель Eloquent как мощный конструктор запросов, с помощью которого вы можете быстрее запрашивать связанные данные из таблиц. Например:
$users = app\model\User::all();
foreach ($users as $user) {
echo $user->name;
}
Подсказка: поскольку модели Eloquent также являются конструкторами запросов, вам также следует ознакомиться с доступными методами конструктора запросов. Вы можете использовать эти методы в запросах Eloquent.
Дополнительные ограничения
Метод all Eloquent возвращает все результаты модели. Поскольку каждая модель Eloquent служит конструктором запросов, вы также можете добавить условия запроса, а затем использовать метод get для получения результатов запроса:
$users = app\model\User::where('name', 'like', '%tom')
->orderBy('uid', 'desc')
->limit(10)
->get();
Перезагрузка модели
Вы можете использовать методы fresh и refresh для перезагрузки модели. Метод fresh заново извлекает модель из базы данных. Существующий экземпляр модели не затрагивается:
$user = app\model\User::where('name', 'tom')->first();
$fresh_user = $user->fresh();
Метод refresh использует новые данные из базы данных, чтобы обновить существующую модель. Кроме того, загруженные отношения будут перезагружены:
$user = app\model\User::where('name', 'tom')->first();
$user->name = 'jerry';
$user = $user->fresh();
$user->name; // "tom"
Коллекция
Методы all и get Eloquent могут возвращать несколько результатов и возвращают экземпляр Illuminate\Database\Eloquent\Collection
. Класс Collection
предоставляет множество вспомогательных функций для работы с результатами Eloquent:
$users = $users->reject(function ($user) {
return $user->disabled;
});
Использование курсоров
Метод cursor позволяет вам использовать курсор для обхода базы данных, выполняя только один запрос. При обработке больших объемов данных метод cursor может значительно уменьшить использование памяти:
foreach (app\model\User::where('sex', 1')->cursor() as $user) {
//
}
Метод cursor возвращает экземпляр Illuminate\Support\LazyCollection
. Ленивая коллекция позволяет вам использовать большинство методов коллекций Laravel, при этом загружая в память только одну модель за раз:
$users = app\model\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Подзапросы selects
Eloquent предоставляет расширенную поддержку подзапросов; вы можете извлекать данные из связанных таблиц с помощью одного запроса. Например, предположим, что у нас есть таблица направлений destinations и таблица рейсов flights. Таблица flights содержит поле arrival_at, указывающее, когда рейс прибывает в пункт назначения.
Используя методы select и addSelect, предлагаемые функциональностью подзапросов, мы можем запросить все направления destinations и имена последних рейсов, прибывающих в каждое направление, с помощью одного запроса:
use app\model\Destination;
use app\model\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
Сортировка по подзапросу
Кроме того, функция orderBy в конструкторе запросов также поддерживает подзапросы. Мы можем использовать эту функцию, чтобы отсортировать все направления в зависимости от времени прибытия последнего рейса. Это также позволяет выполнять только один запрос к базе данных:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
Извлечение одной модели / коллекции
В дополнение к извлечению всех записей из указанной таблицы, вы можете использовать методы find, first или firstWhere для извлечения одной записи. Эти методы возвращают единичный экземпляр модели, а не коллекцию моделей:
// Найти модель по первичному ключу...
$flight = app\model\Flight::find(1);
// Найти первую модель, соответствующую условиям запроса...
$flight = app\model\Flight::where('active', 1)->first();
// Быстрая реализация для нахождения первой модели, соответствующей условиям запроса...
$flight = app\model\Flight::firstWhere('active', 1);
Вы также можете использовать массив первичных ключей в качестве параметров для вызова метода find, который вернет коллекцию записей, соответствующих условиям:
$flights = app\model\Flight::find([1, 2, 3]);
Иногда вы можете захотеть выполнить дополнительные действия, если не удается найти первое значение. Метод firstOr возвратит первое найденное значение, если результат найден, в противном случае выполнит данный обратный вызов. Возвращаемое значение обратного вызова будет возвращено методом firstOr:
$model = app\model\Flight::where('legs', '>', 100)->firstOr(function () {
// ...
});
Метод firstOr также принимает массив полей для запроса:
$model = app\model\Flight::where('legs', '>', 100)
->firstOr(['id', 'legs'], function () {
// ...
});
Исключение «Не найдено»
Иногда вы можете захотеть вызывать исключение, если модель не найдена. Это удобно в контроллерах и маршрутах. Методы findOrFail и firstOrFail получат первый результат запроса и выбросят исключение Illuminate\Database\Eloquent\ModelNotFoundException, если результат не найден:
$model = app\model\Flight::findOrFail(1);
$model = app\model\Flight::where('legs', '>', 100)->firstOrFail();
Извлечение коллекции
Вы также можете использовать методы count, sum и max, предоставляемые конструктором запросов, и другие функции коллекции для работы с коллекциями. Эти методы возвращают только соответствующие скалярные значения, а не экземпляр модели:
$count = app\model\Flight::where('active', 1)->count();
$max = app\model\Flight::where('active', 1)->max('price');
Вставка
Чтобы добавить новую запись в базу данных, создайте новый экземпляр модели, установите свойства экземпляра, а затем вызовите метод save:
<?php
namespace app\controller;
use app\model\User;
use support\Request;
use support\Response;
class FooController
{
/**
* Добавить новую запись в таблице пользователей
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// Проверка запроса
$user = new User;
$user->name = $request->get('name');
$user->save();
}
}
Временные метки created_at и updated_at будут установлены автоматически (если свойство $timestamps в модели установлено в true), поэтому не нужно вручную задавать значения.
Обновление
Метод save также может быть использован для обновления уже существующей модели в базе данных. Чтобы обновить модель, вам нужно сначала извлечь её, установить необходимые свойства для обновления, а затем вызвать метод save. Аналогично, временная метка updated_at будет автоматически обновлена, так что тоже нет необходимости задавать значение вручную:
$user = app\model\User::find(1);
$user->name = 'jerry';
$user->save();
Массовое обновление
app\model\User::where('uid', '>', 10)
->update(['name' => 'tom']);
Проверка изменений свойства
Eloquent предоставляет методы isDirty, isClean и wasChanged для проверки внутреннего состояния модели и определения, как изменились ее свойства с момента первоначальной загрузки.
Метод isDirty определяет, были ли изменены какие-либо свойства с момента загрузки модели. Вы можете передать конкретное имя свойства, чтобы определить, изменилось ли оно. Метод isClean работает противоположно isDirty и также принимает необязательный параметр свойства:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
Метод wasChanged определяет, были ли изменены какие-либо свойства последний раз, когда модель была сохранена в текущем запросе. Вы также можете передать название свойства, чтобы проверить, было ли оно изменено:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false
Массовая установка значений
Вы также можете использовать метод create для сохранения новой модели. Этот метод возвращает экземпляр модели. Однако прежде чем использовать его, вам необходимо определить,какие свойства допустимо массово заполнять в модели, так как все модели Eloquent по умолчанию недоступны для массового заполнения.
Когда пользователь передает неожиданные параметры HTTP через запрос, и этот параметр изменяет поле, которое вам не нужно изменять в базе данных, может возникнуть уязвимость массового заполнения. Например, злонамеренный пользователь может передать параметр is_admin через HTTP-запрос и затем передать его в метод create, что позволит пользователю повысить свои права до администратора.
Таким образом, прежде всего вам следует определить, какие свойства модели могут быть массово заполняемыми. Вы можете сделать это с помощью свойства $fillable на модели. Например: разрешим свойству name модели Flight быть массово заполняемым:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Свойства, которые могут быть массово заполняемыми.
*
* @var array
*/
protected $fillable = ['name'];
}
После того как мы установили, какие свойства могут быть массово заполняемыми, мы можем использовать метод create для вставки новых данных в базу данных. Метод create вернет сохраненный экземпляр модели:
$flight = app\model\Flight::create(['name' => 'Flight 10']);
Если у вас уже есть экземпляр модели, вы можете передать массив в метод fill для установки значений:
$flight->fill(['name' => 'Flight 22']);
$fillable можно считать «черным списком» для массового заполнения, вы также можете использовать свойство $guarded для этого. Свойство $guarded содержит массив недоступных для массового заполнения. То есть, $guarded следует рассматривать больше как «черный список». Обратите внимание, что вы можете использовать только одно из свойств $fillable или $guarded — не используйте их одновременно. В следующем примере все свойства, кроме свойства price, могут быть массово заполняемыми:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Свойства, которые нельзя массово заполнять.
*
* @var array
*/
protected $guarded = ['price'];
}
Если вы хотите, чтобы все свойства были доступны для массового заполнения, вы можете определить $guarded как пустой массив:
/**
* Свойства, которые нельзя массово заполнять.
*
* @var array
*/
protected $guarded = [];
Другие методы создания
firstOrCreate/ firstOrNew
Вот два метода, которые вы можете использовать для массового заполнения: firstOrCreate и firstOrNew. Метод firstOrCreate пытается сопоставить данные в базе данных с помощью заданной пары ключ / значение. Если модель не найдена в базе данных, будет вставлена запись, содержащая свойства первого параметра и дополнительные необязательные свойства из второго параметра.
Метод firstOrNew работает аналогично firstOrCreate, пытаясь найти запись в базе данных с заданными свойствами. Однако, если метод firstOrNew не находит соответствующей модели, он вернет новый экземпляр модели. Обратите внимание, что экземпляр модели, возвращаемый firstOrNew, еще не сохранен в базе данных, и вам необходимо вручную вызвать метод save, чтобы сохранить его:
// Найти рейс по name, если не существует, то создать новое...
$flight = app\model\Flight::firstOrCreate(['name' => 'Flight 10']);
// Найти рейс по name или создать с использованием свойств name, delayed и arrival_time...
$flight = app\model\Flight::firstOrCreate(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Найти рейс по name, если не существует, то создать новый экземпляр...
$flight = app\model\Flight::firstOrNew(['name' => 'Flight 10']);
// Найти рейс по name или создать экземпляр с использованием свойств name, delayed и arrival_time...
$flight = app\model\Flight::firstOrNew(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Вы также можете столкнуться со сценарием, в котором хотите обновить существующую модель или создать новую, если таковая отсутствует. Метод updateOrCreate реализует это в одном шаге. Подобно методу firstOrCreate, updateOrCreate сохраняет модель, поэтому вам не нужно вызывать save():
// Если существует рейс из Окленда в Сан-Диего, его цена составит 99 долларов.
// Если не будет совпадений с существующей моделью, будет создана новая.
$flight = app\model\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Удаление модели
Вы можете вызвать метод delete на экземпляре модели, чтобы удалить его:
$flight = app\model\Flight::find(1);
$flight->delete();
Удаление модели по первичному ключу
app\model\Flight::destroy(1);
app\model\Flight::destroy(1, 2, 3);
app\model\Flight::destroy([1, 2, 3]);
app\model\Flight::destroy(collect([1, 2, 3]));
Удаление модели через запрос
$deletedRows = app\model\Flight::where('active', 0)->delete();
Копирование модели
Вы можете использовать метод replicate, чтобы скопировать новый экземпляр, который еще не сохранен в базе данных. Этот метод очень полезен, когда экземпляры модели имеют много одинаковых свойств.
$shipping = App\Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
Сравнение моделей
Иногда вам может понадобиться определить, являются ли две модели «одинаковыми». Метод is можно использовать для быстрой проверки, имеют ли две модели одинаковый первичный ключ, таблицу и подключение к базе данных:
if ($post->is($anotherPost)) {
//
}
Наблюдатели модели
Используйте ссылку События модели и наблюдатель в Laravel
Внимание: для поддержки наблюдателей модели Eloquent ORM требуется дополнительный импорт composer require "illuminate/events"
.
<?php
namespace app\model;
use support\Model;
use app\observer\UserObserver;
class User extends Model
{
public static function boot()
{
parent::boot();
static::observe(UserObserver::class);
}
}
Транзакции
Смотрите Транзакции базы данных