Может войдёшь?
Черновики Написать статью Профиль

Eloquent ORM

перевод документация 4.x

  1. 1. Введение
  2. 2. Простейшее использование
  3. 3. Массовое заполнение
  4. 4. Вставка, обновление, удаление
    1. 4.1. Мягкое удаление
  5. 5. Поля времени
  6. 6. Заготовки запросов
  7. 7. Глобальные заготовки (scopes)
  8. 8. Отношения
    1. 8.1. Один к одному
    2. 8.2. Один ко многим
    3. 8.3. Многие ко многим
    4. 8.4. Ко многим через
    5. 8.5. Полиморфические отношения
    6. 8.6. Полиморфические связи многие ко многим
    7. 8.7. Запросы к отношениям
  9. 9. Динамические свойства
  10. 10. Активная загрузка
    1. 10.1. Ограничения активной загрузки
    2. 10.2. Ленивая активная загрузка
  11. 11. Вставка связанных моделей
    1. 11.1. Связывание моделей (belongs to)
    2. 11.2. Связывание моделей (многие ко многим)
  12. 12. Обновление времени владельца
  13. 13. Работа со связующими таблицами
  14. 14. Коллекции
  15. 15. Читатели и преобразователи
    1. 15.1. Преобразователи дат
  16. 16. События моделей
  17. 17. Наблюдатели моделей
  18. 18. Преобразование в массивы и JSON
Этот перевод актуален для англоязычной документации на (ветка 4.2) , (ветка 4.1) и (ветка 4.0). Опечатка? Выдели и нажми Ctrl+Enter.

Введение

Система объектно-реляционного отображения (ORM) Eloquent — красивая и простая реализация шаблона ActiveRecord в Laravel для работы с базами данных. Каждая таблица имеет соответствующий класс-модель, который используется для работы с этой таблицей.

Прежде чем начать настройте ваше соединение с БД в файле app/config/database.php.

Простейшее использование

Для начала создадим модель Eloquent. Модели обычно располагаются в папке app/models, но вы можете поместить в любое место, в котором работает автозагрузчик в соответствии с вашим файлом composer.json.

Создание модели Eloquent

PHP
class User extends Eloquent {}

Заметьте, что мы не указали, какую таблицу Eloquent должен привязать к нашей модели. Если это имя не указано явно, то будет использовано имя класса в нижнем регистре и во множественном числе. В нашем случае Eloquent предположит, что модель PHPUser хранит свои данные в таблице users. Вы можете указать произвольную таблицу, определив свойство PHP$table в классе модели:

PHP
class User extends Eloquent {

  protected 
$table 'my_users';

}

Внимание: Eloquent также предполагает, что каждая таблица имеет первичный ключ с именем PHPid. Вы можете определить свойство PHP$primaryKey для изменения этого имени. Аналогичным образом, вы можете определить свойство PHP$connection для задания имени подключения к БД, которое должно использоваться при работе с данной моделью.

Как только модель определена, у вас всё готово для того, чтобы можно было выбирать и создавать записи. Обратите внимание, что вам нужно создать в этой таблице поля updated_at и created_at. Если вы не хотите, чтобы они были автоматически используемы, установите свойство PHP$timestamps класса модели в PHPfalse.

Получение всех моделей (записей)

PHP
$users User::all();

Получение записи по первичному ключу

PHP
$user User::find(1);

var_dump($user->name);

Все методы, доступные в конструкторе запросов, также доступны при работе с моделями Eloquent.

Получение модели по первичному ключу с возбуждением исключения

Иногда вам нужно возбудить исключение, если определённая модель не была найдена, что позволит вам его отловить в обработчике PHPApp::error() и вывести страницу 404 («Не найдено»).

PHP
$model User::findOrFail(1);

$model User::where('votes''>'100)->firstOrFail();

Для регистрации обработчика ошибки подпишитесь на событие ModelNotFoundException:

PHP
use Illuminate\Database\Eloquent\ModelNotFoundException;

App::error(function (ModelNotFoundException $e) {
  return 
Response::make('Not Found'404);
});

Построение запросов в моделях Eloquent

PHP
$users User::where('votes''>'100)->take(10)->get();

foreach (
$users as $user) {
  
var_dump($user->name);
}

Агрегатные функции в Eloquent

Конечно, вам также доступны агрегатные функции.

PHP
$count User::where('votes''>'100)->count();

Если у вас не получается создать нужный запрос с помощью конструктора, то можно использовать метод PHPwhereRaw():

PHP
$users User::whereRaw('age > ? and votes = 100', array(25))->get();
+ 4.1

добавлено в 4.1 ()

Разделение результата на блоки

Если вам нужно обработать много (тысячи) записей Eloquent, использование команды PHPchunk (блок — прим. пер.) позволит вам избежать заполнения всей вашей оперативной памяти:

PHP
User::chunk(200, function($users)
{
  foreach (
$users as $user)
  {
    
//
  
}
});

Первый передаваемый в метод аргумент — число записей, получаемых в одном блоке. Передаваемая в качестве второго аргумента функция-замыкание будет вызываться для каждого блока, получаемого из БД.

Указание имени соединения с БД

Иногда вам нужно указать, какое подключение должно быть использовано при выполнении запроса Eloquent — просто используйте метод PHPon():

