How to get pivot data specific to user in Laravel? - php

I have a users table, an items table and a pivot table item_user.
I can get a list of all users items or a list of all an items users easy enough using belongsToMany relationship.
$item->users;
$user->items;
However I'm using this as a sort of "favorites" relationship.
So if I display ALL items on my website regardless of user, I want to know which ones the user has in the pivot table as the "favorite".
So far I have this:
// Item Model
public function favorites()
{
return $this->belongsToMany('App\Models\User')->withPivot('user_id')->where('user_id', #\Auth::user()->id);
}
It works fine, but the query it runs is this:
select `users`.*, `item_user`.`item_id` as `pivot_item_id`, `item_user`.`user_id` as `pivot_user_id` from `users` inner join `item_user` on `users`.`id` = `item_user`.`user_id` where `user_id` = '1' and `item_user`.`item_id` in ('282', '10', '826', '632', '896', '604', '8', '990', '175', '979', '7', '805', '665', '263', '507', '327', '397', '208', '762', '926', '474', '389', '433', '742', '613', '689', '782', '435', '898', '518')
I don't need the data from the users table, I just need the pivot data:
SELECT user_id, item_id FROM item_user WHERE user_id = 1 AND item_id IN (1, 2, 3, 4, 5, 6, 282);
Is there anyway to run the more efficient version in Laravel?

As much I understand your requirement.. it can be resolve by adding a column to pivot table.
First include 'favorite' on your pivot call to Item Model:
return $this->belongsToMany('User', 'user_items')
->withPivot('favorite');
Note: Vice versa can also define but didn't test that option.
Then to check weather it 'favorite' or not :
$item = User::with('items')->get()->find(1)->items->find(2)->pivot->favorite;
But in that case you also need to organise you relationship structure as user has many items. If you still any question to understand my approach please let me know.

Related

Duplicated records in Yii2 GridView

It was working just fine, but I had to import data afresh in the existing order1 table which is related to order_item table on order.id = order_item.order_id and order_item.location_id = location.id
So to get the location in GridView of order1 table I had relation defined like so:
public function getLocation() {
return $this->hasOne(Location::className(), ['id' => 'location_id'])->viaTable('{{%order_item}}', ['order_id' => 'id']);
}
Now I have multiple records in the GridView. The query formed for the GridView is like:
SELECT `order1`.*
FROM `order1`
LEFT JOIN `order_item` ON `order1`.`id` = `order_item`.`order_id`
LEFT JOIN `location` ON `order_item`.`location_id` = `location`.`id`
where 1 ORDER BY `id` DESC
LIMIT 20;
How I can fix this as inner join or otherwise, so that it returns records only once from order1 table?
In GridView I am using location.location_title.
Note: there are multiple order items per order.
also Tried:
public function getOrderItem()
{
return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
}
public function getLocation()
{
return $this->hasOne(Location::className(), ['id' => 'location_id'])
->via('orderItem');
}
You need to add GROUP BY in your search model to ensure that orders will not be duplicated in query results:
$query->groupBy('order1.id');
Although hasOne() seems to be incorrect (if one order can have multiple items, then it could also have multiple locations), changing this to hasMany() will not fix GridView results. You need to be careful with one-to-many or many-to-many relations, usually you need to use GROUP BY to remove duplicates or adjust your DB structure or joins in search model, to avoid such situation.
BTW: Adding groupBy() in relation definition (getLocation()) is almost always incorrect. This is not a job of relation definition to handle grouping of main model results (you can be almost sure it will create issues with lazy loading).

How to order by column in nested 2 level relationship in Laravel?

I have 3 tables: reports, fields and report_fields which is a pivot between the other 2. What i need to do is order report_field.field by the position column in the field table.
I tried ordering in the relation in the Models or when using with but I may be doing it wrong.
ex:
$query = Report::with([ 'reportFields.field' => function ($q) {
$q->orderBy('position', 'asc');
//$q->orderByRaw("fields.position DESC");
},
Can someone give a basic example of ordering a 2 level nested relationship?
Edit: I do not need to order by any column in the base table but the list of entries in the pivot table by an column in the second table.
Edit2:
To give an example how the output should be ordered:
Report
ReportField
Field.position = 1
ReportField
Field.position = 2
ReportField
Field.position = 3
You can add your needed ordering on the relation of the first table reports:
public function reportFields()
{
return $this->hasMany(ReportFields::class)
->select('report_fields.*')
->join('fields', 'report_fields.field_id', 'fields.id')
->orderBy('fields.position', 'asc');
}

Display Json data in laravel view with Joins

I'm trying to retrieve data from database to laravel blade view with join associate table.
in my case I have two tables called interesting_courses and courses. here some student can have many interesting courses. Therefore courses_id stored as json array in database as follows.
["1","11","15","16"]
but I need to join the courses table to get the associate course name as follows.
["Hospitality","Business Management","Auto Mobile","Health Care"]
Below is my controller
$intresting_courses = DB::table('intresting_courses')
->join('courses','courses.id','=','intresting_courses.courses_id')
->where('intresting_courses.youth_id',$id)
->first();
How can I join the tables.
There is no way you can join it in query natively.
The solution is to write two queries
select * from intresting_courses where youth_id = ? limit 1
select * from courses where id in (1, 2, 3, 4, 5, ...)
that way you can get courses that student intresting.
Here the laravel code for thoses queries.
$intresting_courses = DB::table('intresting_courses')
->where('intresting_courses.youth_id', $id)
->first();
$courses = DB::table('courses')
->whereIn('id', json_decode($intresting_courses->courses_id))
->get();

Laravel get data based on foreign key

So I have 2 tables, TABLE1 and TABLE2.
They are linked throug Eloquent:
TABLE1 has many TABLE2's, and TABLE2 has one TABLE 1.
They are linked with foreign keys as such:
in TABLE2 table1_id -> links to -> TABLE1 id.
How do I retrieve the entire table data or TABLE 2, + replace table1_id with a column of table1 (let's say by booktitle for example)?
Sorry for the abstraction, but this is the best way I can explain it? :D
The easiest way is to make use of Eloquent's eager loading and fetch all records from table2 and their corresponding record in table1.
This will do the trick:
$results = Table2::with('table1')->get();
foreach ($results as $table2record) {
echo $table2record->id; //access table2 data
echo $table2record->table1->booktitle; //access table1 data
}
You need to make the relationship in the models , for example if you have a "Category" and this has many "Products" you need to put this code in your model
public function products()
{
return $this->hasMany('App\Product', 'foreign_key_you_have');
}
and when you need to retrieve all products associated to that category in your view you need to call the function of model, for example
#foreach($category->products as $cp) // with $category->products you call all products associated to your foreign_key
<h1>{{$cp->price}}</h1>
#endforeach

Looping within a loop of a recordset

Just for reference I am using Laravel 5.
I have a two tables
users
id
first name
skills
id
name
and a pivot table
skill_user
skill_id
user_id
if I do a select in MySQL as follows:
select users.id as id, users.first_name, skills.name from users
left join skill_user on users.id = skill_user.user_id
left join skills on skill_user.skill_id=skills.id
I get:
id, first_name, skill
1, Jenna, Reliable
1, Jenna, Organized
2, Alex, Hardworking
3, Barry, Capable
3, Barry, Amiable
3, Barry, Patient
4, Janine, (null)
I pass this through to a view via a Controller:
$peoples = [];
$peoples = \DB::table('users')
->select(\DB::raw('users.id as id, first_name, skill.name as name"'))
->leftJoin('skill_user','users.id','=','skill_user.user_id')
->leftJoin('skills','skill_user.skill_id','=','skills.id')
->get();
return view('find-people', compact(['peoples']));
Now, I want to loop through this in the view (pseudocode):
forelse ( peoples as people )
people - > first_name
people - > skill
empty
no people found
endforelse
Which all works fine in a sense - but the first name gets repeated when there is more than one skill.
I can probably hack a loop of the skills by doing something like comparing user_id to itself but it seems such a clumsy way to do it.
user_id = $peoples->id
while (some looping criteria)
{
write out skills
if($peoples->id != user_id){break;}
}
How do I loop through the recordset in an elegant/eloquent fashion? Or is there a better entirely to do this?
If you define the relationships in your models you don't need to try and construct raw SQL to achieve that. This is what the Laravel ORM "Eloquent" is for!
class People extends Model {
public function skills () {
return $this->hasMany('Skill');
}
}
Then you define the skill model :
class Skill extends Model {
public function People () {
return $this->belongsToMany('People');
}
}
Now you're able to iterate over the People model and for each person, get their ->skills. This way you don't end up with the duplicate issue you're experiencing and you greatly simplify the SQL you're trying to achieve by leveraging the ORM.

Categories