кодесурса
«Laravel

Laravel (5.7) Красноречивые отношения

script1adsense2code
script1adsense3code

Вступление

В приложении часто существует связь между таблицами базы данных. Например, сообщение в блоге может иметь много просмотров и реагировать, заказ может быть связан с клиентом, который разместил t. Eloquent делает управление отношениями в нашем приложении очень простым.

Определение отношений

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

$user->posts()->where('active', 1)->get();

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

Один к одному

Отношения один-к-одному - это очень основное отношение. Например, модель пользователя может быть связана с одним телефоном. Чтобы определить эту связь, мы должны поместить метод телефона в модель User. Затем метод phone должен вызвать метод hasOne и вернуть его результат:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    /**
     * Get the phone record associated with the user.
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

Первый аргумент, который передается методу hasOne, - это имя связанной модели. Как только отношение было определено, мы можем получить связанную запись, используя динамические свойства Eloquent. Динамические свойства позволят нам получить доступ к методам отношений, как если бы они были свойствами, определенными в модели:

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

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

return $this->hasOne('App\Phone', 'foreign_key');

Кроме того, Eloquent предполагает, что внешний ключ имеет значение, соответствующее столбцу id (или пользовательскому столбцу $ primaryKey) родительского элемента. Это означает, что Eloquent будет искать значение столбца id пользователя в столбце user_id записи Phone. Если мы хотим, чтобы отношения использовали значение, отличное от id, мы можем передать третий аргумент методу hasOne, указывающему наш пользовательский ключ:

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

Определение обратной связи

Итак, у нас есть доступ к модели телефона от нашего пользователя. Теперь давайте определим отношение к модели телефона, которое позволит нам получить доступ к пользователю, которому принадлежит телефон. Мы можем определить обратное отношение hasOne y, используя метод ownTo:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
    /**
     * Get the user that owns the phone.
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

В приведенном выше примере Eloquent попытается сопоставить user_id с телефона с идентификатором в модели User. Eloquent определит имя внешнего ключа по умолчанию, проверив имя метода отношения и добавив суффикс имени метода к _id. Но в том случае, когда внешний ключ в модели телефона не user_id, вы можете передать имя пользовательского ключа в качестве второго аргумента в методе assignTo:

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

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

/**
 * Get the user that owns the phone.
 */
public function user()
{
    return $this->belongsTo('App\User', 'foreign_key', 'other_key');
}

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

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

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    /**
     * Get the comments for the blog post.
     */
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

Напомним, Eloquent автоматически определяет правильный столбец внешнего ключа в модели Comment. В соответствии с соглашением Eloquent берет имя «змеиного» имени модели-владельца и добавляет к _id суффикс. Таким образом, в этом примере Eloquent предполагает, что внешний ключ в модели Comment - post_id.

После того, как мы определили отношение, мы можем получить доступ к коллекции комментариев, используя свойство comments. Напомним, что поскольку Eloquent предоставляет «динамические свойства», мы также можем получить доступ к методам отношений, как если бы они были определены как свойства в модели:

$comments = App\Post::find(1)->comments;
foreach ($comments as $comment) {
    //
}

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

$comment = App\Post::find(1)->comments()->where('title', 'foo')->first();

Как и метод hasOne, мы также можем переопределить внешний и локальный ключи, передав дополнительные аргументы методу hasMany:

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

Один ко многим (обратный)

Теперь, когда мы можем получить доступ ко всем комментариям поста, давайте определим отношение, чтобы позволить комментарию получить доступ к его родительскому посту. Чтобы определить обратное отношение hasMany, мы определяем функцию отношения в дочерней модели, которая вызывает метод ownTo:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
    /**
     * Get the post that owns a comment.
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

Как только связь определена, мы можем получить модель Post для конкретного комментария, обратившись к сообщению «динамическое свойство»:

$comment = App\Comment::find(1);
echo $comment->post->title;

В приведенном выше примере Eloquent попытается сопоставить post_id из модели Comment с идентификатором в модели Post. Eloquent определит имя внешнего ключа по умолчанию, проверив имя метода отношения и добавив суффикс имени метода с _ после имени столбца первичного ключа. Однако, если внешний ключ в модели Comment не является post_id, вы можете передать имя настраиваемого ключа в качестве второго аргумента методу ownTo:

/**
 * Get the post that owns the comment.
 */
public function post()
{
    return $this->belongsTo('App\Post', 'foreign_key');
}

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

