Быстрый старт

Модель 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;
}

Название таблицы

Вы можете указать пользовательскую таблицу, определив атрибут таблицы в модели:

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;
}

Выборки с подзапросами

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\modle\Flight::where('legs', '>', 100)
            ->firstOr(['id', 'legs'], function () {
                // ...
            });

Исключение "не найдено"

Иногда вы можете захотеть вызвать исключение, если модель не найдена. Это может быть очень удобно в контроллерах и маршрутах. Методы findOrFail и firstOrFail извлекут первый результат запроса и, если его нет, вызовут исключение Illuminate\Database\Eloquent\ModelNotFoundException:

$model = app\modle\Flight::findOrFail(1);
$model = app\modle\Flight::where('legs', '>', 100)->firstOrFail();

Извлечение коллекции

Также вы можете использовать методы count, sum и max, предоставляемые строителем запросов, а также другие функции коллекций для работы с коллекциями. Эти методы возвращают скалярное значение, а не экземпляр модели:

$count = app\modle\Flight::where('active', 1)->count();

$max = app\modle\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 для сохранения новой модели. Этот метод вернет экземпляр модели. Однако перед использованием вам нужно указать свойство fillable или guarded на модели, потому что все модели 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)) {
    //
}

Наблюдатели моделей

Используйте ссылку События модели и Observer в 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);
    }
}