PHP
$user User::on('имя-соединения')->find(1);

Массовое заполнение

При создании новой модели вы передаёте её конструктору массив атрибутов. Эти атрибуты затем присваиваются модели через массовое заполнение. Это удобно, но в то же время представляет серьёзную проблему с безопасностью, когда вы передаёте ввод от клиента в модель без проверок — в этом случае пользователь может изменить любое и каждое поле вашей модели. По этой причине по умолчанию Eloquent защищает вас от массового заполнения.

Для начала определите в классе модели свойство PHP$fillable или PHP$guarded.

Указание доступных к заполнению атрибутов

Свойство PHP$fillable указывает, какие поля должны быть доступны при массовом заполнении. Их можно указать на уровне класса или объекта.

PHP
class User extends Eloquent {

  protected 
$fillable = array('first_name''last_name''email');

}

В этом примере только три перечисленных поля будут доступны массовому заполнению.

Указание охраняемых (guarded) атрибутов модели

Противоположность PHP$fillable — свойство PHP$guarded, которое содержит список запрещённых к заполнению полей:

PHP
class User extends Eloquent {

  protected 
$guarded = array('id''password');

}

При использовании PHPguarded, вы по-прежнему не должны передавать PHPInput::get() или любые сырые массивы пользовательского ввода в методы PHPsave и PHPupdate, потому что может быть обновлён любой незащищённый столбец.

Защита всех атрибутов от массового заполнения

В примере выше атрибуты PHPid и PHPpassword не могут быть присвоены через массовое заполнение. Все остальные атрибуты — могут. Вы также можете запретить все атрибуты для заполнения, используя символ PHP*:

PHP
protected $guarded = array('*');

Вставка, обновление, удаление

Для создания новой записи в БД просто создайте экземпляр модели и вызовите метод PHPsave().

Сохранение новой модели

PHP
$user = new User;

$user->name 'Джон';

$user->save();

Внимание: обычно ваши модели Eloquent содержат автоматические числовые ключи (autoincrementing). Однако если вы хотите использовать собственные ключи, установите свойство PHP$incrementing класса модели в значение PHPfalse.

Вы также можете использовать метод PHPcreate() для создания и сохранения модели одной строкой. Метод вернёт добавленную модель. Однако перед этим вам нужно определить либо свойство PHP$fillable, либо PHP$guarded в классе модели, так как изначально все модели Eloquent защищены от массового заполнения.

После сохранения или создания новой модели, использующей автоматические (autoincrementing) ID, вы можете получать ID объектов, обращаясь к их атрибуту PHPid:

PHP
$insertedId $user->id;

Установка охранных свойств модели

PHP
class User extends Eloquent {

  protected 
$guarded = array('id''account_id');

}

Создание модели

PHP
// Создание нового пользователя в БД...
$user User::create(array('name' => 'Джон'));

// Получение пользователя по свойствам, или его создание, если такого не существует...
$user User::firstOrCreate(array('name' => 'Джон'));

// Получение пользователя по свойствам, или создание нового экземпляра...
$user User::firstOrNew(array('name' => 'Джон'));

Методы PHPfirstOrCreate и PHPfirstOrNew появились в версии 4.1 — прим. пер.

Обновление полученной модели

Для обновления модели вам нужно получить её, изменить атрибут и вызвать метод PHPsave():

PHP
$user User::find(1);

$user->email 'john@foo.com';

$user->save();

Сохранение модели и её отношений

Иногда вам может быть нужно сохранить не только модель, но и все её отношения. Для этого просто используйте метод PHPpush():

PHP
$user->push();

Вы также можете выполнять обновления в виде запросов к набору моделей:

PHP
$affectedRows User::where('votes''>'100)->update(array('status' => 2));

При обновлении набора моделей с помощью конструктора запросов Eloquent никакие события моделей не срабатывают.

Удаление существующей модели

Для удаления модели вызовите метод PHPdelete() на её объекте:

PHP
$user User::find(1);

$user->delete();

Удаление модели по ключу

PHP
User::destroy(1);

User::destroy(array(123));

User::destroy(123);

Конечно, вы также можете выполнять удаление на наборе моделей:

PHP
$affectedRows User::where('votes''>'100)->delete();

Обновление времени изменения модели

Если вам нужно просто обновить время изменения записи — используйте метод PHPtouch():

PHP
$user->touch();

Мягкое удаление

Когда вы «мягко» удаляете модель, она на самом деле остаётся в базе данных, однако устанавливается её поле deleted_at.

+ 4.2

добавлено в 4.2 ()

Для включения мягких удалений на модели примените к ней PHPSoftDeletingTrait

PHP
use Illuminate\Database\Eloquent\SoftDeletingTrait;

class 
User extends Eloquent {

  use 
SoftDeletingTrait;

  protected 
$dates = ['deleted_at'];

}
+ 4.1 4.0

добавлено в 4.1 () 4.0 ()

Для включения мягких удалений на модели определите её свойство PHP$softDelete:

PHP
class User extends Eloquent {

  protected 
$softDelete true;

}

Для добавления поля deleted_at к таблице можно использовать метод PHPsoftDeletes() из миграции:

PHP
$table->softDeletes();

Теперь когда вы вызовите метод PHPdelete(), поле deleted_at будет установлено в значение текущего времени. При запросе моделей, использующих мягкое удаление, «удалённые» модели не будут включены в результат запроса.

