Laravel 5 - constraining by parent relations - php

Say I have a reservations table
id - restaurant_table_id - guest_id
1 - 3 - 5
2 - 4 - 7
And then an orders table
id - reservation_id - item_id
1 - 1 - 2
2 - 2 - 4
How can I pull orders that belong to a restaurant table?
I have tried:
$orders = ReservationOrders::with(['reservation' => function ($query) use ($restaurant_table_id) {
$query->where('restaurant_table_id', $restaurant_table_id);
}])
->get();
But the results included orders for all restaurant tables.

You can filter on relation's attributes using Eloquent's whereHas() method - you can get more information here: http://laravel.com/docs/5.1/eloquent-relationships
In your case sth like the code below should help:
$orders = ReservationOrders::with('reservation')->whereHas('reservation', function($query) use($restaurant_table_id) {
$query->where('restaurant_table_id', $restaurant_table_id);
})->get();

Related

How to get ID from table only if it contains multiple values needed, from different rows

I have a channel_members table containing channel_id, user_id. I need to get the channel_id if there are multiple rows using that same channel_id which contains multiple user_id that I will provide.
Example:
If there are 5 rows in the table
CHANNEL_ID | USER_ID
2 | 2
2 | 3
2 | 4
3 | 2
3 | 4
I need to get the channel_id which are being used by user_id 2, 3, and 4. So based in the table above that I provided. I should get channel_id 2.
I assume you have already defined your relations uisng models, As per your provided description, channel has many to many relation with user
class Channel extends Model
{
public function members()
{
return $this->belongsToMany('App\User', 'channel_members', 'channel_id', 'user_id');
}
}
Using eloquent realtions you can check existance of related models as
$channels = App\Channel::whereHas('members', function ($query) {
$query->where('id', '=', 2);
})->whereHas('members', function ($query) {
$query->where('id', '=', 3);
})->whereHas('members', function ($query) {
$query->where('id', '=', 4);
})->get();
Above will return you only channels who have associations with all 3 users based on supplied id
Querying Relationship Existence
SELECT CHANNEL_ID
FROM channel_members
WHERE USER_ID in (2,3,4)
GROUP BY CHANNEL_ID
HAVING COUNT(CHANNEL_ID) > 0

Multiple leftJoins using Laravel's Query Builder producing incorrect counts

