Laravel whereHas ManyToMany relation but then order by a value in the third table - laravel

I have 3 tables
entry (
id,
title,
other_stuff)
entry_award (
id,
award_id,
entry_id)
award (
id,
name,
type)
I am trying to create a laravel query which lets me get all the entries that have awards, and order them by award.type ASC
$Entries = Entry::with('award')
->whereHas('award', function ($query) {
$query->orderBy('award.award_id','ASC');
})->paginate(20);
But this doesn't work.
This is the sql version of it
SELECT DISTINCT entry.*
FROM entry, award, entry_award
WHERE entry.id = entry_award.entry_id
AND award.id = entry_award.award_id
ORDER BY award.type ASC;
Now I tried to just use the raw sql for it, but the problem seems to be that laravel does not then recognize the result as Entry models/objects. And i need to get other Entry relations later on in the html via blade.
So how can I either make a query-builder query that gets me all entries that have awards and orders them by award.type value
or use the raw sql but have Laravel see it as an array of Entry
objects instead of just an array of JSON values.
class Entry extends Model {
public function entry_award(){
return $this->belongsToMany('App\Award', 'entry_award');
}
}
class Award extends Model {
public function entries() {
return $this->belongsToMany('App\Entry', 'entry_award');
}
}

Related

Get data through pivot table in Laravel

I got 3 tables. Table 1 & 2 has their ids as foreign keys in third one(pivot).
Relations for first one is
$this->hasMany("App\Pivot","game_id");
, second is
$this->belongsToMany("App\Pivot","army_id");
and pivot has relationships with both of them i.e belongsTo.
My schema:
I tried accessing it in controller of first one like this:
$games= Game::with("armies")->get();
Result that i get is array of games where instead of individual army data , i get collection from pivot table.
I can loop through it and get it that way, is there more elegant way of doing it?
If you are using pivot table this is the way how to do it.
Games Model
public function armies()
{
return $this->belongsToMany(App\Armies::class, 'pivot_table', 'game_id', 'army_id');
}
Armies Model
public function armies()
{
return $this->belongsToMany(App\Games::class, 'pivot_table', 'army_id', 'game_id');
}
Access the relationship like this..
Controller
App\Games::first()->armies()->get();
or
App\Games::first()->armies
or
App\Games::find(1)->armies
If you're going to use an intermediate table like that I'd probably do something like this:
Games model
public function armies()
{
return $this->belongsToMany('App\Armies');
}
Armies model
public function games()
{
return $this->belongsToMany('App\Games');
}
I'd keep the table structures all the same but rename the "pivot" table to armies_games since this is what Laravel will look for by default. If you want to keep it named Pivots, you'll need to pass it in as the second argument in belongsToMany.
With this, you don't really need the Pivot model, you should just be able to do:
$armies = Game::first()->armies()->get();
or
$armies = Game::find(3)->armies()->orderBy('name')->get();
or
$game = Game::first();
foreach ($game->armies as $army) {
//
}
etc.

Generate sugestions by genre id

Using laravel 7 and pivot tables i have something like this:
Model: show
public function genres()
{
return $this->belongsToMany('App\ShowGenre');
}
Model: ShowGenre
public function shows()
{
return $this->belongsToMany('App\Show', 'show_show_genre')->orderBy('name', 'asc');
}
In mysql database i have show_genres where i put my show_id and genre_id. So i want to create something like suggestion based.
At bottom my page i want to display 5 random tv shows that have same genre. Some tv shows have multiple genres so how can i display that and limit it with suma 5.
show_show_genre should have show_id and show_genre_id because show_show_genre is pivot table. show_genres is not pivot table since it is genre's table as described in code. Also, you don't need to set show_show_genre in return statement since Laravel will automatically recognize it's pivot table.
Try something like this:
$show = Show::findOrFail($id);
$featuredShows = Show::whereHas('genres', function ($query) use ($show) {
$query->whereIn('show_genres.id', $show->genres()->pluck('id')->toArray());
})->where('id', '!=' , $show->id)->with(['genres'])->inRandomOrder()->limit(5)->get();

Laravel returns a Collection with duplicates of the first model

