Laravel leftJoin only last record of right table - php

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();

Related

model relation with and without foreign key

please ignore syntax errors
im working on a system with these tables
products : id , title
1 p1
custom_field : id , title
10 color
11 something all products have
custom_field_attach : custom_field_id , product_id
10 1
11 0
i want to get product custom fields
i can have this relation in Product model
function CustomFields(){
return $this->belongsToMany(CustomField::class , 'custom_field_attach');
}
and have this query
$product = Product::with('CustomFields')->get();
but the thing is some custom_fields are for all the products so in the custom_field_attach table
product_id is 0for those custom_fields
sine my relation is using product_id as foreign key i wont get those rows with product_id = 0 in the result
basically i want
custom_field_attach where product_id = product.id OR product_id = 0
to make thisng simpler i can have this relation in Product model
function CustomFieldAttach(){
return $this->hasMany(CustomFieldAttach::class);
}
no i want something like
$product = Product::with(['CustomFieldAttach' => function($CustomFieldAttach){
// where forieng key works product_id=product.id orWhere product_id =0
}])->get();
i know i can do this with query builder but i want to use relation if possible
please note someone else has written this code and im just adding some features to the system ... changing the db structure is not an option

Using Orderby before GroupBy in Laravel

I have a table that I want to select product_name with lowest/min price the following:
product_name | price
Cat | 12
Dog | 21
Cat | 14
Dog | 20
Fish | 10
Fish | 3
THE DESIRE Output should be
Cat | 12
Dog | 20
Fish | 3
BELOW IS MY SQL QUERY
$products = DB::table('products')
->orderBy('products.products_price', 'asc')
->groupBy('products.products_name')
->get();
When I used this script, it only shows the highest/max price, not the lowest price
You need an aggregate instead of ordering. For Laravel, that means passing in the columns along with a DB::raw:
$products = DB::table('products')
->orderBy('products.products_price', 'asc')
->groupBy('products.products_name')
->select(['product_name', DB::raw('min(price) as price')])
->get();
Edit for ID
Taking off the answer here : SQL: Group by minimum value in one field while selecting distinct rows the mysql query would be
SELECT p1.*
FROM products p1 INNER JOIN
(
SELECT product_name, MIN(price) AS as min_price
FROM products
GROUP BY product_name
) p2 ON p1.product_name = p2.product_name AND p1.price = p2.min_price
Now we have to convert it to Query Builder
$products = DB::table('products AS p1')
->join(DB::raw('(
SELECT product_name, MIN(price) AS as min_price
FROM products
GROUP BY product_name
) AS p2'),
function($join)
{
$join->on('p1.product_name', '=', 'p2.product_name');
$join->on('p1.price', '=', 'p2.min_price');
})
->get(['p1.id', 'p1.product_name', 'p1.price']);
This has not been tested, so I hope it works
Problem:
You are not defining that the price attribute should be aggregated to a minimum.
Solution:
If you want the max price you need to select the MIN() aggregation.
You can do this by using ->selectRaw('MIN(price) as max_price').
Please Note:
If you want other attributes also selected simply add them comma separated.
->selectRaw('name, MAX(price) as max_price')
#edit
Do you still use the oderBy? If not, try it with orderBy('products.products_price', 'ASC')

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

Display products of sub-categories on parent category page in php, mysql

I have an issue in getting products on parent-category page.
My database table structure:
Parent-category:
id -catname
1 - Mobiles & Tablets
2 - Compuetrs
Sub-category:
id cid sub_name
1 1 Mobiles
2 1 Tablets
3 2 Desktops
4 2 Laptops
Products tables structure: My products tables are multiple and based on sub-categories. Example: Tablets products are found under tablets table and mobiles products are found under mobiles table. Products are stored under different tables based on their sub-categories.
id cid sub_id product_name
1 1 1 Lenovo latest mobile
2 2 3 Dell Monitor
Now i want to fetch products from tables(mobiles,tablets,desktops,laptops) on parent category pages. i tried this with union all but at a time only one table is fetching in query. Could anyone suggest something. Thanks in advance.
<?php
if(isset($_GET)) {
$cid = $_GET['id'];
if($cid == 1){
$tablename = "mobiles";
}
if($cid == 2){
$tablename = "computers";
}
$results=mysql_query("SELECT * FROM $tablename WHERE cid = '$cid'");
while($rows=mysql_fetch_array($results)){
// code
}
}
Query in loop reduces performance, you can always join tables and get data by querying database only once,
Try,
SELECT products.product_name, parent_category.catname
FROM products
JOIN parent_category ON products.cid = parent_category.id
Joins also work on multiple tables, suppose you want to get category as well as sub category,
Try,
SELECT products.product_name, parent_category.catname, sub_category.sub_name
FROM products
JOIN parent_category ON products.cid = parent_category.id
JOIN sub_category ON products.sub_id = sub_category.id
More about joins: here

How can i use sql count in these multiple tables?

I am still a php/mysql newbie and I am working on mysql table relationship concept and i am having an issue with using mysql count in multiple table. Here is my db structure.
**product table**
id product_name product_img groupeid
1 Sneaker Mark sneaker_adi.png 1
2 bag Eric bageric.png 2
3 Sneaker Etoi sneakeretoi.jpg 1
**groupe table**
group_id group_name
1 men
2 women
**category table**
catid catname
1 sneaker-shoes
2 bag-woman
**productcategory table**
prod_id cat_ID
1 1
2 2
3 1
What i want to do is to determine the number of sneaker-shoes using mysql.
We can see that the number of sneaker-shoes in the db is 2.
But how can i use **count()** in these multiple tables.
I tried like this;
$sql = "SELECT COUNT(*) product.id,product_name,catname FROM product INNER JOIN productcategory ON product.id = prod_id INNER JOIN category ON catid = cat_ID WHERE catname='sneaker-shoes'";
i got error like:
Fatal error: Call to a member function execute() on a non-object in C:\wamp\www\kbashopping\Homme\index.php on line 32
Hope i exposed the issue clearly, any help and assistance will be appreciate
Thanks
If you are looking only for the count, mention only the count phrase in the Select clause.
Change :
SELECT COUNT(*) product.id,product_name,catname FROM
to :
SELECT COUNT(product.id) FROM
SELECT count (pc.cat_ID) FROM productcategory pc inner join category c on c.catid = pc.cat_ID where c.catname = 'sneaker shoes';
This will build a temporary table in mysql that joins category and product category but only including results where the catname is sneaker shoes. Then it selects a column to run the count operation on, and returns the result of count.

Categories