Включение удалённых моделей в результат выборки

Для отображения всех моделей, в том числе удалённых, используйте метод PHPwithTrashed():

PHP
$users User::withTrashed()->where('account_id'1)->get();

Метод PHPwithTrashed может быть использован в отношениях:

PHP
$user->posts()->withTrashed()->get();

Если вы хотите получить только удалённые модели, вызовите метод PHPonlyTrashed():

PHP
$users User::onlyTrashed()->where('account_id'1)->get();

Для восстановления мягко удалённой модели в активное состояние используется метод PHPrestore():

PHP
$user->restore();

Вы также можете использовать его в запросе:

PHP
User::withTrashed()->where('account_id'1)->restore();

Как и метод PHPwithTrashed, метод PHPrestore() можно использовать и в отношениях:

PHP
$user->posts()->restore();

Если вы хотите полностью удалить модель из БД, используйте метод PHPforceDelete():

PHP
$user->forceDelete();

Он также работает с отношениями:

PHP
$user->posts()->forceDelete();

Для того, чтобы узнать, удалена ли модель, можно использовать метод PHPtrashed():

PHP
if ($user->trashed()) {
  
//
}

Поля времени

По умолчанию Eloquent автоматически поддерживает поля created_at и updated_at. Просто добавьте эти timestamp-поля к таблице и Eloquent позаботится об остальном. Если вы не хотите, чтобы он поддерживал их, добавьте свойство PHP$timestamps к классу модели.

Отключение автоматических полей времени

PHP
class User extends Eloquent {

  protected 
$table 'users';

  public 
$timestamps false;

}

Использование собственного формата времени

Для настройки формата времени перекройте метод PHPgetDateFormat():

PHP
class User extends Eloquent {

  protected function 
getDateFormat()
  {
    return 
'U';
  }

}

Заготовки запросов

Создание заготовки запроса

Заготовки позволяют вам повторно использовать логику запросов в моделях. Для создания заготовки просто начните имя метода со scope:

PHP
class User extends Eloquent {

  public function 
scopePopular($query)
  {
    return 
$query->where('votes''>'100);
  }

  public function 
scopeWomen($query)
  {
    return 
$query->whereGender('W');
  }

}

Использование заготовки

PHP
$users User::popular()->women()->orderBy('created_at')->get();

Динамические заготовки

Иногда вам может потребоваться определить заготовку, которая принимает параметры. Для этого просто добавьте эти параметры к методу заготовки:

PHP
class User extends Eloquent {

  public function 
scopeOfType($query$type)
  {
    return 
$query->whereType($type);
  }

}

А затем передайте их при вызове метода заготовки:

PHP
$users User::ofType('member')->get();
+ 4.2

добавлено в 4.2 ()

Глобальные заготовки (scopes)

Иногда вам требуется определить заготовку, которая будет применяться для всех выполняемых в модели запросов. По сути так и работает «мягкое удаление» в Eloquent. Глобальные заготовки определяются с помощью комбинации типажей PHP и реализации Illuminate\Database\Eloquent\ScopeInterface.

Сначала определим типаж. В этом примере мы будем использовать встроенный в Laravel PHPSoftDeletingTrait:

PHP
trait SoftDeletingTrait {

  
/**
   * Загрузка типажа мягкого удаления для модели.
   *
   * @return void
   */
  
public static function bootSoftDeletingTrait()
  {
    static::
addGlobalScope(new SoftDeletingScope);
  }

}

Если в модели Eloquent используется типаж, содержащий соответствующий соглашению по названиям bootNameOfTrait метод, тогда этот метод типажа будет вызываться при загрузке модели Eloquent. Это даёт вам возможность зарегистрировать глобальную заготовку, или сделать ещё что-либо необходимое. Заготовка должна реализовывать PHPScopeInterface, который содержит два метода: PHPapply() и PHPremove().

Метод PHPapply() принимает объект конструктора запросов Illuminate\Database\Eloquent\Builder и отвечает за добавление любых дополнительных операторов where, которые необходимы заготовке.
Метод PHPremove() также принимает объект Builder и отвечает за отмену действий, произведённых методом PHPapply(). Другими словами, PHPremove() должен удалить добавленные операторы where (или любые другие).
Поэтому для нашей PHPSoftDeletingScope методы будут такими:

PHP
/**
 * Применение заготовки к указанному конструктору запросов Eloquent.
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $builder
 * @return void
 */
public function apply(Builder $builder)
{
  
$model $builder->getModel();

  
$builder->whereNull($model->getQualifiedDeletedAtColumn());
}

/**
 * Удаление заготовки из указанного конструктора запросов Eloquent.
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $builder
 * @return void
 */
public function remove(Builder $builder)
{
  
$column $builder->getModel()->getQualifiedDeletedAtColumn();

  
$query $builder->getQuery();

  foreach ((array) 
$query->wheres as $key => $where)
  {
    
// Если оператор where ограничивает мягкое удаление данных, мы удалим его из
    // запроса и сбросим ключи в операторах where. Это позволит разработчику
    // включить удалённую модель в отношения результирующего набора, который загружается "лениво".
    
if ($this->isSoftDeleteConstraint($where$column))
    {
      unset(
$query->wheres[$key]);

      
$query->wheres array_values($query->wheres);
    }
  }
}

