Eloquent: Relationships


https://laravel.com/docs/5.8/eloquent-relationships#many-to-many

Eloquent - реализация паттерна active record в Laravel.

Главная идея Eloquent  в том, что модели могут вытаскивать из таблицы сразу все поля, если правильно задать аттрибуты-массивы $visible и $hidden. 

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
    use Concerns\HasAttributes, # по умолчанию $attributes; добавляемые $appends = []; привести к типу $casts = [] - integer, real, float, double, decimal:<digits>, string, boolean, object, array, collection, date, datetime, and timestamp
        Concerns\HasEvents,
        Concerns\HasGlobalScopes,
        Concerns\HasRelationships, # functions hasOne hasMany...  | loaded relationships for the model - $relations = []
        Concerns\HasTimestamps, 
        Concerns\HidesAttributes, # attributes for serialization - $hidden = [] , $visible = []
        Concerns\GuardsAttributes, # mass assignable  attributes - $fillable , $guarded = ['*'];  
        ForwardsCalls;

Связи делаются функциями:

# прямые связи у модели App\User
return $this->hasOne('App\Profile', 'foreign_key', 'local_key'); # у пользователя один профиль
return $this->hasMany('App\Phone', 'user_id', 'id');   # у пользователя может быть много телефонов
# обратная у App\Phone
return $this->belongsTo('App\User',  'user_id'); # телефон принадлежит одному пользователю

В отличии от Doctrine и Django, которые по моделям могут создавать файлы миграции, Eloquent заставляет писать их вручную.

# многие ко многим . связующую таблицу (её называют pivot) называют по именам основных в алфавитном порядке
return $this->belongsToMany('App\Address', 'address_user', # у пользователя может быть несколько адресов проживания
           'user_id',  'address_id',  #  по одному адресу могут жить несколько пользователей
           'id', 'id')
->withPivot(['rent', 'end_date'])
->withTimestamps(); # в pivot может быть доп информация  (стоимость аренды, дата выселения)

 

LARAVEL 5 ELOQUENT: ЧТО ЗНАЧИТ HAS, WITH, WHEREHAS

Запрос по связи в модели

# если делаем id вместо code то будет ошибка хз почему
                $eloquentQueryBuilder->whereHas('details', function ($q1) {
                    $q1->whereHas('about', function ($q2) {
                        $q2->where('code', 'active');
                    });
                });
# все дома которые сдаются от 1000 до 9999
$eloquentQueryBuilder->whereHas('adress', function ($query) {
    return $query->whereBetween('adress_user.rent', [1000, 9999]);
});
public function cars()
{
    return $this->belongsToMany('App\Car','car_customer','car_id','customer_id');
}
#Query to get all customers with N cars:
 $userInput = 2;
 $data = Customer::with('cars')
                ->withCount('cars')
                ->has('cars', '<', $userInput)
                ->orderBy('cars_count', 'desc')
                ->get();
# or 
$customers = Customer::whereHas("cars", function($query) use ($input) {
    $query->where(DB::raw("count(cars.id)"), "<", DB::raw($input))
})->get();

Ограничение количества элементов у связи

->with(['comments'=>function($query) {
    return $query->limit(5);
}])

Миграции

$table->...->onDelete('CASCADE');
$table->...->onDelete('SET NULL');
$table->...->onDelete('RESTRICT');

        Schema::table('test', function (Blueprint $table) {
            $table->dropForeign( ['mytest_id']); //  если массив то можно без *_foreign
        });

 

Builder

# pre condition для  каждого запроса
    public function newQuery()
    {
        $query = parent::newQuery();
        return $query->whereNotNull("avatar");
    }

 

 

 

 * hasOne / hasMany (1-1, 1-M)
    -save(new or existing child)
    -saveMany(array of models new or existing)
    -create(array of attributes)
    -createMany(array of arrays of attributes)
    ---------------------------------------------------------------------------

 * belongsTo (M-1, 1-1)
    -associate(existing model)
    ---------------------------------------------------------------------------

 *  belongsToMany (M-M)
    -save(new or existing model, array of pivot data, touch parent = true)
    -saveMany(array of new or existing model, array of arrays with pivot ata)
    -create(attributes, array of pivot data, touch parent = true)
    -createMany(array of arrays of attributes, array of arrays with pivot data)
    -attach(existing model / id, array of pivot data, touch parent = true)
    -sync(array of ids OR ids as keys and array of pivot data as values, detach = true)
    -updateExistingPivot(relatedId, array of pivot data, touch)
    ---------------------------------------------------------------------------

 *  morphTo (polymorphic M-1)
    // the same as belongsTo
    ---------------------------------------------------------------------------

 *  morphOne / morphMany (polymorphic 1-M)
    // the same as hasOne / hasMany
    ---------------------------------------------------------------------------

 *  morphedToMany /morphedByMany (polymorphic M-M)
    // the same as belongsToMany

 

Joins

https://laracasts.com/discuss/channels/eloquent/left-outer-join-using-eloquent-with-and-operator

$q = Houses::select("houses.*");
$q->join('favorites', 'houses.id', '=', 'favorites.house_id', 'left outer');
$q->join('users', 'users.id', '=', 'favorites.user_id', 'left outer');
$q->where(function ($q) {
    $q->where('users.id', 1);
    $q->orWhereNull('users.id');
});
$q->orderBy('status', 'ASC');
$q->orderBy('users.id', 'desc');

https://reinink.ca/articles/ordering-database-queries-by-relationship-columns-in-laravel

https://laraveldaily.com/post/order-by-belongsto-relationship-column-eloquent-vs-query-builder

$product_id = $product->id;
$ratings = Rating::select('ratings.id', 'ratings.score', 'ratings.product_id', 'ratings.user_id')
		->join('users', 'users.id', '=', 'ratings.user_id')
		->join('products', function($query) use($product_id) {
			$query->where('products.id', $product_id)
				->where('ratings.product_id', $product_id);
		})
		->orderBy('users.name', 'asc')
		->get();
# 
                $eloquentQueryBuilder->select(['users.*', 'groups.name as group_name']);
                $eloquentQueryBuilder->join('groups', 'users.group_id', '=', 'groups.id');
                $eloquentQueryBuilder->orderBy('group_name', $sort_order);

OrderBy random field

->select(["users.*",
    DB::raw("(select max(ms.created_at) from messages ms  WHERE ms.sender_id = users.id and ms.viewer_id = users.id) as last_message_time")
])
->orderBy('last_message_time', 'desc')

whereRaw

        $q->whereRaw("users.id in (select user_id from favorites where house_id = {$house->id})");