I am using Laravel 5.4's Query Builder to perform a series of leftJoins on three tables. Here are my tables:
items
id type title visibility status created_at
-- ---- ----- ---------- ------ ----------
1 1 This is a Title 1 1 2017-06-20 06:39:20
2 1 Here's Another Item 1 1 2017-06-24 18:12:13
3 1 A Third Item 1 1 2017-06-26 10:10:34
count_loves
id items_id user_id
-- ------- -------
1 1 2
2 1 57
3 1 18
count_downloads
id items_id user_id
-- ------- -------
1 1 879
2 1 323
And here is the code I am running in Laravel:
$items_output = DB::table('items')
->leftJoin('count_loves', 'items.id', '=', 'count_loves.items_id')
->leftJoin('count_downloads', 'items.id', '=', 'count_downloads.items_id')
->where('items.visibility', '=', '1')
->where('items.status', '=', '1')
->orderBy('items.created_at', 'desc')
->select('items.*', DB::raw('count(count_loves.id) as loveCount'), DB::raw('count(count_downloads.id) as downloadCount'))
->groupBy('items.id')
->get();
When I return the results for this query, I am getting the following counts:
count_loves: 6
count_downloads: 6
As you can see, the actual count values should be:
count_loves: 3
count_downloads: 2
If I add another entry to the count_loves table, as an example, the totals move to 8. If I add another entry to the count_downloads table after that, the totals jump to 12. So, the two counts are multiplying together.
If I die and dump the query, here's what I get:
"query" => "select 'items'.*, count(count_loves.id) as loveCount,
count(count_downloads.id) as downloadCount from 'items' left join
'count_loves' on 'items'.'id' = 'count_loves'.'items_id' left join
'count_downloads' on 'items'.'id' = 'count_downloads'.'items_id'
where 'items'.'visibility' = ? and 'items'.'status' = ? group by
'items'.'id' order by 'items'.'created_at' desc"
How do I perform multiple leftJoins using Query Builder and count on several tables to return the proper sums?
NOTE:
This is intended as a HELP answer not the total absolute answer but I could not write the code in a comment. I am not asking for votes (for those who just can't wait to downvote me). I have created your tables and tried a UNION query on raw sql. I got correct results. I dont have laravel installed, but maybe you could try a UNION query in Laravel.
https://laravel.com/docs/5.4/queries#unions
select count(count_downloads.user_id)
from count_downloads
join items
on items.id = count_downloads.items_id
UNION
select count(count_loves.user_id)
from count_loves
join items
on items.id = count_loves.items_id

Delete/detach first pivot table records

Case
Laravel 5.3
Having a pivot table between Cart & Product with an additional column:
id - cart_id - product_id - item_id (additional column)
1 - 1 - 1 - 5
2 - 1 - 1 - 6
3 - 1 - 1 - 7
4 - 2 - 1 - 8
Normally you detach a pivot table record using:
$product->carts()->detach($cartId);
But in this case, there are several pivot table records with the same cart & product id
Problem
Lets say I want delete to row 1.
What I hoped to work was either one of these:
$product->carts()->detach($itemId);
or
$product->carts()->detach($cartId)->first();
If I query the pivot table based on cart_id & product_id, call the first & run delete() on that query result a Call to undefined method stdClass::delete() will be returned
$firstItem = DB::table('cart_product')
->where('cart_id', $cart_id)
->where('product_id', $product->id)
->first();
$firstItem->delete();
Although when I dd() $firstItem after querying the data, it will return a (correct) object
{#238 ▼
+"id": 1
+"cart_id": 1
+"product_id": 1
+"item_id": 5
}
You can't use detach() if you want to delete just one row in this table.
If you want to delete just first item, just use this:
DB::table('cart_product')
->where('cart_id', $cart_id)
->where('product_id', $product->id)
->take(1)
->delete();
Or from your code:
$id = DB::table('cart_product')
->where('cart_id', $cart_id)
->where('product_id', $product->id)
->first()->id;
DB::table('cart_product')
->where('id', $id)
->delete();

Laravel leftJoin only last record of right table

I am new to laravel.
I have two tables.
1) products
2) prices
-----------------------------
- products -
-----------------------------
- id_product | int (p_key) -
- name | varchar -
-----------------------------
-------------------------
- prices -
-----------------------------
- id_price | int (p_key) -
- id_product | int -
- price | int -
-----------------------------
the products table holds data about products like id, name,...
the price changes are stored in prices table where the last record is the newest price that should be displayed to users.
now I want to search through products and get the last price of each product from prices table. this is my query:
$result = DB::table('products')->leftJoin('prices', function($join) {
$join->on('products.id_product', '=', 'prices.id_product');
})->whereRaw(MY_SEARCH_FILTERS);
the above code is wrong because if a product has 4 records in prices table, then it will be repeated 4 times in $result, but only 1 record with the last price should be displayed.
Here we have 2 tables users and answers where users is left table and answers is right table which has user answers.
We wanted to left join users with answers but the join should be with the latest record or answers table.
$query = Users::select('users.id', 'users.user_name','answers.created_at as last_activity_date')
->leftJoin('answers', function($query)
{
$query->on('users.id','=','answers.user_id')
->whereRaw('answers.id IN (select MAX(a2.id) from answers as a2 join users as u2 on u2.id = a2.user_id group by u2.id)');
})
->where('users.role_type_id', Users::STUDENT_ROLE_TYPE)->get();
you can make make it easy by using Laravel Elquent:
class Product extends Model
{
public function lastPrice()
{
// optional: change id_price to created_at by add created_at to prices table
return $this->hasOne(Price::class)->orderBy('id_price', 'DESC');
}
}
now in
public function getProducts(){
$MY_SEARCH_FILTERS=....;
// get all products with last price
$products=Product::with('lastPrice')->whereRaw(MY_SEARCH_FILTERS)->get()
return $products
}
Here we have 2 tables 'articles' and 'comments' where articles is left table and comments is right table which has article's comments.
We wanted to left join articles with comments but the join should be with the latest record from comments table.
$query = Article::select('articles.*', 'comments.comment as article_comment')
->leftJoin('comments', function($query) {
$query->on('comments.article_id','=','articles.id')
->whereRaw('comments.id IN (select MAX(a2.id) from comments as a2 join articles as u2 on u2.id = a2.article_id group by u2.id)');
})
->get();
i found this solution from here https://laravelcode.com/post/how-to-get-last-record-from-leftjoin-table-in-laravel
You need to add two things in here,
1) orderBy descending on prices table.
2) first clause in the DB::table function (It will fetch only 1
record, that will be the latest price).
The solution :
$result = DB::table('products')
->leftJoin('prices',function($join)
{
$join->on('products.id_product', '=', 'prices.id_product')
})->whereRaw(MY_SEARCH_FILTERS)
->orderBy('prices.id_price','desc')
->first();
You can also use (Laravel 5.1) :
$result = DB::table('products')
->leftJoin('products.id','=','prices.id_product')
->whereRaw(MY_SEARCH_FILTERS)
->orderBy('prices.id_price','desc')
->first();

Laravel eloquent Relationship Query

My database schema is
links:
id status user_id url
1 1 1 something
2 1 1 something
3 1 1 something
links_type:
id link_id links_type
1 1 external
2 1 external
3 1 internal
4 1 external
5 2 internal
6 2 external
7 2 internal
8 2 external
i want to take data of all links which status is 1 and user_id is 1
and count external and internal links and which external count is >2.
by using laravel 5.2 eloquent.
result should be like this from data given
id status user_id url external_links internal_links
1 1 1 something 3 1
Just define this relationship in Link Model
public function link_type()
{
return $this->hasMany('App\linkType');
}
and use this query
Link::where('status','=','1')->where('user_id','=','1')->has('link_type','>', '2')->with('link_type')->get();
If you already have the right migrations with fk's and Models following code should be working:
$links = Link::where('status','=','1')->where('user_id','=','1')
->whereHas('links_type',function ($query) {
$query->whereNotIn('external_count', [0,1]);
})->get();
Probably should add with('links_type') for eager_loading (N+1 problem):
$links = Link::with('links_type')->where('status','=','1')->where('user_id','=','1')
->whereHas('links_type',function ($query) {
$query->whereNotIn('external_count', [0,1]);
})->get();
maybe this could work.
before you must create hasMany relation for Link and name as type
$links = Link::where('status', 1)
->wherer('user_id', 1)
->whereHas('type', function($query) {
$query->where(
$query->where('links_type', 'external')->count(), '>', 2
);
})
->get();

Categories