/**
 * Get the post that owns the comment.
 */
public function post()
{
    return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
}

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

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

Мы определяем отношения «многие ко многим», написав метод, который возвращает результат метода assignToMany. Например, давайте определим метод ролей в нашей модели User:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    /**
     * The roles that belong to the user.
     */
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}

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

$user = App\User::find(1);
foreach ($user->roles as $role) {
    //
}

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

$roles = App\User::find(1)->roles()->orderBy('name')->get();

Как мы упоминали ранее, для определения имени таблицы объединяющей таблицы отношений Eloquent объединяет два связанных имени модели в алфавитном порядке. Однако вы можете переопределить это соглашение. Вы можете сделать это, передавая второй аргумент в метод ownToMany:

return $this->belongsToMany('App\Role', 'role_user');

Помимо настройки имени присоединяемой таблицы, вы также можете настраивать имена столбцов ключей таблицы, передавая дополнительные аргументы в метод ownToMany. Третий аргумент будет именем внешнего ключа модели, для которой вы определяете отношение, а четвертый аргумент будет именем внешнего ключа модели, к которой вы присоединяетесь:

return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');

Определение обратной связи

Определяя обратное отношение «многие ко многим», вы помещаете еще один вызов в принадлежащую модель к вашей связанной модели. Чтобы продолжить наш пример ролей пользователей, давайте определим метод users в модели Role:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
    /**
     * The users that belong to the role.
     */
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

Теперь вы можете видеть, что отношение было определено точно так же, как его пользовательский аналог, за исключением ссылки на модель AppUser. Поскольку мы повторно используем метод ownToMany, все обычные параметры настройки таблиц и ключей доступны, когда вы определяете обратное отношение «многие ко многим».

Получение промежуточных столбцов таблицы

Как вы уже узнали, работа с отношениями «многие ко многим» потребует наличия промежуточной таблицы. Eloquent предоставляет несколько очень полезных способов взаимодействия с этой таблицей. Например, давайте предположим, что у нашего объекта User есть много объектов Role, с которыми он связан. Получив доступ к этой взаимосвязи, мы можем получить доступ к промежуточной таблице, используя атрибут pivot на моделях:

$user = App\User::find(1);
foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

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

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

return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

Если мы хотим, чтобы наша сводная таблица автоматически поддерживала временные метки create_at и updated_at, мы должны использовать метод withTimestamps в определении отношений:

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

Настройка имени атрибута сводки

Как мы уже отмечали ранее, к атрибутам из промежуточной таблицы можно получить доступ в моделях, используя атрибут Pivot. Однако мы можем настроить имя этого атрибута, чтобы лучше отражать его назначение в нашем приложении.

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

return $this->belongsToMany('App\Podcast')
                ->as('subscription')
                ->withTimestamps();

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

$users = User::with('podcasts')->get();
foreach ($users->flatMap->podcasts as $podcast) {
    echo $podcast->subscription->created_at;
}

Фильтрация отношений через промежуточные столбцы таблицы

Мы также можем отфильтровать результаты, возвращаемые assignToMany, используя методы wherePivotIn и wherePivot при определении отношения:

return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]);

Определение пользовательских промежуточных табличных моделей

Если вы хотите определить пользовательскую модель для представления промежуточной таблицы ваших отношений, вы можете вызвать метод using при определении отношения. Настраиваемые сводные модели многие-ко-многим должны расширять класс IlluminateDatabaseEloquentRelationsPivot, в то время как настраиваемые полиморфные сводные модели многие-ко-многим должны расширять класс IlluminateDatabaseEloquentRelationsMorphPivot. Например, вы можете определить Role, который использует пользовательскую модель RoleUser Pivot:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
    /**
     * The users that belong to the role.
     */
    public function users()
    {
        return $this->belongsToMany('App\User')->using('App\RoleUser');
    }
}

При определении модели RoleUser мы расширим класс Pivot:

<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUser extends Pivot
{
    //
}

Вы можете объединить с помощью Pivot и использовать для того, чтобы извлечь столбцы из промежуточной таблицы. Например, вы можете извлечь столбцы create_by и updated_by из сводной таблицы RoleUser, передав имена столбцов методу withPivot:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
    /**
     * The users that belong to the role.
     */
    public function users()
    {
        return $this->belongsToMany('App\User')
                        ->using('App\RoleUser')
                        ->withPivot([
                            'created_by',
                            'updated_by'
                        ]);
    }
}