Отношения

Конечно, ваши таблицы скорее всего как-то связаны с другими таблицами БД. Например, статья в блоге может иметь много комментариев, а заказ может быть связан с оставившим его пользователем. Eloquent упрощает работу и управление такими отношениями. Laravel поддерживает многие типы связей:

  1. Один к одному
  2. Один ко многим
  3. Многие ко многим
  4. Ко многим через
  5. Полиморфические связи
  6. Полиморфические связи «многие ко многим»

Один к одному

Создание связи «один к одному»

Связь вида «один к одному» является очень простой. К примеру, модель PHPUser может иметь один PHPPhone. Мы можем определить такое отношение в Eloquent:

PHP
class User extends Eloquent {

  public function 
phone()
  {
    return 
$this->hasOne('Phone');
  }

}

Первый параметр, передаваемый PHPhasOne() — имя связанной модели. Как только отношение установлено вы можете получить к нему доступ через динамические свойства Eloquent:

PHP
$phone User::find(1)->phone;

Сгенерированный SQL имеет такой вид:

sqlselect * from users where id = 1

select * from phones where user_id = 1

Заметьте, что Eloquent считает, что поле в таблице называется по имени модели плюс _id. В данном случае предполагается, что это user_id. Если вы хотите перекрыть стандартное имя передайте второй параметр методу PHPhasOne(). Кроме того вы можете передать в метод третий аргумент, чтобы указать, какие локальные столбцы следует использовать для объединения:

PHP
return $this->hasOne('Phone''foreign_key');

return 
$this->hasOne('Phone''foreign_key''local_key');

Создание обратного отношения

Для создания обратного отношения в модели PHPPhone используйте метод PHPbelongsTo() («принадлежит к»):

PHP
class Phone extends Eloquent {

  public function 
user()
  {
    return 
$this->belongsTo('User');
  }

}

В примере выше Eloquent будет искать поле user_id в таблице phones. Если вы хотите назвать внешний ключ по другому, передайте это имя вторым параметром к методу PHPbelongsTo():

PHP
class Phone extends Eloquent {

  public function 
user()
  {
    return 
$this->belongsTo('User''local_key');
  }

}

Кроме того, вы передаёте третий параметр, который определяет имя связанного столбца в родительской таблице:

PHP
class Phone extends Eloquent {

  public function 
user()
  {
    return 
$this->belongsTo('User''local_key''parent_key');
  }

}

Один ко многим

Примером отношения «один ко многим» является статья в блоге, которая имеет «много» комментариев. Вы можете смоделировать это отношение таким образом:

PHP
class Post extends Eloquent {

  public function 
comments()
  {
    return 
$this->hasMany('Comment');
  }

}

Теперь мы можем получить все комментарии с помощью динамического свойства:

PHP
$comments Post::find(1)->comments;

Если вам нужно добавить ограничения на получаемые комментарии, можно вызвать метод PHPcomments() и продолжить добавлять условия:

PHP
$comments Post::find(1)->comments()->where('title''=''foo')->first();

Как обычно, вы можете передать второй параметр в метод PHPhasMany() для перекрытия стандартного имени ключа. И как для отношения «hasOne» также может быть указан локальный столбец:

PHP
return $this->hasMany('Comment''foreign_key');

return 
$this->hasMany('Comment''foreign_key''local_key');

Определение обратного отношения

Для определения обратного отношения используйте метод PHPbelongsTo():

PHP
class Comment extends Eloquent {

  public function 
post()
  {
    return 
$this->belongsTo('Post');
  }

}

Многие ко многим

Отношения типа «многие ко многим» — более сложные, чем остальные виды отношений. Примером может служить пользователь, имеющий много ролей, где роли также относятся ко многим пользователям. Например, один пользователь может иметь роль admin. Нужны три таблицы для этой связи: users, roles и role_user. Название таблицы role_user происходит от упорядоченных по алфавиту имён связанных моделей и она должна иметь поля user_id и role_id.

Вы можете определить отношение «многие ко многим» через метод PHPbelongsToMany():

PHP
class User extends Eloquent {

  public function 
roles()
  {
    return 
$this->belongsToMany('Role');
  }

}

Теперь мы можем получить роли через модель PHPUser:

PHP
$roles User::find(1)->roles;

Вы можете передать второй параметр к методу PHPbelongsToMany() с указанием имени связующей (pivot) таблицы вместо стандартной:

PHP
return $this->belongsToMany('Role''user_roles');

Вы также можете перекрыть имена ключей по умолчанию:

PHP
return $this->belongsToMany('Role''user_roles''user_id''foo_id');

Конечно, вы можете определить и обратное отношение на модели PHPRole:

PHP
class Role extends Eloquent {

  public function 
users()
  {
    return 
$this->belongsToMany('User');
  }

}
+ 4.1

добавлено в 4.1 ()

Ко многим через

Связь «ко многим через» обеспечивает удобный короткий путь для доступа к удалённым отношениям через промежуточные. Например, модель Country может иметь много Post через модель User. Таблицы для этих отношений будут выглядеть так:

PHP
countries
  id 
integer
  name 
string

users
  id 
integer
  country_id 
integer
  name 
string

posts
  id 
integer
  user_id 
integer
  title 
string