I'm developing a Laravel 5.7 (API) application with a PostgreSQL database behind it. The relevant Models are: User (customers and employees), Car, and Request.
An employee User creates a Request for a Car, that belongs to a customer User.
The relationships are:
Car (as customer) : User = n:m
Car : Request = 1:n
User : Request (as employee) = 1:n
(The data design is suboptimal, to put it mildly, but anyway, it's the given reality for now.)
Now to the actual issue. I want to display all Requests of a customer User:
Request::query()
->join('user_car', 'user_car.car_id', '=', 'request.car_id')
->join('user', 'user.id', '=', 'user_car.user_id')
->where('user.id', '=', $customer->id)
->select()
->get();
The customer with the given $customer->id has n Requests. And the length of the result Collection of the call above is correct. But all these n entries are duplicates of the first one. Means: I'm getting a list with n instances of Request#1.
Why does the first call return a list of references to the same Model object? Is it a (known) bug?
ADDITIONAL INFORMATION
Relationships:
class User extends \Illuminate\Foundation\Auth\User
{
// ...
public function cars()
{
return $this->belongsToMany('App\Car', 'user_car')->withTimestamps();
}
public function requests()
{
return $this->hasMany(Request::class, 'user_id');
}
}
class Car extends Model
{
// ...
public function users()
{
return $this->belongsToMany('App\User', 'user_car')->withTimestamps();
}
public function requests()
{
return $this->hasMany(Request::class);
}
}
class Request extends Model
{
// ...
public function car()
{
return $this->belongsTo(Car::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
The query is correct.
I logged the database requests, got the generated statement
SELECT *
FROM "request"
INNER JOIN "user_car" ON "user_car"."car_id" = "request"."car_id"
INNER JOIN "user" ON "user"."id" = "user_car"."user_id"
WHERE "user"."id" = 1;
..., and executed it manually. The result table contains as expected n different entries.
NOT just references
The result Collection's entries instances references to the different objects:
$test1 = $resultCollection->first();
$test2 = $resultCollection->last();
$test3 = spl_object_hash($test1);
$test4 = spl_object_hash($test2);
Xdebug output:
$test3 = "0000000077505ccd000000007964e0a8" <-- ccd0
$test4 = "0000000077505c33000000007964e0a8" <-- c330
Workaround
I found a workaround. This call
Request::whereIn('car_id', $customer->cars()->pluck('id')->toArray())->get();
... retrieves the correct/expected set of model.
First, note that your object hashes are not actually identical, and you're likely dealing with two separate instances.
What you're likely experiencing is an issue with ambiguous column names. When you JOIN together multiple tables, any matching/duplicate column names will contain the value of the last matching column. Your SQL GUI/client usually separates these. Unfortunately Laravel doesn't have a prefixing mechanism, and just uses an associative array.
Assuming all of your tables have a primary key column of id, every Request object in your result set will likely have the same ID - the User's ID you pass in the WHERE condition.
You can fix this in your existing query by explicitly selecting the columns you need to prevent ambiguity. Use ->select(['request.*']) to limit the returned info to the Request object data.

Many to many relationship returns empty set

I have a following table structure in my existing database :
tbl_Grzyby (ID, Nazwa, ...) - main table,
tblk_TypGrzyba (ID, TypGrzyba, OrderNo) - lookup table,
tblm_TypGrzyba (ID_Grzyb, ID_TypGrzyba) (many-to-many table).
I've created a grzyby Eloquent model class, which has a custom property, defined as:
class grzyby extends Model
{
protected $table = 'tbl_Grzyby';
public function typ_grzyba() {
return $this->belongsToMany('\App\Lookups\typ_grzyba', 'tblm_TypGrzyba','ID_Grzyb', 'ID_TypGrzyba');
}
}
I've also created the lookup table model, defined as:
class typ_grzyba extends Model {
protected $table = 'tblk_TypGrzyba';
public function grzyby() {
return $this->belongsToMany('\App\grzyby', 'tblm_TypGrzyba','ID_Grzyb', 'ID_TypGrzyba');
}
}
When I try with tinker to get record from main with ID=67, like:
$main_record=\App\grzyby::where('ID',67)->first();
it returns me all the details for that record fine. Returning all lookups runs also fine:
$types = \App\Lookups\typ_grzyba::all();
However, if I try to do the following:
$main_record_types = $main_record->typ_grzyba;
tinker returns me an empty collection, despite record existing in the many to many table:
When I run the query log it outputs me following SQL query:
select `tblk_TypGrzyba`.*, `tblm_TypGrzyba`.`ID_Grzyb` as `pivot_ID_Grzyb`, `tblm_TypGrzyba`.`ID_TypGrzyba` as `pivot_ID_TypGrzyba` from `tblk_TypGrzyba` inner join `tblm_TypGrzyba` on `tblk_TypGrzyba`.`id` = `tblm_TypGrzyba`.`ID_TypGrzyba` where `tblm_TypGrzyba`.`ID_Grzyb` is null
If I run that query against the database it returns me empty result, however, if I change the where clause manually to
where `tblm_TypGrzyba`.`ID_Grzyb` = 67
I'm getting the expected results in phpMyAdmin.
What's the reason of it and how I can fix that?
The solution was to add the primaryKey overrride because I was using uppercase for the ID field and then restart tinker.
You mixed up your foreign key defintion in your relationships. You wrote this twice:
return $this->belongsToMany('\App\grzyby', 'tblm_TypGrzyba','ID_Grzyb', 'ID_TypGrzyba');
I assume copy pasting? in the second model, justt swap the foreign key. Foreign key of the current model comes first.
In typ_grzyba model, do
public function grzyby() {
return $this->belongsToMany('\App\grzyby', 'tblm_TypGrzyba', 'ID_TypGrzyba', 'ID_Grzyb');
}

Correct relationship in Laravel

I have four tables in database: groups, specialties, lessons, group_lesson. It's structures:
groups
id
specialty_id
name
specialties
id
name
lessons
id
specialty_id
group_lesson (UNIQUE INDEX lesson_id, group_id)
lesson_id
group_id
date
My models look like that for now:
class Group extends Eloquent {
public function specialty() {
return $this->belongsTo('Specialty');
}
}
class Lesson extends Eloquent {
public function specialty() {
return $this->belongsTo('Specialty');
}
}
class Specialty extends Eloquent {
public function lessons() {
return $this->hasMany('Lesson');
}
public function groups() {
return $this->hasMany('Group');
}
}
I need get additional fields in Group model look like that
Group - Eloquent model
name - string
lessons - collection of Lesson models of Group Specialty
date - date from group_lesson table
I've tried different relationships and combinations, but it's doesn't work. Please help me to write correct relationships.
You can use eager-loading to access relational data through relationships, and can even chain relationships further. As a rule of thumb, if you can draw a path to from 1 model to another through a relationship, you can eagerload all the relevant and relational data for that with chained eager-loads.
Laravel Eager Loading
As an example
$speciality_group = Speciality::with('group','lessons')->find($id);
Even though you are only getting a single instance of the speciality model, the related data is hasMany, meaning multiple records. You need to loop through these records using a foreach loop to access the relevant data for them, or alternitavely add additional closures in your initial query to load only a single related model.
foreach($speciality_group->group as $group)
{
echo $group->name;
}
You will need to do this for both instances where you want to display related information.

Resources