Пользовательские модели Pivot и увеличивающиеся идентификаторы

Если вы определили отношение «многие ко многим», в котором используется настраиваемая модель сводных данных, а модель сводных данных имеет первичный ключ с автоинкрементным увеличением, то вы должны убедиться, что класс настраиваемой модели сводных данных определяет свойство приращения, для которого установлено значение true.

/**
 * Indicates if the IDs are auto-incrementing.
 *
 * @var bool
 */
public $incrementing = true;

Имеет один сквозной

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

users
    id - integer
    supplier_id - integer
suppliers
    id - integer
history
    id - integer
    user_id ? integer

хотя таблица истории не содержит столбец supplier_id, отношение hasOneThrough предоставляет доступ к истории пользователя для модели поставщика. Теперь, когда мы успешно проверили структуру таблицы для отношения, давайте определим ее в модели поставщика:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
    /**
     * Get the user's history.
     */
    public function userHistory()
    {
        return $this->hasOneThrough('App\History', 'App\User');
    }
}

Первый аргумент, который передается методу hasOneThrough, - это имя конечной модели, к которой мы хотим получить доступ, а второй аргумент - имя промежуточной модели.

Типичные соглашения о внешних ключах Eloquent используются при выполнении запросов отношения. Если мы хотим настроить ключи отношения, мы можем передать их в качестве третьего и четвертого аргументов методу hasOneThrough. в то время как третий аргумент - это имя внешнего ключа промежуточной модели. Четвертым аргументом будет имя внешнего ключа в окончательной модели. Пятый аргумент будет локальным ключом, а шестой аргумент будет локальным ключом промежуточной модели:

class Supplier extends Model
{
    /**
     * Get the user's history.
     */
    public function userHistory()
    {
        return $this->hasOneThrough(
            'App\History',
            'App\User',
            'supplier_id', // Foreign key on users table...
            'user_id', // Foreign key on history table...
            'id', // Local key on suppliers table...
            'id' // Local key on users table...
        );
    }
}

Имеет много через

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

countries
    id - integer
    name - string
users
    id - integer
    country_id - integer
    name - string
posts
    id - integer
    user_id - integer
    title ? string

Хотя таблица сообщений не содержит столбец country_id, отношение hasManyThrough предоставит доступ к сообщениям страны через $ country-> posts. Для выполнения этого запроса Eloquent проверит country_id в таблице промежуточных пользователей. После того, как он найдет соответствующие идентификаторы пользователей, они будут использованы для запроса таблицы сообщений.

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

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
    /**
     * Get all of the posts for the country.
     */
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

Первый аргумент, передаваемый методу hasManyThrough, - это имя конечной модели, к которой мы хотим получить доступ, а второй передаваемый аргумент - это имя промежуточной модели.

Типичные соглашения о внешних ключах Eloquent используются при выполнении запросов отношения. Если мы хотим настроить ключи отношения, мы можем передать их в качестве третьего и четвертого аргументов методу hasManyThrough. Третьим аргументом будет имя внешнего ключа промежуточной модели. Четвёртый аргумент, который передается, это имя внешнего ключа в финальной модели. Пятый аргумент будет локальным ключом, а шестой аргумент - локальным ключом промежуточной модели:

class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough(
            'App\Post',
            'App\User',
            'country_id', // Foreign key on users table...
            'user_id', // Foreign key on posts table...
            'id', // Local key on countries table...
            'id' // Local key on users table...
        );
    }
}

Полиморфные отношения >

Полиморфные отношения позволят целевой модели принадлежать более чем одному типу модели, использующей одну ассоциацию.

Один в Один (Полиморфный)

Структура таблицы

Отношение один к одному полиморфное сходно с простым отношением один к одному; однако целевая модель может принадлежать более чем одному типу модели в одной ассоциации. Например, сообщение в блоге и пользователь могут иметь полиморфное отношение к модели изображения. Использование однозначного полиморфного отношения позволит нам иметь единый список уникальных изображений, которые используются как для сообщений в блоге, так и для учетных записей пользователей. Сначала рассмотрим структуру таблицы:

posts
    id - integer
    name - string
users
    id - integer
    name - string
images
    id - integer
    url - string
    imageable_id - integer
    imageable_type ? string

Обратите внимание на столбцы imageable_type и imageable_id в таблице изображений. Столбец imageable_id содержит значение идентификатора сообщения или пользователя, а столбец imageable_type содержит имя класса родительской модели. Столбец imageable_type используется Eloquent при определении того, какой «тип» родительской модели будет возвращаться при доступе к отношений imageable.