Несмотря на то, что таблица posts не содержит столбца country_id, отношение «hasManyThrough» позволит нам получить доступ к posts через country с помощью PHP$country->posts. Давайте определим отношения:

PHP
class Country extends Eloquent {

  public function 
posts()
  {
    return 
$this->hasManyThrough('Post''User');
  }

}

Если вы хотите указать ключи отношений вручную, вы можете передать их в качестве третьего и четвертого аргументов метода:

PHP
class Country extends Eloquent {

  public function 
posts()
  {
    return 
$this->hasManyThrough('Post''User''country_id''user_id');
  }

}

Полиморфические отношения

Полиморфические отношения позволяют модели быть связанной с более, чем одной моделью. Например, может быть модель PHPPhoto, содержащая записи, принадлежащие к моделям PHPStaff и PHPOrder. Мы можем создать такое отношение таким образом:

PHP
class Photo extends Eloquent {

  public function 
imageable()
  {
    return 
$this->morphTo();
  }

}

class 
Staff extends Eloquent {

  public function 
photos()
  {
    return 
$this->morphMany('Photo''imageable');
  }

}

class 
Order extends Eloquent {

  public function 
photos()
  {
    return 
$this->morphMany('Photo''imageable');
  }

}

Чтение полиморфической связи

Теперь мы можем получить фотографии и для персонала, и для заказа:

PHP
$staff Staff::find(1);

foreach (
$staff->photos as $photo) {
  
//
}

Чтение связи на владельце полиморфического отношения

Однако истинная «магия» полиморфизма происходит при чтении связи на модели PHPPhoto:

PHP
$photo Photo::find(1);

$imageable $photo->imageable;

Отношение PHPimageable модели PHPPhoto вернёт либо объект PHPStaff, либо объект PHPOrder в зависимости от типа модели, которой принадлежит фотография.

Структура таблиц полиморфической связи

Чтобы понять, как это работает, давайте изучим структуру БД для полиморфического отношения:

staff
  id - integer
  name - string

orders
  id - integer
  price - integer

photos
  id - integer
  path - string
  imageable_id - integer
  imageable_type - string

Главные поля, на которые нужно обратить внимание: imageable_id и imageable_type в таблице photos. Первое содержит ID владельца, в нашем случае — заказа или персонала, а второе — имя класса-модели владельца. Это позволяет ORM определить, какой класс модели должен быть возвращён при использовании отношения PHPimageable.

+ 4.1

добавлено в 4.1 ()

Полиморфические связи многие ко многим

Структура таблиц полиморфической связи многие ко многим

В дополнение к традиционным полиморфическим связям вы можете также задать полиморфические связи многие ко многим. Например, модели блогов Post и Video могут разделять полиморфическую связь с моделью Tag. Во-первых, давайте рассмотрим структуру таблиц:

PHP
posts
  id 
integer
  name 
string

videos
  id 
integer
  name 
string

tags
  id 
integer
  name 
string

taggables
  tag_id 
integer
  taggable_id 
integer
  taggable_type 
string

Далее, мы готовы к установке связи с моделью. Обе модели Post и Video будут иметь связь «morphToMany» через метод PHPtags:

PHP
class Post extends Eloquent {

  public function 
tags()
  {
    return 
$this->morphToMany('Tag''taggable');
  }

}

Модель Tag может определить метод для каждого из своих отношений:

PHP
class Tag extends Eloquent {

  public function 
posts()
  {
    return 
$this->morphedByMany('Post''taggable');
  }

  public function 
videos()
  {
    return 
$this->morphedByMany('Video''taggable');
  }

}

Запросы к отношениям

Проверка связей при выборке

При чтении отношений модели вам может быть нужно ограничить результаты в зависимости от существования связи. Например, вы хотите получить все статьи в блоге, имеющие хотя бы один комментарий. Для этого можно использовать метод PHPhas():

PHP
$posts Post::has('comments')->get();

Вы также можете указать оператор и число:

PHP
$posts Post::has('comments''>='3)->get();
+ 4.1

добавлено в 4.1 ()

Если вам нужно ещё больше возможностей, вы можете использовать методы PHPwhereHas и PHPorWhereHas, чтобы поместить условия "where" в ваши запросы has:

PHP
$posts Post::whereHas('comments', function($q)
{
  
$q->where('content''like''foo%');

})->
get();

Динамические свойства

Eloquent позволяет вам читать отношения через динамические свойства. Eloquent автоматически определит используемую связь и даже вызовет PHPget() для связей «один ко многим» и PHPfirst() — для связей «один к одному». К примеру, для следующей модели PHP$phone:

PHP
class Phone extends Eloquent {

  public function 
user()
  {
      return 
$this->belongsTo('User');
  }

}

$phone Phone::find(1);

Вместо того, чтобы получить e-mail пользователя так:

PHP
echo $phone->user()->first()->email;

...вызов может быть сокращён до такого:

PHP
echo $phone->user->email;

Отношения, которые возвращают много результатов, вернут экземпляр класса PHPIlluminate\Database\Eloquent\Collection.

Активная загрузка

Активная загрузка (eager loading) призвана устранить проблему запросов N + 1. Например, представьте, что у нас есть модель PHPBook со связью к модели PHPAuthor. Отношение определено как:

PHP
class Book extends Eloquent {

