Блог

PHP/ORM: хороший инструмент Laravel ORM

Что такое ORM?

ORM это объектно-реляционное представление – технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования, создавая “виртуальную объектную базу данных”.

Laravel ORM – является одним из основных поклонников этой технологии. Здесь она имеет название “Eloquent”, потому что позволяет работать с объектами и связями баз данных, применяя “красноречивый” и выразительный синтаксис.

Как начать работать?

Работа с базами данных в Laravel очень легкая. Eloquent позволяет отображать таблицы базы данных, в виде простых модельных объектов, которые включают в себя различные методы для получения и обновления базы данных. Во время работы над кодом очень легко читать. К счастью Laravel написана таким образом, что некоторые из его основных компонентов может быть использован в качестве автономных модулей.

Использование магии базы данных Laravel в собственных проектах довольно прост. Прежде всего, необходимо убедиться, что у вас есть все необходимые компоненты, включенные в проект. Является компонентом базы данных и называется “Illuminate\Database”. Самый простой способ, установить и начать использовать “Illuminate\Database” как всегда наш всеми любимый composer.

Убедитесь, что файл с именем ‘composer.json’ в вашем проекте, содержит следующее:

{
    "require": {
        "illuminate/database": "*"
    }
}

Как подключится к базе данных?

<?php
require 'vendor/autoload.php';  

use Illuminate\Database\Capsule\Manager;  

$capsule = new Manager;
$capsule->addConnection(array(
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'test',
    'username'  => 'test',
    'password'  => 'super_secret_password',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => ''
));

$capsule->bootEloquent();

Как использовать?

В общем, вы можете определить Eloquent модель для каждой таблицы базы данных.

Соглашения

Предполагается, что база данных имеет определенную структуру:

  • Каждая таблица имеет первичный ключ с именем id.
  • Каждое имя таблицы имеет множественное число соответствующего названия, т.е. если модель имеет имя ‘Test’, то таблица имеет имя ‘tests’.

Можно самостоятельно указывать имя таблицы. Просто добавьте статическое свойство table вашей модели:

class Test extends \Illuminate\Database\Eloquent\Model
{
     public static $table = 'test_others';
}

Использованием Eloquent на удивление просто. Самый простой способ получить Eloquent является статический метод find. Этот метод возвращает одну модель с первичным ключом с соответствующими для каждого столбца в таблице свойствами. Теперь мы можем начать работу с таблицами базы данных.

// Create model
class Test extends \Illuminate\Database\Eloquent\Model 
{
    public $timestamps = false;
}

// Grab with an id of 1
$book = Test::find(1);

// Change some stuff
$book->key   = "time";
$book->value = time();

// Save it to the database
$book->save();

Для получения дополнительной информации о работе с Eloquent, можно получить здесь.

Зависимости (отношения)

Eloquent делает процесс создания отношений и запрос связанных моделей простым и интуитивным. Laravel поддерживает три типа отношений:

  • One-To-One
  • One-To-Many
  • Many-To-Many

Для определения отношений в Eloquent модели, вы просто создаете метод, возвращающий результат методов либо has_one, has_many, belongs_to, или has_many_and_belongs_to.

One-To-One

One-To-One – базовая форма отношений. Давайте представим, что пользователь бы зарегистрирован через Invite. Это может быть описано Eloquent:

class User extends \Illuminate\Database\Eloquent\Model
{
     public function invite()
     {
          return $this->has_one('Invite');
     }
}

Обратите внимание, что имя соответствующей модели передается в метод has_one. Теперь вы можете узнать кто привел к вам нового пользователя через invite метод:

$invite = User::find(1)->invite()->first();

Рассмотрим детальнее, производится два запроса: один на получение пользователя и один на получение кто приглашал:

SELECT * FROM "users" WHERE "id" = 1

SELECT * FROM "invites" WHERE "user_id" = 1

Заметьте, Eloquent предполагает наличие внешнего (“foreign”) ключа, поиск будет производится по полю user_id.

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

return $this->has_one('Invite', 'my_foreign_key');

Хотите просто узнать кто пригласил пользователя, просто используйте динамическое свойство invite. Eloquent будет автоматически загружать отношения для вас, чтобы знать, нужно ли вызывать get (для one-to-many отношений) или first (для one-to-one отношений) метода:

$invite = User::find(1)->invite;

Что, если вы захотите получить пользователя по значению приглашения?

Так как внешний ключ (user_id) находится в таблице Invite, мы должны описать эту связь с помощью belongs_to метод. При использовании метода belongs_to, имя метода отношения должно корреспондироваться с внешним ключом (без _id). Так, если внешний ключ user_id, метод отношения должен иметь имя user:

class Invite extends \Illuminate\Database\Eloquent\Model
{
     public function user()
     {
          return $this->belongs_to('User');
     }
}

Отлично! Теперь Вы можете получить доступ к модели User через модель Invite с использованием либо модели вашего метода отношения, либо динамического свойства:

echo Invite::find(1)->user()->first()->email;

echo Invite::find(1)->user->email;

One-To-Many

Предположим, что запись блога имеет много комментариев. Это легко реализуемо посредством определения отношения с помощью метода has_many:

class Post extends \Illuminate\Database\Eloquent\Model
{
     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 чаще всего интересует при использовании сторонних ORM. Предположим, что пользователь имеет много ролей, но роль может принадлежать многим пользователям. Обычно нужно три таблицы users, roles, и промежуточная таблица roles_user. Структуры для каждой таблицы выглядит следующим образом:

CREATE TABLE `users` (
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
	`email` VARCHAR(100) NOT NULL DEFAULT '',
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci';

CREATE TABLE `roles` (
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(100) NOT NULL DEFAULT '',
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci';

CREATE TABLE `roles_users` (
        `user_id` BIGINT(20) NOT NULL,
        `role_id` BIGINT(20) NOT NULL
)
COLLATE='utf8_general_ci';

Теперь мы готовы определить отношения для моделей, используя метод has_many_and_belongs_to:

class User extends \Illuminate\Database\Eloquent\Model
{
     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 \Illuminate\Database\Eloquent\Model
{
     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));

Больше можно узнать здесь официальная документация и здесь на русском.

Даже для общего ознакомления стоит посмотреть.