Структура модели

Далее, давайте рассмотрим определения модели, необходимые для построения этих отношений:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
    /**
     * Get the owning imageable model.
     */
    public function imageable()
    {
        return $this->morphTo();
    }
}
class Post extends Model
{
    /**
     * Get the post's image.
     */
    public function image()
    {
        return $this->morphOne('App\Image', 'imageable');
    }
}
class User extends Model
{
    /**
     * Get the user's image.
     */
    public function image()
    {
        return $this->morphOne('App\Image', 'imageable');
    }
}

Получение Отношения

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

$post = App\Post::find(1);
$image = $post->image;

мы также можем извлечь родителя из полиморфной модели, обратившись к имени метода, который выполняет вызов morphTo. В нашем случае это метод imageable на модели Image. Таким образом, мы все еще можем получить доступ к этому методу как к динамическому свойству:

$image = App\Image::find(1);
$imageable = $image->imageable;

Отношение imageable в модели Image возвращает экземпляр Post или User, в зависимости от типа модели, которой принадлежит изображение.

Один ко многим (полиморфный)

Структура таблицы

Полиморфное отношение «один ко многим» похоже на простое отношение «один ко многим»; однако целевая модель может принадлежать более чем одному типу модели в одной ассоциации. Например, давайте представим, что пользователи вашего приложения могут «комментировать» как посты, так и видео. Если мы используем полиморфные отношения, мы можем использовать одну таблицу комментариев для обоих этих сценариев. Во-первых, давайте рассмотрим структуру таблицы, необходимую для построения этого отношения:

posts
    id - integer
    title - string
    body - text
videos
    id - integer
    title - string
    url - string
comments
    id - integer
    body - text
    commentable_id - integer
    commentable_type ? string

Структура модели

Далее, давайте рассмотрим определения модели, необходимые для построения этих отношений:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
    /**
     * Get the owning commentable model.
     */
    public function commentable()
    {
        return $this->morphTo();
    }
}
class Post extends Model
{
    /**
     * Get all of the post's comments.
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}
class Video extends Model
{
    /**
     * Get all of the video's comments.
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

Восстановление отношений

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

$post = App\Post::find(1);
foreach ($post->comments as $comment) {
    //
}

Мы также можем извлечь владельца полиморфного отношения из полиморфной модели, обратившись к имени метода, который выполняет вызов morphTo. В нашем случае это комментируемый метод в модели Comment. Итак, мы сможем получить доступ к этому методу как к динамическому свойству:

$comment = App\Comment::find(1);
$commentable = $comment->commentable;

Комментируемое отношение в модели Comment, таким образом, вернет экземпляр Post или Video, в зависимости от того, какой тип модели владеет комментарием.

Много ко многим (полиморфный)

Структура таблицы

Полиморфные отношения «многие ко многим» немного сложнее, чем отношения «morphMany» и «morphOne». Например, данная модель блога и видео может иметь полиморфное отношение к модели тега. Использование полиморфного отношения «многие ко многим» позволит вам получить единый список уникальных тегов, которые будут использоваться в блогах и видео. Сначала рассмотрим структуру таблицы:

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

Структура модели

Далее мы готовы определить отношения на модели. Модели Video и Post будут иметь метод tag, который вызывает метод morphToMany базового класса Eloquent:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    /**
     * Get all of the tags for the post.
     */
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

Определение обратной связи

Далее, в модели Tag вы должны определить метод для каждой из связанных с ней моделей. Итак, для этого примера мы определим метод posts и метод videos:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
    /**
     * Get all of the posts that are assigned this tag.
     */
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }
    /**
     * Get all of the videos that are assigned this tag.
     */
    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }
}

Получение Отношения

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

$post = App\Post::find(1);
foreach ($post->tags as $tag) {
    //
}

Вы также можете получить владельца полиморфного отношения из полиморфной модели, обратившись к имени метода, который выполняет вызов morphedByMany. В нашем случае это будут методы записей или видео в модели тегов. Итак, вы сможете получить доступ к этим методам как к динамическим свойствам:

$tag = App\Tag::find(1);
foreach ($tag->videos as $video) {
    //
}

Пользовательские Полиморфные Типы

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

use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
    'posts' => 'App\Post',
    'videos' => 'App\Video',
]);