  public function 
author()
  {
      return 
$this->belongsTo('Author');
  }

}

Теперь, у нас есть такой код:

PHP
foreach (Book::all() as $book) {
  echo 
$book->author->name;
}

Цикл выполнит один запрос для получения всех книг в таблице, а затем будет выполнять по одному запросу на каждую книгу для получения автора. Таким образом, если у нас 25 книг, то потребуется 26 запросов.

К счастью, мы можем использовать активную загрузку для кардинального уменьшения числа запросов. Отношение будет активно загружено, если оно было указано при вызове метода PHPwith():

PHP
foreach (Book::with('author')->get() as $book) {
  echo 
$book->author->name;
}

В цикле выше будут выполнены всего два запроса:

sqlselect * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)

Разумное использование активной загрузки поможет сильно повысить производительность вашего приложения.

Конечно, вы можете загрузить несколько отношений одновременно:

PHP
$books Book::with('author''publisher')->get();

Вы даже можете загрузить вложенные отношения:

PHP
$books Book::with('author.contacts')->get();

В примере выше, связь PHPauthor будет активно загружена вместе со связью PHPcontacts модели автора.

Ограничения активной загрузки

Иногда вам может быть нужно не только активно загрузить отношение, но также указать условие для его загрузки:

PHP
$users User::with(array('posts' => function($query)
{
  
$query->where('title''like''%первое%');

}))->
get();

В этом примере мы загружаем сообщения пользователя, но только те, заголовок которых содержит подстроку «первое».

+ 4.1

добавлено в 4.1 ()

Конечно, функции-замыкания активной загрузки не ограничиваются только условиями. Вы также можете применить упорядочивание:

PHP
$users User::with(array('posts' => function($query)
{
  
$query->orderBy('created_at''desc');

}))->
get();

Ленивая активная загрузка

Возможно активно загрузить связанные модели напрямую из уже созданного набора объектов моделей. Это может быть полезно при определении во время выполнения, требуется ли такая загрузка или нет, или в комбинации с кэшированием.

PHP
$books Book::all();

$books->load('author''publisher');

Вставка связанных моделей

Создание связанной модели

Часто вам нужно будет добавить связанную модель. Например, вы можете создать новый комментарий к сообщению. Вместо явного указания значения для поля post_id вы можете вставить модель через её владельца — модели PHPPost:

PHP
$comment = new Comment(array('message' => 'Новый комментарий.'));

$post Post::find(1);

$comment $post->comments()->save($comment);

В этом примере поле post_id вставленного комментария автоматически получит значение ID своей статьи.

+ 4.2

добавлено в 4.2 ()

Сохранить несколько связанных моделей можно так:

PHP
$comments = array(
  new 
Comment(array('message' => 'A new comment.')),
  new 
Comment(array('message' => 'Another comment.')),
  new 
Comment(array('message' => 'The latest comment.'))
);

$post Post::find(1);

$post->comments()->saveMany($comments);

Связывание моделей (belongs to)

При обновлении связей PHPbelongsTo («принадлежит к») вы можете использовать метод PHPassociate(). Он установит внешний ключ на связанной модели:

PHP
$account Account::find(10);

$user->account()->associate($account);

$user->save();

Связывание моделей (многие ко многим)

Вы также можете вставлять связанные модели при работе с отношениями многие ко многим. Продолжим использовать наши модели PHPUser и PHPRole в качестве примеров. Вы можете легко привязать новые роли к пользователю методом PHPattach().

Связывание моделей «многие ко многим»

PHP
$user User::find(1);

$user->roles()->attach(1);

Вы также можете передать массив атрибутов, которые должны быть сохранены в связующей (pivot) таблице для этого отношения:

PHP
$user->roles()->attach(1, array('expires' => $expires));

Конечно, существует противоположность PHPattach()PHPdetach():

PHP
$user->roles()->detach(1);
+ 4.2

добавлено в 4.2 ()

Оба метода PHPattach() и PHPdetach() также принимают в качестве параметров массивы ID:

PHP
$user User::find(1);

$user->roles()->detach([123]);

$user->roles()->attach([=> ['attribute1' => 'value1'], 23]);

Использование PHPsync() для привязки моделей «многие ко многим»

Вы также можете использовать метод PHPsync() для привязки связанных моделей. Этот метод принимает массив ID, которые должны быть сохранены в связующей таблице. Когда операция завершится только переданные ID будут существовать в промежуточной таблице для данной модели:

PHP
$user->roles()->sync(array(123));

Добавление данных для связующей таблицы при синхронизации

Вы также можете связать другие связующие таблицы с нужными ID:

PHP
$user->roles()->sync(array(=> array('expires' => true)));

Иногда вам может быть нужно создать новую связанную модель и добавить её одной командой. Для этого вы можете использовать метод PHPsave():

PHP
$role = new Role(array('name' => 'Editor'));

User::find(1)->roles()->save($role);

В этом примере новая модель PHPRole будет сохранена и привязана к модели PHPUser. Вы можете также передать массив атрибутов для помещения в связующую таблицу:

PHP
User::find(1)->roles()->save($role, array('expires' => $expires));

Обновление времени владельца

