## Содержание
- [Основы](#the-basics)
- [Соглашения](#conventions)
- [Получение моделей](#get)
- [Агрегатирование](#aggregates)
- [Вставка и изменение моделей](#save)
- [Зависимости](#relationships)
- [Вставка связанных моделей](#inserting-related-models)
- [Работа с промежуточными таблицами](#intermediate-tables)
- [Активная загрузка](#eager)
- [Ограничение активной загрузки](#constraining-eager-loads)
- [Геттеры и сеттеры](#getter-and-setter-methods)
- [Множественные присвоения](#mass-assignment)
- [Конвертирование моделей в массивы](#to-array)
## Основы
ORM это [объектно-реляционное представление](http://en.wikipedia.org/wiki/Object-relational_mapping), и Laravel является одним из основных поклонников этой технологии. Здесь она имеет название "Eloquent", потому что позволяет работать с объектами и связями баз данных, применяя "красноречивый" и выразительный синтаксис. В общем, вы можете определить Eloquent модель для каждой таблицы базы данных. Для начала, определим простую модель:
class User extends Eloquent {}
Чудесно! Заметьте, что класс *User* наследует класс **Eloquent**. Этот класс предоставляет всю функциональность для работы с базой данных.
> **Примечание:** Обычно, Eloquent модели "поселены" в **application/models**.
## Соглашения
Eloquent предполагает что база данных имеет определенную структуру:
- Каждая таблица имеет первичный ключ с именем **id**.
- Каждое имя таблицы имеет форму множественного числа соответствующего названия модели, т.е. если модель имеет имя 'User', то таблица имеет имя 'users' (преобразовано во множественное число).
Иногда вы можете использовать имя таблицы не во множественном числе. Нет проблем. Просто добавьте статическое свойство **table** вашей модели:
class User extends Eloquent {
public static $table = 'my_users';
}
## Получение моделей
Получение моделей с использованием Eloquent на удивление просто. Самый простой способ получить Eloquent модель является статический метод **find**. Этот метод возвращает одну модель с первичным ключом с соответствующими для каждого столбца в таблице свойствами:
$user = User::find(1);
echo $user->email;
Метод **find** выполняет запрос в виде:
SELECT * FROM "users" WHERE "id" = 1
Нужно получить всю таблицу? Просто используйте статический метод **all**:
$users = User::all();
foreach ($users as $user)
{
echo $user->email;
}
Конечно, получение всей таблицы не очень полезно. К счастью, **любой метод, который можно получить посредством Fluent конструктора запросов, доступен в Eloquent**. Просто составляйте запросы к вашей модели с использованием статических методов [конструктора запросов](/docs/v3/database/query), и выполняйте запросы с методами **get** или **first**. "Get" метод вернет массив моделей, метод "first" - единичную модель:
$user = User::where('email', '=', $email)->first();
$user = User::where_email($email)->first();
$users = User::where_in('id', array(1, 2, 3))->or_where('email', '=', $email)->get();
$users = User::order_by('votes', 'desc')->take(10)->get();
> **Примечание:** Если результат не будет найден, метод **first** вернет NULL. Методы **all** и **get** вернут пустой массив.
## Агрегатирование
Необходимы **MIN**, **MAX**, **AVG**, **SUM**, или **COUNT** значения? Просто вставьте соответствующий метод:
$min = User::min('id');
$max = User::max('id');
$avg = User::avg('id');
$sum = User::sum('id');
$count = User::count();
Ну и, конечно же, вы можете применить ограничения. Например:
$count = User::where('id', '>', 10)->count();
## Вставка и изменение моделей
Нет ничего проще установки Eloquent модели в таблицу. Сначала, создаем экземпляр модели. Затем устанвливаем свойства. И, наконец, вызываем **save** метод:
$user = new User;
$user->email = 'example@gmail.com';
$user->password = 'secret';
$user->save();
В качестве альтернативы вы можете использовать метод **create**, который вставляет новую запись в базу данных и возвращает екземпляр модели новой вставленной записи, или **false** в случае неуспеха.
$user = User::create(array('email' => 'example@gmail.com'));
Изменение модели также очень просто. Вместо создания екземпляра модели, запросите ее из базы, установите свойства, и сохраните:
$user = User::find(1);
$user->email = 'new_email@gmail.com';
$user->password = 'new_secret';
$user->save();
Необходимо поддерживать создание и обновление меток записей базы данных? С Eloquent вы можете не беспокоиться об этом. Просто добавьте статическое свойство **timestamps** вашей модели:
class User extends Eloquent {
public static $timestamps = true;
}
Затем, добавьте **created_at** и **updated_at** столбцы типа "DATE" в таблицу. Теперь, при сохранении модели, метки даты будут созданы автоматически.
> **Примечание:** Временную зону по умолчанию вы можете изменить в **application/config/application.php**.
## Зависимости (отношения)
Даже если вы делаете что-то неправильно, таблицы вашей базы данных будут связаны друг с другом. Например, "заказ" может принадлежать "пользователю". Или сообщение может иметь много комментариев. Eloquent делает процесс создания отношений и запрос связанных моделей простым и интуитивным. Laravel поддерживает три типа отношений:
- [One-To-One](#one-to-one)
- [One-To-Many](#one-to-many)
- [Many-To-Many](#many-to-many)
> **Примечание пер.** Наименования отношений намеренно не переводятся для более точного понимания.
Для определения отношений в Eloquent модели, вы просто создаете метод, возвращающий результат методов либо **has\_one**, **has\_many**, **belongs\_to**, или **has\_many\_and\_belongs\_to**. Давайте рассмотрим каждый из них подробнее.
### One-To-One
One-To-One - базовая форма отношений. Давайте представим, что пользователь имеет один номер телефона. Это может быть описано Eloquent:
class User extends Eloquent {
public function phone()
{
return $this->has_one('Phone');
}
}
Обратите внимание, что имя соответствующей модели передается в метод **has_one**. Теперь вы можете получить телефон пользователя через **phone** метод:
$phone = User::find(1)->phone()->first();
Рассмотрим соответстующие вышуказанным действиям SQL выражения. Производится два запроса: один на получение пользователя и один на получение номера телефона:
SELECT * FROM "users" WHERE "id" = 1
SELECT * FROM "phones" WHERE "user_id" = 1
Заметьте, Eloquent предполагает наличие внешнего ("foreign") ключа по полю **user\_id**.
Большинство внешних ключей будет следовать этому **model\_id** соглашению, однако, если вы хотите использовать другое имя столбца в качестве внешнего ключа, просто передайте его в качестве второго параметра метода:
return $this->has_one('Phone', 'my_foreign_key');
Хотите просто получить телефон пользователя без вызова первого метода? Нет проблем. Просто используйте **динамическое свойство phone**. Eloquent будет автоматически загружать отношения для вас, будучи достаточно умным, чтобы знать, нужно ли вызывать **get** (для **one-to-many** отношений) или **first** (для **one-to-one** отношений) метода:
$phone = User::find(1)->phone;
Что, если вы захотите получить пользователя по значению телефона?
Так как внешний ключ (**user\_id**) находится в таблице телефонов, мы должны описать эту связь помощью **belongs\_to** метод. Это имеет смысл, не так ли? Телефоны принадлежат пользователям.
при использовании метода **belongs\_to**, имя метода отношения должно корреспондироваться с внешним ключом (без **\_id**). Так, если внешний ключ **user\_id**, метод отношения должен иметь имя **user**:
class Phone extends Eloquent {
public function user()
{
return $this->belongs_to('User');
}
}
Отлично! Теперь Вы можете получить доступ к модели User через модель Phone с использованием либо модели вашего метода отношения, либо динамического свойства:
echo Phone::find(1)->user()->first()->email;
echo Phone::find(1)->user->email;
### One-To-Many
Предположим, что запись блога имеет много комментариев. Это легко реализуемо посредством определения отношения с помощью метода **has_many**:
class Post extends Eloquent {
public function comments()
{
return $this->has_many('Comment');
}
}
Теперь, простой доступ к комментариям осуществляется с помощью метода отношения или динамического свойства:
$comments = Post::find(1)->comments()->get();
$comments = Post::find(1)->comments;
Что аналогично следующим запросам SQL:
SELECT * FROM "posts" WHERE "id" = 1
SELECT * FROM "comments" WHERE "post_id" = 1
Нужно присоединить другой внешний ключ? Нет проблем. Передайте его в качестве второго параметра в метод:
return $this->has_many('Comment', 'my_foreign_key');
Вы можете спросить: _Если динамические свойства возвращают отношения и требует меньше кода, почему я никогда не использую методы отношений?_ На самом деле, методы отношений являются очень мощными. Они позволяют вам выстроить цепочку методов запроса до получения отношения. Например:
echo Post::find(1)->comments()->order_by('votes', 'desc')->take(10)->get();
### Many-To-Many
Many-to-many отношения являются наиболее сложными из трех отношений. Но не волнуйтесь, вы сможете это делать. Например, предположим, что пользователь имеет много ролей, но роль может принадлежать многим пользователям. Три таблицы базы данных должны быть созданы для выполнения этих отношений: таблицы **users**, **roles**, и промежуточная таблица **roles_user**. Структуры для каждой таблицы выглядит следующим образом:
**Users:**
id - INTEGER
email - VARCHAR
**Roles:**
id - INTEGER
name - VARCHAR
**Roles_Users:**
user_id - INTEGER
role_id - INTEGER
Теперь мы готовы определить отношения для моделей, используя метод **has\_many\_and\_belongs\_to**:
class User extends Eloquent {
public function roles()
{
return $this->has_many_and_belongs_to('Role');
}
}
Отлично! Теперь пришло время для получения ролей пользователей:
$roles = User::find(1)->roles()->get();
Или, как обычно, вы можете получить отношение через динамическое свойство роли:
$roles = User::find(1)->roles;
Как вы могли заметить, по умолчанию имя промежуточной таблицы представляет собой имена двух родственных моделей расположены в алфавитном порядке и объединенных символом подчеркивания. Тем не менее, вы можете указать свое имя таблицы. Свобода превыше всего! Просто вставьте имя таблицы вторым параметром в метод **has\_and\_belongs\_to\_many**:
class User extends Eloquent {
public function roles()
{
return $this->has_many_and_belongs_to('Role', 'user_roles');
}
}
## Вставка связанных моделей
Предположим, у вас есть **Post** модель,имеющая много комментариев. И вам нужно вставить новый комментарий к определенной записи блога. Вместо того чтобы вручную установить **post_id** внешний ключ в вашей модели, вы можете вставить новый комментарий его Post модели. Это может выглядеть так:
$comment = new Comment(array('message' => 'A new comment.'));
$post = Post::find(1);
$post->comments()->insert($comment);
При вставке связанной модели посредством "родительской" модели, внешний ключ устанвливается автоматически. Таким образом, в данном случае, "post_id" автоматически установится в "1" для нового вставленного комментария.
При работе с `has_many` зависимостями, вы можете испоьзовать метод `save` вставки / изменения связанной модели:
$comments = array(
array('message' => 'A new comment.'),
array('message' => 'A second comment.'),
);
$post = Post::find(1);
$post->comments()->save($comments);
### Вставка связанных моделей (Many-To-Many)
Это тем более полезно при работе с "many-to-many" отношениями. Например, рассмотрим **User** модель, которая имеет много ролей. Кроме того, роль **Role** модель может иметь много пользователей. Таким образом, промежуточная таблица для этого отношения имеет "user_id" и "role_id" столбцы. Теперь, вставим новую Role для User:
$role = new Role(array('title' => 'Admin'));
$user = User::find(1);
$user->roles()->insert($role);
Теперь, когда Role вставлена, Role не только вставлена в таблицу "roles", но также вставлена запись и в промежуточную таблицу для вас. Что может быть проще! Тем не менее, вы можете хотеть вставить новую запись только в промежуточную таблицу. Например, возможно, вы хотите присоединить role к уже существующему пользователю. Просто используйте метод "attach":
$user->roles()->attach($role_id);
Кроме того, вы можете использовать метод `sync`, который принимает массив ID для синхронизации в промежуточной таблице. По завершению операции, только ID из массива будут в промежуточной таблице.
$user->roles()->sync(array(1, 2, 3));
## Работа с промежуточными таблицами
Как вы уже узнали, 'many-to-many' требует наличия промежуточной таблицы. Eloquent легко осуществляет эту поддержку. Например, у нас есть **User** модель, имеющая много ролей. В свою очередь, **Role** модель имеет много пользоателей. И промежуточная таблица имеет поля "user_id" и "role_id". Мы можем получить доступ к сводной таблице для отношений:
$user = User::find(1);
$pivot = $user->roles()->pivot();
Как только мы получим экземпляр сводной таблицы, мы можем использовать его, как и любую другую Eloquent модель:
foreach ($user->roles()->pivot()->get() as $row)
{
//
}
Вы также можете получить доступ к конкретной промежуточной строке таблицы, связанной с данной записью. Например:
$user = User::find(1);
foreach ($user->roles as $role)
{
echo $role->pivot->created_at;
}
Обратите внимание, что каждой полученной связанной **Role** модели автоматически присваивается **pivot** атрибут. Этот атрибут содержит модель, представляющую промежуточные записи таблицы, ассоциированные с этой связанной моделью.
Может понадобиться удалить все записи из промежуточной таблицы для данной модели отношений. Например, может быть, вы хотите удалить все назначенные роли пользователя. Пример:
$user = User::find(1);
$user->roles()->delete();
Отметим, что это не приводит к удалению ролей из таблицы "role", но только удаляет записи из промежуточной таблицы, которые связаны с ролями данного пользователя.
## Активная загрузка
Активная загрузка разработана для избежания проблемы (N+1) запроса. Что эта проблема? Примем, что каждая книга принадлежит автору. Опишем эти отношения следующим образом:
class Book extends Eloquent {
public function author()
{
return $this->belongs_to('Author');
}
}
Теперь рассмотри следующий код:
foreach (Book::all() as $book)
{
echo $book->author->name;
}
Сколько запросов будет выполнено? Сначала, один запрос будет выполнен для получения всех книг из таблицы. Затем, следующим запросом для каждой книги будет запрошен автор. Для отображения авторов 25 книг потребуется 26 запросов. Давайте посмотрим, как это ускорить.
К счастью, мы можем использовать активную загрузку при помощи метода **with**.
Thankfully, you can eager load the author models using the **with** method. Простое указание **имени функции** зависимости производит активную загрузку:
foreach (Book::with('author')->get() as $book)
{
echo $book->author->name;
}
В этом примере, **будут выполнены только два запроса**!
SELECT * FROM "books"
SELECT * FROM "authors" WHERE "id" IN (1, 2, 3, 4, 5, ...)
Очевидно, что разумное применение активной загрузки может значительно увеличить производиетльность вашего приложения. В приведенном выше примере активная загрузка сократила время выполнения в два раза.
Применение активной загрузки для множественных связей:
$books = Book::with(array('author', 'publisher'))->get();
> **Примечание:** При использовании активной загрузки необходимо вызывать статтический метод **with** всегда в начале запроса.
Можно применять акивную загрузку для связанных отношений. Например, пусть наша **Author** модель имеет "contacts" отношение. Использование активной загрузки с учетом отношений для Book модели:
$books = Book::with(array('author', 'author.contacts'))->get();
## Ограничения в активной загрузке
Использование ограничений и условий при активной загрузке:
$users = User::with(array('posts' => function($query)
{
$query->where('title', 'like', '%first%');
}))->get();
В этом примере загружаются все сообщения пользователя, поле которых "title" содержит "first".
## Геттеры и Сеттеры
Сеттеры позволяют управлять свойствами при помощи пользовательских методов. Сеттер определяется префиксом "set_" для имени свойства.
public function set_password($password)
{
$this->set_attribute('hashed_password', Hash::make($password));
}
Вызов сеттера произойдет автоматически при присвоении свойству значения.
$this->password = "my new password";
Геттеры очень просты. Они используются для модификации возвращаемых свойств. Определяется геттер префиксом "get_" к имени свойства.
public function get_published_date()
{
return date('M j, Y', $this->get_attribute('published_at'));
}
Так же, как и сеттер, геттер выполняется автоматически при запросе свойства:
echo $this->published_date;
## Массовые присвоения
Массовые присвоения практикуются для передачи ассоциативного массива в метод модели, который затем заполняет свойства модели значениями из массива.Массовое присвоение может быть произведено путем передачи массива в конструктор модели:
$user = new User(array(
'username' => 'first last',
'password' => 'disgaea'
));
$user->save();
Или, массовое присвоение может быть выполнено при помощи **fill** метода.
$user = new User;
$user->fill(array(
'username' => 'first last',
'password' => 'disgaea'
));
$user->save();
По умолчанию, все пары свойств ключ/значение сохраняются посредством массового присвоения. Тем не менее, можно создать "белый список" свойств, которые будут установлены. Если "белый список" доступных свойств установлен, то будут установлены только свойства из этого списка во время массового присвоения.
Вы можете указать доступные свойства, определив **$accessible** статический массив. Каждый элемент содержит имя внесенных в белый список свойств.
public static $accessible = array('email', 'password', 'name');
В качестве альтернтивы, вы можете использовать метод **accessible** для вашей модели:
User::accessible(array('email', 'password', 'name'));
> **Примечание:** При использовании массового присвоения следует соблюдать предельную осторожность.Технические упущения могут привести к серьезной уязвимости.
## Конвертация модели в массив
При построении JSON API, вам может понадобиться ковертирование модели в массив для сериализации. Это совсем просто.
#### Конвертация модели в массив:
return json_encode($user->to_array());
Метод `to_array` автоматически получит все свойства модели с учетом всех зависимостей.
Иногда может понадобиться ограничить включаемые в массив свойства, такие, как, например, пароли. Для этого добавьте массив скрытых (`hidden`) свойств в модель:
#### Excluding attributes from the array:
class User extends Eloquent {
public static $hidden = array('password');
}