Вы можете зарегистрировать morphMap в функции загрузки вашего AppServiceProvider или создать отдельного поставщика услуг, если хотите.

Опрос отношений

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

Например, представьте себе систему блогов, в которой модель User имеет много связанных моделей Post:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    /**
     * Get all of the posts for the user.
     */
    public function posts()
    {
        return $this->hasMany('App\Post');
    }
}

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

$user = App\User::find(1);
$user->posts()->where('active', 1)->get();

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

Цепочки или Где оговорки после отношений

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

$user->posts()
        ->where('active', 1)
        ->orWhere('votes', '>=', 100)
        ->get();
// select * from posts 
// where user_id =  and active = 1 or votes >= 100

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

use Illuminate\Database\Eloquent\Builder;
$user->posts()
        ->where(function (Builder $query) {
            return $query->where('active', 1)
                         ->orWhere('votes', '>=', 100);
        })
        ->get();
// select * from posts 
// where user_id = ? and (active = 1 or votes >= 100)

Методы отношений против. Динамические Свойства

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

$user = App\User::find(1);
foreach ($user->posts as $post) {
    //
}

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

Запрос наличия отношений

Когда мы обращаемся к записям для модели, мы можем захотеть ограничить наши результаты в зависимости от наличия отношений. Для этого вы можете передать имя отношения методам has и orHas:

// Retrieve all posts that have at least one comment...
$posts = App\Post::has('comments')->get();

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

// Retrieve all posts that have three or more comments...
$posts = App\Post::has('comments', '>=', 3)->get();

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

`
// Retrieve posts that have at least one comment with votes...
$posts = App\Post::has('comments.votes')->get();

Если вам нужна еще большая мощность, вы можете использовать методы whereHas и orWhereHas, чтобы поставить условия «где» в свои запросы has. Эти методы позволят вам добавить настраиваемые ограничения в ограничение отношений, например, проверить содержимое комментария:

use Illuminate\Database\Eloquent\Builder;
// Retrieve posts with at least one comment containing words like foo%...
$posts = App\Post::whereHas('comments', function (Builder $query) {
    $query->where('content', 'like', 'foo%');
})->get();
// Retrieve posts with at least ten comments containing words like foo%...
$posts = App\Post::whereHas('comments', function (Builder $query) {
    $query->where('content', 'like', 'foo%');
}, '>=', 10)->get();

Запросы Отношения Отсутствие

Когда мы обращаемся к записям для модели, вы можете ограничить наши результаты из-за отсутствия отношений. Чтобы сделать это, мы можем передать имя отношения методам doesntHave и orDoesntHave:

$posts = App\Post::doesntHave('comments')->get();

Если нам понадобится еще больше энергии, мы можем использовать методы whereDoesntHave и orWhereDoesntHave, чтобы поставить условия «где» в наших запросах doesntHave. Эти методы позволяют нам добавлять настраиваемые ограничения в ограничение отношений, например, проверять содержимое комментария:

use Illuminate\Database\Eloquent\Builder;
$posts = App\Post::whereDoesntHave('comments', function (Builder $query) {
    $query->where('content', 'like', 'foo%');
})->get();

мы можем использовать «точечную» запись для выполнения запроса к вложенным отношениям. Например, следующий запрос сможет получить все сообщения с комментариями от авторов, которые не заблокированы:

use Illuminate\Database\Eloquent\Builder;
$posts = App\Post::whereDoesntHave('comments.author', function (Builder $query) {
    $query->where('banned', 1);
})->get();

Опрос полиморфных отношений

Для того чтобы мы могли запросить существование отношений MorphTo, мы можем использовать метод whereHasMorph и другие соответствующие ему методы:

use Illuminate\Database\Eloquent\Builder;
// Retrieve comments associated to posts or videos with a title like foo%...
$comments = App\Comment::whereHasMorph(
    'commentable', 
    ['App\Post', 'App\Video'], 
    function (Builder $query) {
        $query->where('title', 'like', 'foo%');
    }
)->get();
// Retrieve comments associated to posts with a title not like foo%...
$comments = App\Comment::whereDoesntHaveMorph(
    'commentable', 
    'App\Post', 
    function (Builder $query) {
        $query->where('title', 'like', 'foo%');
    }
)->get();

Мы можем использовать параметр $ type для добавления различных ограничений в зависимости от связанной модели:

use Illuminate\Database\Eloquent\Builder;
$comments = App\Comment::whereHasMorph(
    'commentable', 
    ['App\Post', 'App\Video'], 
    function (Builder $query, $type) {
        $query->where('title', 'like', 'foo%');
        if ($type ==='App\Post') {
            $query->orWhere('content', 'like', 'foo%');
        }
    }
)->get();

Скорее, чем передавая массив возможных полиморфных моделей, мы можем предоставить * в качестве символа подстановки и позволить Laravel извлечь все возможные полиморфные типы из базы данных. Laravel затем выполнит дополнительный запрос для выполнения этой операции:


```use Illuminate\Database\Eloquent\Builder;
$comments = App\Comment::whereHasMorph('commentable', '*', function (Builder $query) {
    $query->where('title', 'like', 'foo%');
})->get();