Когда модель принадлежит к другой посредством PHPbelongsTo() — например, PHPComment, принадлежащий PHPPost — иногда нужно обновить время изменения владельца при обновлении связанной модели. Например, при изменении модели PHPComment вы можете обновлять поле updated_at её модели PHPPost. Eloquent делает этот процесс простым — просто добавьте свойство PHP$touches, содержащее имена всех отношений с моделями-потомками.

PHP
class Comment extends Eloquent {

  protected 
$touches = array('post');

  public function 
post()
  {
      return 
$this->belongsTo('Post');
  }

}

Теперь при обновлении PHPComment владелец PHPPost также обновит своё поле updated_at:

PHP
$comment Comment::find(1);

$comment->text 'Изменение этого комментария.';

$comment->save();

Работа со связующими таблицами

Как вы уже узнали, работа отношения многие ко многим требует наличия промежуточной таблицы. Например, предположим, что наш объект PHPUser имеет множество связанных объектов PHPRole. После чтения отношения мы можем прочитать таблицу PHPpivot на обеих моделях:

PHP
$user User::find(1);

foreach (
$user->roles as $role) {
  echo 
$role->pivot->created_at;
}

Заметьте, что каждая модель PHPRole автоматически получила атрибут PHPpivot. Этот атрибут содержит модель, представляющую промежуточную таблицу и она может быть использована как любая другая модель Eloquent.

По умолчанию, только ключи будут представлены в объекте PHPpivot. Если ваша связующая таблица содержит другие поля вы можете указать их при создании отношения:

PHP
return $this->belongsToMany('Role')->withPivot('foo''bar');

Теперь атрибуты foo и bar будут также доступны на объекте PHPpivot модели PHPRole.

Если вы хотите автоматически поддерживать поля created_at и updated_at актуальными, используйте метод PHPwithTimestamps() при создании отношения:

PHP
return $this->belongsToMany('Role')->withTimestamps();

Удаление всех связующих записей

Для удаления всех записей в связующей таблице можно использовать метод PHPdetach():

PHP
User::find(1)->roles()->detach();

Заметьте, что эта операция не удаляет записи из таблицы roles, а только из связующей таблицы.

+ 4.2

добавлено в 4.2 ()

Обновление записи в связующей таблице

Иногда необходимо обновить связующую таблицу не отвязывая её. Для обновления вашей связующей таблицы на месте используйте метод PHPupdateExistingPivot():

PHP
User::find(1)->roles()->updateExistingPivot($roleId$attributes);

Определение собственной связующей модели

Laravel также позволяет определять собственную связующую модель. Для этого сначала создайте свой класс «основной» модели, который наследует PHPEloquent. В остальных ваших моделях Eloquent наследуйте эту базовую модель вместо базового PHPEloquent по умолчанию. В вашу базовую модель добавьте следующую функцию, которая возвращает экземпляр вашей собственной связующей модели:

PHP
public function newPivot(Model $parent, array $attributes$table$exists)
{
  return new 
YourCustomPivot($parent$attributes$table$exists);
}

Коллекции

Все методы Eloquent, возвращающие набор моделей — либо через PHPget(), либо через отношения — возвращают объект-коллекцию. Этот объект реализует стандартный интерфейс PHP PHPIteratorAggregate, что позволяет ему быть использованным в циклах наподобие массива. Однако этот объект также имеет набор других полезных методов для работы с результатом запроса.

Проверка на существование ключа в коллекции

Например, мы можем выяснить, содержит ли результат запись с определённым первичным ключом методом PHPcontains():

PHP
$roles User::find(1)->roles;

if (
$roles->contains(2)) {
  
//
}

Коллекции также могут быть преобразованы в массив или строку JSON:

PHP
$roles User::find(1)->roles->toArray();

$roles User::find(1)->roles->toJson();

Если коллекция преобразуется в строку, результатом будет JSON-выражение:

PHP
$roles = (string) User::find(1)->roles;

Проход по элементам коллекции

Коллекции Eloquent имеют несколько полезных методов для прохода и фильтрации содержащихся в них элементов:

PHP
$roles $user->roles->each(function ($role)
{
  
//
});

Фильтрация элементов коллекции

При фильтрации коллекций передаваемая функция будет использована как функция обратного вызова для array_filter.

PHP
$users $users->filter(function($user)
{
  return 
$user->isAdmin();
});

При фильтрации коллекций и конвертации их в JSON попробуйте сначала вызвать функцию PHPvalues для сброса ключей массива.

Применение функции обратного вызова

PHP
$roles User::find(1)->roles;

$roles->each(function ($role) {
  
//
});

Сортировка коллекции по значению

PHP
$roles $roles->sortBy(function ($role) {
  return 
$role->created_at;
});

Сортировка коллекции по значению

PHP
$roles $roles->sortBy('created_at');

Использование произвольного класса коллекции

Иногда вам может быть нужно получить собственный объект PHPCollection со своими методами. Вы можете указать его при определении модели Eloquent, перекрыв метод PHPnewCollection():

PHP
class User extends Eloquent {

  public function 
newCollection(array $models = array())
  {
    return new 
CustomCollection($models);
  }

}

Читатели и преобразователи

Объявление читателя

Eloquent содержит мощный механизм для преобразования атрибутов модели при их чтении и записи. Просто объявите в её классе метод PHPgetFooAttribute(). Помните, что имя метода должно следовать соглашению camelCase, даже если поля таблицы используют соглашение snake-case (он же — «стиль Си», с подчёркиваниями — прим. пер.):

