Eloquent — очень мощный и впечатляющий ORM (Object-Relational Mapper – слой объектно-реляционного отображения) в Laravel. Если вы знаете, как работать с объектами в PHP, значит, вы знаете, как работать с Eloquent. Нельзя сказать, что это очень просто, но тем не менее у нас есть самый элегантный синтаксис PHP для работы с моделями в Eloquent. Eloquent — довольно большая тема для разговора, и честно говоря, наверное, нам следовало бы пройти через некоторые составляющие части, чтобы дойти до самого Eloquent, но мы так не поступаем. Мы сразу ныряем с головой, надеясь, что выплывем.
Таблица БД для отображения модели Eloquent
В Laravel каждая модель Eloquent представляет одну таблицу БД. Но важно отметить, что даже при том, что одна модель отображает одну таблицу, мы чаще будем использовать комбинации моделей при получении статей, например, или задач, или записей в блоге. Используя отношения, мы можем запрашивать несколько таблиц, для получения требуемого результата.
От класса DB к Eloquent
В прошлом руководстве мы рассмотрели, как сделать базовые CRUD в Laravel с помощью класса DB прямо из коробки. Вспомните, мы даже не создали ни одной модели для этого, функциональность уже была там прямо из коробки. Это впечатляет. Двигаемся дальше, теперь мы можем определить некоторые модели, и сделаем это, наследуя Eloquent.
Давайте построим нашу первую модель Eloquent
Мы будем иметь дело с художниками (PHPPainter
) и картинами (PHPPainting
), поэтому сначала создадим нашу модель PHPPainter
, но перед тем как мы создадим наши модели, давайте настроим миграции таблиц нашей БД вот так:
<?php
// Таблица художников
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePaintersTable extends Migration {
/**
* Запуск миграций.
*
* @return void
*/
public function up()
{
Schema::create('painters', function(Blueprint $table)
{
$table->increments('id');
$table->string('username')->unique();
$table->text('bio');
$table->timestamps();
});
}
/**
* Откат миграций.
*
* @return void
*/
public function down()
{
Schema::drop('painters');
}
}
<?php
// Таблица картин
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePaintingsTable extends Migration {
/**
* Запуск миграций.
*
* @return void
*/
public function up()
{
Schema::create('paintings', function(Blueprint $table)
{
$table->increments('id');
$table->string('title');
$table->text('body');
$table->integer('painter_id');
$table->timestamps();
});
}
/**
* Откат миграций.
*
* @return void
*/
public function down()
{
Schema::drop('paintings');
}
}
Давайте запустим наши миграции, используя shphp artisan migrate
.
Здорово! Теперь мы можем создать модель и внести некоторые данные в нашу базу. Следующая строка создаст модель PHPPainter
, которая будет отображать таблицу базы данных painters:
class Painter extends Eloquent {}
Итак, мы создали нашу модель Eloquent, знаю, было очень непросто набрать столько кода. Давайте посмотрим, что даёт нам одна эта маленькая строчка кода, используя отражение для изучения нашего нового объекта.
Route::get('/', function()
{
$reflection = new ReflectionClass('Painter'); // обзор методов и констант любого класса
print_r($reflection->getMethods());
});
Этот код выдаст 164 метода, которые вы найдёте в Laravel Eloquent API — это немного обескураживает. Но как говорится, спокойствие, только спокойствие. Для начала мы посмотрим только на некоторые из них. Поскольку мы уже рассмотрели настройку CRUD в Laravel 4 используя класс DB, почему бы теперь нам не взглянуть на настройку CRUD с помощью Eloquent?
// получаем запросы и выводим их для ознакомления
Event::listen('illuminate.query', function($sql) {
var_dump($sql);
});
Это позволит нам видеть все запросы, которые создаёт для нас Laravel. Это поможет нам разобраться.
Создание
В Eloquent мы можем вставлять новые записи в БД несколькими способами, но мой любимый синтаксис — с использованием метода PHPsave()
:
Route::get('/', function()
{
$painter = new Painter;
$painter->username = 'Leonardo Da Vinci';
$painter->bio = 'Художник ренессанса, учёный, изобретатель и не только.';
$painter->save();
});
sqlstring ‘insert into painters (username, bio, updated_at, created_at) values (?, ?, ?, ?)’ (length=90)
Выборка (Select)
Теперь давайте посмотрим на нашу новую запись, выбрав данные с помощью PHPall()
и PHPfirst()
:
Route::get('/', function()
{
$painters = Painter::all()->first();
echo $painters->username.'<br>';
echo $painters->bio;
});
sqlstring ‘select * from painters‘ (length=24) Leonardo Da Vinci Художник ренессанса, учёный, изобретатель и не только.
Изменение
Давайте перейдем к обновлению. Я предлагаю добавить дефис в фамилию. Заметьте, что это не какой-то конкретный метод обновления, а скорее выборка, изменение атрибута, и использование метода сохранения, вот так:
Route::get('/', function()
{
$painters = Painter::all()->first();
$painters->username = 'Leonardo Da-Vinci';
$painters->save();
});
sqlstring ‘select * from painters‘ (length=24) string ‘update painters set username = ?, updated_at = ? where id = ?’ (length=69)
Удаление
Удалять записи в Eloquent супер просто. Смотрите:
Route::get('/', function()
{
$painters = Painter::all()->first();
$painters->delete();
});
sqlstring ‘select * from painters‘ (length=24) string ‘delete from painters where id = ?’ (length=37)
Вот так — бац! — и Леонардо больше нет в БД.
Связи в Eloquent
Связи являются ключевым компонентом в Eloquent, но сначала нам нужно еще больше художников. Давайте добавим их:
Route::get('/', function()
{
$painter = new Painter;
$painter->username = 'Leonardo Da Vinci';
$painter->bio = 'Renaissance painter, scientist, inventor, and more. Da Vinci is one of most famous painters for his iconic Mona Lisa and Last Supper.';
$painter->save();
$painter = new Painter;
$painter->username = 'Vincent Van Gogh';
$painter->bio = 'Dutch post-impressionist painter. Famous paintings include: Sunflowers, The Starry night, Cafe Terrace at Night.';
$painter->save();
$painter = new Painter;
$painter->username = 'Rembrandt';
$painter->bio = 'One of greatest painters, admired for his vivid realism. Famous paintings include The Jewish Bride, The Storm of the sea of Galilee';
$painter->save();
});
sqlstring ‘insert into painters (username, bio, updated_at, created_at) values (?, ?, ?, ?)’ (length=90) string ‘insert into painters (username, bio, updated_at, created_at) values (?, ?, ?, ?)’ (length=90) string ‘insert into painters (username, bio, updated_at, created_at) values (?, ?, ?, ?)’ (length=90)
Отлично! Мы знаем, что художники, скорее всего, создали несколько картин. Для краткости я покажу только один фрагмент, показывающий как можно вставить картину. Сразу отмечу, что для наших целей ID Леонардо да Винчи — painter_id 2, ID Винсента Ван Гога — painter_id 3, и ID Рембрандта — painter_id 4. Именно поле painter_id в таблице картин помогает нашим отношениям. Нам нужно помнить об этом, при построении методов для выборки данных из нашей базы данных с помощью отношений.
Route::get('/', function()
{
$painter = Painter::find(4);
$painting = new Painting;
$painting->title = 'The Storm on the Sea of Galilee';
$painting->body = 'The Storm on the Sea of Galilee is a painting from 1633 by the Dutch Golden Age painter Rembrandt van Rijn that was in the Isabella Stewart Gardner Museum of Boston, Massachusetts, United States, prior to being stolen on March 18, 1990.';
$painting->painter_id = $painter->id;
$painting->save();
});
Примечание: Мы выполняем вариации этого кода несколько раз, чтобы у каждого художника было, по крайней мере, 2 картины в БД.
hasMany
У художника обычно много своих картин, как у писателя много книг, или у курицы много яиц. В Laravel мы можем задать такие отношения в нашей модели вот так:
<?php
class Painter extends Eloquent {
public function paintings()
{
return $this->hasMany('Painting');
}
}
Вот отличный трюк, который поможет вам запомнить, как это работает. Начинается с ключевого слова PHP$this
, затем идёт имя класса файла, далее метод PHP$this
, и затем переданная модель. В нашем случае это будет звучать так: This PHPPainter
hasMany PHPPainting
(у этого художника много картин). Видите, как это работает?
belongsTo
Верно и обратное, каждая картина должна быть нарисована художником. Можно сказать, что картина принадлежит (PHPbelongsTo
) художнику. Посмотрим на эту модель:
<?php
class Painting extends Eloquent {
public function painter()
{
return $this->belongsTo('Painter');
}
}
Здесь мы можем использовать тот же трюк: (this|имя файла класса|имя метода|имя переданной модели). В нашем случае это будет звучать так: This PHPPainting
belongsTo PHPPainter
(эта картина принадлежит художнику).
Динамические методы
Теперь, когда мы настроили наши модели для представления обоих отношений — hasMany и belongsTo, мы можем начать запрашивать данные из БД очень умными способами. Например, с помощью динамических методов и отношений, которые мы только что создали, мы можем сказать базе данных пойти, взять картины Леонардо да Винчи и вернуть их нам. Давайте посмотрим:
Route::get('/', function()
{
$painter = Painter::whereUsername('Leonardo Da Vinci')->first();
foreach($painter->paintings as $painting){
echo $painting->title.'<br>';
echo $painting->body.'<br><br>';
}
});
sqlstring ‘select * from painters where username = ? limit 1′ (length=53) string ‘select * from paintings where paintings.painter_id = ?’ (length=60) Mona Lisa Мона Лиза - портрет женщины работы итальянского художника Леонардо да Винчи, который считается “самым известным, самым посещаемым, самым упоминаемым, самым воспетым, самым пародируемым произведением искусства в мире Last Supper Тайная вечеря - роспись конца 15 века в трапезной монастыря Санта-Мария делле Грацие в Милане работы Леонардо да Винчи. Предположительно работа над произведением была начата около 1495 года, и оно стало частью общей схемы реконструкции церкви и ее монастырских зданий покровителя Леонардо - Лодовико Сфорца, герцога Миланского.
Обратите внимание на метод whereUsername, я не видел его раньше. И вы тоже, это потому что в Laravel вы можете комбинировать условие where с именем таблицы и передавать строку, которую ищете. Ошеломляюще! Вот ваш динамический метод. Что касается отношения, то это сработает, потому что мы сказали нашей модели PHPPainter
, что у этого художника много картин.
Теперь давайте сделаем обратное, спросим у базы данных что-нибудь такое: «Кто нарисовал Поедателей картофеля?» С теми отношениями в модели PHPPainting
, которые мы задали, такими как эта картина принадлежит художнику, мы можем также сделать и обратное:
Route::get('/', function()
{
$painting = Painting::whereTitle('The Potato Eaters')->first();
echo Painter::find($painting->painter_id)->username;
});
sqlstring ‘select * from paintings where title = ? limit 1′ (length=51) string ‘select * from painters where id = ? limit 1′ (length=47) Vincent Van Gogh
Ну конечно! Винсент Ван Гог — тот художник, который написал картину «Поедатели картофеля». Приведённый код — долгий способ для этого. Видите ли, когда мы создаём оба отношения, мы можем комбинировать модели для достижения любого поля обеих таблиц, проходя через них. Так как мы создали экземпляр модели PHPPainting
в PHP$painting
, мы можем теперь пройти через модель PHPPainter
до любого поля в этой таблице. Как? Вот так:
Route::get('/', function()
{
$painting = Painting::whereTitle('The Potato Eaters')->first();
echo $painting->painter->username.'<br>';
echo $painting->painter->bio;
});
sqlstring ‘select * from paintings where title = ? limit 1′ (length=51) string ‘select * from painters where painters.id = ? limit 1′ (length=58) Vincent Van Gogh Голландский пост-импрессионист. Известные картины: Подсолнухи, Звёздная ночь, Ночная терраса кафе.
Заметьте, единственная модель, для которой понадобился экземпляр, это модель Painting, хотя мы с лёгкостью обращаемся и к полям таблицы художников, потому что проходим через неё.
Используйте ваше воображение, и вы сможете спросить у БД практически всё, что пожелаете, для этого надо всего лишь немного проб и ошибок. Давайте скажем базе данных «Выдай мне все картины, которые у тебя есть, и скажи мне, кто нарисовал каждую из них». Вас понял, будет сделано.
Route::get('/', function()
{
$paintings = Painting::all();
foreach($paintings as $painting){
echo $painting->painter->username;
echo ' painted the ';
echo $painting->title;
}
});
sqlstring ‘select * from paintings‘ (length=25) string ‘select * from painters where painters.id = ? limit 1′ (length=58) Leonardo Da Vinci painted the Mona Lisa string ‘select * from painters where painters.id = ? limit 1′ (length=58) Leonardo Da Vinci painted the Last Supper string ‘select * from painters where painters.id = ? limit 1′ (length=58) Vincent Van Gogh painted the The Starry Night string ‘select * from painters where painters.id = ? limit 1′ (length=58) Vincent Van Gogh painted the The Potato Eaters string ‘select * from painters where painters.id = ? limit 1′ (length=58) Rembrandt painted the The Night Watch string ‘select * from painters where painters.id = ? limit 1′ (length=58) Rembrandt painted the The Storm on the Sea of Galilee
Обратите внимание, как много запросов поступает в нашу БД. Возможно, это не лучший вариант. Лучше сделать это, используя нетерпеливую загрузку (eager loading) с помощью метода PHPwith
:
Route::get('/', function()
{
$paintings = Painting::with('painter')->get();
foreach($paintings as $painting){
echo $painting->painter->username;
echo ' painted the ';
echo $painting->title;
echo '<br>';
}
});
sqlstring ‘select * from paintings‘ (length=25) string ‘select * from painters where painters.id in (?, ?, ?)’ (length=59) Leonardo Da Vinci painted the Mona Lisa Leonardo Da Vinci painted the Last Supper Vincent Van Gogh painted the The Starry Night Vincent Van Gogh painted the The Potato Eaters Rembrandt painted the The Night Watch Rembrandt painted the The Storm on the Sea of Galilee
Теперь намного круче. 2 запроса и готово.
Что ж, этого достаточно. Есть ещё много вещей в Laravel для изучения, но надеюсь, это тоже будет полезно.
Комментарии (2)
Подскажите, куда вставить код:
что-бы видеть исполняемые запросы?
Куда угодно — в сервис-провайдер вашего приложения или в один из start-файлов.