Подсчет связанных моделей

Если вам нужно подсчитать количество результатов отношения без фактической загрузки, вы можете использовать метод withCount, и тогда в ваших результирующих моделях будет размещен столбец {отношение} _count. Например:

$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
    echo $post->comments_count;
}

Вы можете добавить «счетчики» для нескольких отношений, а также добавить ограничения к запросам:

use Illuminate\Database\Eloquent\Builder;
$posts = App\Post::withCount(['votes', 'comments' => function (Builder $query) {
    $query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

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

use Illuminate\Database\Eloquent\Builder;
$posts = App\Post::withCount([
    'comments',
    'comments as pending_comments_count' => function (Builder $query) {
        $query->where('approved', false);
    }
])->get();
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;

Если вы комбинируете withCount с оператором select, убедитесь, что вы вызываете withCount после метода select:

$posts = App\Post::select(['title', 'body'])->withCount('comments')->get();
echo $posts[0]->title;
echo $posts[0]->body;
echo $posts[0]->comments_count;

Нетерпеливая загрузка

Когда мы обращаемся к отношениям Eloquent в качестве свойств, данные отношений будут «загружаться с отложенной загрузкой». Это означает, что данные отношения фактически не загружаются, пока вы не получите первый доступ к свойству. Тем не менее, Eloquent может одинаково «загружать» отношения во время запроса родительской модели. Стремительная загрузка облегчит проблему запроса N + 1. Например:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
    /**
     * Get the author that wrote the book.
     */
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
}

Теперь давайте найдем все книги и их авторов:

$books = App\Book::all();
foreach ($books as $book) {
    echo $book->author->name;
}

Этот цикл выполнит 1 запрос для получения всех книг в таблице, а затем еще один запрос для каждой книги для поиска автора. Итак, если у нас в таблице 25 книг, этот цикл должен был бы выполнить 26 запросов: 1 запрос для исходной книги и 25 дополнительных запросов для поиска автора каждой книги.

мы действительно можем использовать готовую загрузку, чтобы сократить эту операцию до 2 запросов. Когда мы запрашиваем, мы можем указать, какие отношения следует загружать с помощью метода with:

$books = App\Book::with('author')->get();
foreach ($books as $book) {
    echo $book->author->name;
}

В этой операции мы выполним только два запроса:

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

Стремление загружать несколько отношений

Иногда вы можете захотеть загрузить несколько различных отношений в одной операции. Для этого вам нужно просто передать дополнительные аргументы методу with:

$books = App\Book::with(['author', 'publisher'])->get();

Вложенная нетерпеливая загрузка

Чтобы мы могли загружать вложенные отношения, мы можем использовать синтаксис «точка». Например, давайте поспешно загрузим всех авторов книги и все личные контакты автора в одно высказывание Eloquent:

$books = App\Book::with('author.contacts')->get();

Вложенная нетерпеливая загрузка отношений morphTo

Если вы хотите загрузить загрузку отношения morphTo, а также вложенные отношения для различных объектов, которые могут быть возвращены этим отношением, вы можете использовать метод with в сочетании с методом morphWith отношения morphTo. Давайте рассмотрим следующую модель:

<?php
use Illuminate\Database\Eloquent\Model;
class ActivityFeed extends Model
{
    /**
     * Get the parent of the activity feed record.
     */
    public function parentable()
    {
        return $this->morphTo();
    }
}

В этом случае предположим, что модели Event, Photo и Post могут создавать модели ActivityFeed. Кроме того, предположим, что модели событий принадлежат модели календаря, а модели фотографий связаны с моделями тегов, а модели постов принадлежат модели автора.

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

use Illuminate\Database\Eloquent\Relations\MorphTo;
$activities = ActivityFeed::query()
    ->with(['parentable' => function (MorphTo $morphTo) {
        $morphTo->morphWith([
            Event::class => ['calendar'],
            Photo::class => ['tags'],
            Post::class => ['author'],
        ]);
    }])->get();

Стремление загружать определенные столбцы

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

$books = App\Book::with('author:id,name')->get();

Eager Загрузка по умолчанию

Иногда мы можем захотеть всегда загружать некоторые отношения при извлечении модели. Для этого мы можем определить свойство $ with на модели:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
    /**
     * The relationships that should always be loaded.
     *
     * @var array
     */
    protected $with = ['author'];
    /**
     * Get the author that wrote the book.
     */
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
}

Если вы хотите удалить элемент из свойства $ with для одного запроса, вы можете использовать метод без:

$books = App\Book::without('author')->get();

Сдерживая энергичные нагрузки

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

use Illuminate\Database\Eloquent\Builder;
$users = App\User::with(['posts' => function (Builder $query) {
    $query->where('title', 'like', '%first%');
}])->get();

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

use Illuminate\Database\Eloquent\Builder;
$users = App\User::with(['posts' => function (Builder $query) {
    $query->orderBy('created_at', 'desc');
}])->get();

Lazy Eager Загрузка

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

$books = App\Book::all();
if ($someCondition) {
    $books->load('author', 'publisher');
}

Если нам нужно установить дополнительные ограничения запроса для активного запроса на загрузку, мы можем передать массив с ключами, которые мы хотим загрузить. Значения массива должны быть экземплярами Closure, которые получают экземпляр запроса:

use Illuminate\Database\Eloquent\Builder;
$books->load(['author' => function (Builder $query) {
    $query->orderBy('published_date', 'asc');
}]);

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

public function format(Book $book)
{
    $book->loadMissing('author');
    return [
        'name' => $book->name,
        'author' => $book->author->name
    ];
}

Nested Lazy Eager Загрузка и переход

Если вы предпочитаете загружать отношения morphTo и вложенные отношения на различных объектах, которые могут быть возвращены этим отношением, вы можете использовать метод loadMorph.

Этот метод примет имя отношения morphTo в качестве первого аргумента и массив пар модель / отношение в качестве второго аргумента. Чтобы проиллюстрировать этот метод, рассмотрим следующую модель:

<?php
use Illuminate\Database\Eloquent\Model;
class ActivityFeed extends Model
{
    /**
     * Get the parent of the activity feed record.
     */
    public function parentable()
    {
        return $this->morphTo();
    }
}

В этом случае предположим, что модели Event, Photo и Post могут создавать модели ActivityFeed. Кроме того, предположим, что модели событий принадлежат модели календаря, модели фотографий затем связываются с моделями тегов, а модели постов принадлежат модели автора.

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

$activities = ActivityFeed::with('parentable')
    ->get()
    ->loadMorph('parentable', [
        Event::class => ['calendar'],
        Photo::class => ['tags'],
        Post::class => ['author'],
    ]);

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

Метод сохранения

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

$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
$post->comments()->save($comment);

Обратите внимание, что мы не получили доступ к связи комментариев как к динамическому свойству. Скорее, мы вызвали метод comments, чтобы получить экземпляр отношения. Метод save автоматически добавляет соответствующее значение post_id в новую модель Comment.

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

$post = App\Post::find(1);
$post->comments()->saveMany([
    new App\Comment(['message' => 'A new comment.']),
    new App\Comment(['message' => 'Another comment.']),
]);

Рекурсивно сохраняющие модели и отношения

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

$post = App\Post::find(1);
$post->comments[0]->message ='Message';
$post->comments[0]->author->name ='Author Name';
$post->push();

Метод создания

В дополнение к методам saveMany и save вы также можете использовать метод create, который будет принимать массив атрибутов, создавать модель и вставлять ее в базу данных. Опять же, разница между create и save заключается в том, что save принимает полный экземпляр модели Eloquent, тогда как create принимает простой массив PHP:

$post = App\Post::find(1);
$comment = $post->comments()->create([
    'message' => 'A new comment.',
]);

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

$post = App\Post::find(1);
$post->comments()->createMany([
    [
        'message' => 'A new comment.',
    ],
    [
        'message' => 'Another new comment.',
    ],
]);

Вы также можете использовать методы findOrNew, firstOrNew, firstOrCreate и updateOrCreate для создания или обновления моделей отношений.

Принадлежит к отношениям

Когда вы обновляете отношение ownTo, вы можете использовать метод Associate. Этот метод устанавливает внешний ключ для дочерней модели:

$account = App\Account::find(10);
$user->account()->associate($account);
$user->save();

Когда мы удаляем отношение ownTo, мы можем использовать метод disociate. Этот метод устанавливает внешний ключ отношения в нуль:

$user->account()->dissociate();
$user->save();

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

Отношения ownTo, hasOne, hasOneThrough и morphOne позволяют нам определить модель по умолчанию, которая будет возвращаться, если данное отношение равно нулю. Этот шаблон чаще всего называют шаблоном Null Object и помогает удалить условные проверки в нашем коде. В следующем примере отношение пользователя возвращает пустую модель AppUser, если к сообщению не прикреплен ни один пользователь:

/**
 * Get the author of the post.
 */
public function user()
{
    return $this->belongsTo('App\User')->withDefault();
}

Если мы хотим заполнить модель по умолчанию атрибутами, мы можем передать Closure или массив методу withDefault:

/**
 * Get the author of the post.
 */
public function user()
{
    return $this->belongsTo('App\User')->withDefault([
        'name' => 'Guest Author',
    ]);
}
/**
 * Get the author of the post.
 */
public function user()
{
    return $this->belongsTo('App\User')->withDefault(function ($user) {
        $user->name ='Guest Author';
    });
}

Много ко многим отношениям

Присоединение / отсоединение

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

$user = App\User::find(1);
$user->roles()->attach($roleId);

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

$user->roles()->attach($roleId, ['expires' => $expires]);

Иногда может потребоваться удалить роль у пользователя. Чтобы удалить запись отношения «многие ко многим», мы будем использовать метод detach. Метод detach удаляет соответствующую запись из промежуточной таблицы; однако обе модели останутся в базе данных:

// Detach a single role from the user...
$user->roles()->detach($roleId);
// Detach all roles from the user...
$user->roles()->detach();

Для удобства присоединение и отсоединение также принимают массивы идентификаторов в качестве входных данных:

$user = App\User::find(1);
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([
    1 => ['expires' => $expires],
    2 => ['expires' => $expires]
]);

Синхронизация ассоциаций

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

$user->roles()->sync([1, 2, 3]);

Мы также можем передать дополнительные промежуточные значения таблицы с идентификаторами:

$user->roles()->sync([1 => ['expires' => true], 2, 3]);

Если мы не хотим отсоединять существующие идентификаторы, мы можем использовать метод syncWithoutDetaching:

$user->roles()->syncWithoutDetaching([1, 2, 3]);

Тоглинг Ассоциации

Отношение «многие ко многим» также предоставляет метод переключения, который будет «переключать» статус вложения данных идентификаторов. Если данный идентификатор прикреплен в настоящее время, он отключается. в то время как, если это отсоединено в настоящее время, это присоединяется:

$user->roles()->toggle([1, 2, 3]);

Сохранение дополнительных данных в сводной таблице

Когда мы работаем с отношением «многие ко многим», метод save примет массив дополнительных атрибутов промежуточной таблицы в качестве второго аргумента:

App\User::find(1)->roles()->save($role, ['expires' => $expires]);

Обновление записи в сводной таблице

Если нам нужно обновить существующую строку в нашей сводной таблице, мы можем использовать метод updateExistingPivot. Этот метод будет принимать внешний ключ сводной записи и массив атрибутов для обновления:

$user = App\User::find(1);
$user->roles()->updateExistingPivot($roleId, $attributes);

Касаясь родительских отметок времени

Если модель принадлежит ToMany или принадлежит другой модели, например, в случае комментария, принадлежащего публикации, иногда полезно обновить метку времени родителя при обновлении дочерней модели. Например, когда модель Comment обновляется, мы можем захотеть автоматически «прикоснуться» к отметке времени updated_at принадлежащего сообщения. Красноречивый делает это легко. Мы просто добавляем свойство touch, содержащее имена отношений в дочернюю модель:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
    /**
     * All of the relationships to be touched.
     *
     * @var array
     */
    protected $touches = ['post'];
    /**
     * Get the post that the comment belongs to.
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

Теперь, когда мы обновляем комментарий, в сообщении-владельце будет также обновляться столбец updated_at.

Новый контент: Composer: менеджер зависимостей для PHP , R программирования


script1adsense4code
script1adsense5code
disqus2code
script1adsense6code
script1adsense7code
script1adsense8code
buysellads2code