PHP
class User extends Eloquent {

  public function 
getFirstNameAttribute($value)
  {
      return 
ucfirst($value);
  }

}

В примере выше поле first_name теперь имеет читателя (accessor). Заметьте, что оригинальное значение атрибута передаётся методу в виде параметра.

Объявление преобразователя

Преобразователи (mutators) объявляются подобным образом:

PHP
class User extends Eloquent {

  public function 
setFirstNameAttribute($value)
  {
      
$this->attributes['first_name'] = strtolower($value);
  }

}

Преобразователи дат

По умолчанию Eloquent преобразует поля created_at и updated_at (а до версии 4.2 и deleted_at) в объекты Carbon, которые предоставляют множество полезных методов, расширяя стандартный класс PHP DateTime.

Вы можете указать, какие поля будут автоматически преобразованы и даже полностью отключить преобразование перекрыв метод PHPgetDates() класса модели.

PHP
public function getDates()
{
  return array(
'created_at');
}

Когда поле является датой, вы можете установить его в число-оттиск времени формата Unix (timestamp), строку даты формата Y-m-d, строку даты-времени и, конечно, экземпляр объекта PHPDateTime или PHPCarbon.

Чтобы полностью отключить преобразование дат просто верните пустой массив из метода PHPgetDates():

PHP
public function getDates()
{
  return array();
}

События моделей

Модели Eloquent инициируют несколько событий, что позволяет вам добавить к ним свои обработчики с помощью следующих методов: PHPcreating, PHPcreated, PHPupdating, PHPupdated, PHPsaving, PHPsaved, PHPdeleting, PHPdeleted, PHPrestoring, PHPrestored.

Когда первый раз сохраняется новая модель возникают события creating и created. Если модель уже существовала на момент вызова метода PHPsave(), вызываются события updating и updated. В обоих случаях также возникнут события saving и saved.

Отмена сохранения модели через события

Если обработчики PHPcreating, PHPupdating, PHPsaving или PHPdeleting вернут значение PHPfalse, то действие будет отменено:

PHP
User::creating(function ($user) {
  if ( ! 
$user->isValid()) return false;
});

Использование метода PHPboot() класса модели

Модели Eloquent также содержат статический метод PHPboot, который может быть хорошим местом для регистрации ваших обработчиков событий.

PHP
class User extends Eloquent {

  public static function 
boot()
  {
      
parent::boot();

      
// Регистрация ваших обработчиков...
  
}

}

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

Для того, чтобы держать всех обработчиков событий моделей вместе вы можете зарегистрировать наблюдателя (observer). Объект-наблюдатель может содержать методы, соответствующие различным событиям моделей. Например, методы PHPcreating(), PHPupdating() и PHPsaving(), а также любые другие методы, соответствующие именам событий.

К примеру, класс наблюдателя может выглядеть так:

PHP
class UserObserver {

  public function 
saving($model)
  {
      
//
  
}

  public function 
saved($model)
  {
      
//
  
}

}

Вы можете зарегистрировать его используя метод PHPobserve():

PHP
User::observe(new UserObserver);

Преобразование в массивы и JSON

Преобразование модели к массиву

При создании JSON API вам часто потребуется преобразовывать модели к массивам или выражениям JSON. Eloquent содержит методы для выполнения этих задач. Для преобразования модели или загруженного отношения к массиву можно использовать метод PHPtoArray():

PHP
$user User::with('roles')->first();

return 
$user->toArray();

Заметьте, что целая коллекция моделей также может быть преобразована к массиву:

PHP
return User::all()->toArray();

Преобразование модели к JSON

Для преобразования модели к JSON вы можете использовать метод PHPtoJson():

PHP
return User::find(1)->toJson();

Возврат модели из маршрута

Обратите внимание, что если модель преобразуется к строке, результатом также будет JSON — это значит, что вы можете возвращать объекты Eloquent напрямую из ваших маршрутов!

PHP
Route::get('users', function () {
  return 
User::all();
});

Скрытие атрибутов при преобразовании в массив или JSON

Иногда вам может быть нужно ограничить список атрибутов, включённых в преобразованный массив или JSON-строку — например, скрыть пароли. Для этого определите в классе модели свойство PHP$hidden:

PHP
class User extends Eloquent {

  protected 
$hidden = array('password');

}

При скрытии отношений используйте имя PHPmethod отношения, а не имя для динамического доступа.

Вы также можете использовать атрибут PHP$visible для указания разрешённых полей:

PHP
protected $visible = array('first_name''last_name');

Иногда вам может быть нужно добавить поле, которое не существует в таблице. Для этого просто определите для него читателя:

PHP
public function getIsAdminAttribute()
{
  return 
$this->attributes['admin'] == 'да';
}

Как только вы создали читателя добавьте его имя к свойству-массиву PHP$appends класса модели:

PHP
protected $appends = array('is_admin');

Как только атрибут был добавлен к списку PHP$appends, он будет включён в массивы и выражения JSON, образованные от этой модели. Атрибуты в массиве appends соответствуют настройкам модели visible и hidden.

Комментарии (1)

Napsy864

Не указано про нативные SQL запросы. Их можно делать вообще? А также хотелось бы узнать, можно ли делать сложные JOIN'ы?

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.