Laravel joins including where there is no match - php

I have a table layout like so:
products
id
productname
product_weights
id
productid
weight
product_flavours
id
productid
flavour
I am trying to generate a list of all possible product variations. Some products have no variation in size, some have no flavour variation, some have both. My current thinking is using a join. I have so far for my query:
DB::table('product_weights')
->join('products', 'products.id', '=', 'product_weights.prodid')
->select('product_weights.value', 'products.productname')
->get();
This gives something kind of useful, var_dump giving:
[{"value":"1kg","productname":"Item 1"},{"value":"2.1kg","productname":"Item 2"},{"value":"250g","productname":"Item 3"},{"value":"1kg","productname":"Item 3"}
The issue is that a product item 5 with no weight variations is not returned. And of course I need to build in flavours too.
I then want to just make an array like {'Item 1 1kg', 'Item 2 2.1kg', 'Item 3 250g', 'Item 3 1kg'}
Any ideas? I kind of feel like I am doing the join wrong. Help would be appreciated!
Update: Thanks to comments below, some progress has been made with leftJoin. I can produce the desired results for weight or flavour, but not both. The code now is:
$product_join_weights = DB::table('products')
->leftJoin('product_weights', 'products.id', '=', 'product_weights.prodid')
->select('products.productname', 'product_weights.value')
->get();
$product_join_flavour = DB::table('products')
->leftJoin('product_flavours', 'products.id', '=', 'productattributes.prodid')
->select('products.productname', 'product_flavours.value')
->get();
Any ideas how they can be combined?

Instead of combining the two arrays, why not just chain the joins together?
$product_join = DB::table('products')
->leftJoin('product_weights', 'products.id', '=', 'product_weights.prodid')
->leftJoin('product_flavours', 'products.id', '=', 'productattributes.prodid')
->select('products.productname', 'product_flavours.value', 'products.productname', 'product_weights.value')
->get();
Left Joins fetch a complete set of records from table1, with the
matching records in table2. The
result is NULL in the right side when no matching will take place.
http://www.w3resource.com/sql/joins/perform-a-left-join.php

I think you could simply double the left join...
something like this:
$product_weights_and_flavours = DB::table('products')
->leftJoin('product_weights', 'products.id', '=', 'product_weights.prodid')
->leftJoin('product_flavours', 'products.id', '=', 'productattributes.prodid')
->select('products.productname', 'product_weights.value', 'product_flavours.value')
->get();

Related

I want to find duplicate data from the database and show sum of duplicate data in laravel 8

I want to get all the data from SALES and PURCHASES table but I want to show product name for the single time and also want to show the sum of its quanitity..
How can i do this in laravel 8 ?
What will be the query ??
$stock = DB::table('products')
->join('purchases','purchases.product_id','products.id')
->join('sales','sales.product_id','products.id')
->groupBy('product_id')
->get();
I dont have your whole query but use havingRaw to find the duplicates.
$dups = DB::table('tableName')
->select('col1_name','colX_name', DB::raw('COUNT(*) as `count`'))
->join('purchases','purchases.product_id','products.id')
->join('sales','sales.product_id','products.id')
->groupBy('col1_name', 'ColX_name')
->havingRaw('COUNT(*) > 1')
->get();
Maybe something like that
$stock = DB::table('products')
->select('sales.*','purchases.*','products.name', DB::raw('products.name as `NbProduct`'))
->join('purchases','purchases.product_id','products.id')
->join('sales','sales.product_id','products.id')
->groupBy('sales.id', purchases.id)
->get();

Display best selling items by status

Explaining my problem, I have two tables in my database called order and order_details.
On my dashboard, I display the three best-selling items (currently work!). However, I would like to display only the best selling items THAT have the status = delivered.
Today, it works like this:
$top_sell_items = OrderDetails::with(['product'])
->select('product_id', DB::raw('SUM(quantity) as count'))
->groupBy('product_id')
->orderBy("count", 'desc')
->take(3)
->get();
The problem is that the order status is stored in another table, called orders, column order_status.
How can I create this rule and include it in my $top_sell_items?
if you relationship is done between this table already, you can use this code, if not you have to go to the OrderDetails Model and add new method orders
$top_sell_items = OrderDetails::with(['product', 'orders'])
->whereHas('orders', function($query) {
$query->where('status', 'delivered');
})
->select('product_id', DB::raw('SUM(quantity) as count'))
->groupBy('product_id')
->orderBy('count', 'desc')
->take(3)
->get();
You could either define a relationship between Orders and OrderDetails or use a join like so...
<?php
$top_sell_items = OrderDetails::with(['product'])
->join('orders', 'orders.id', '=', 'order_details.order_id')
->select('product_id', DB::raw('SUM(quantity) as count'))
->where('orders.order_status', 'delivered');
->groupBy('product_id')
->orderBy("count", 'desc')
->take(3)
->get();
More info here: https://laravel.com/docs/8.x/queries#joins
Depending if you desire this, the following solution might be the most efficient:
$products = Product
::whereHas('orderDetails.order', function ($query) {
$query->where('orders.order_status', 'delivered');
})
->withSum('orderDetails', 'quantity')
->orderBy('order_details_sum_quantity', 'desc')
->take(3)
->get();
It will directly return instances of Product. In addition it puts everything in a single query instead of the two that with produces.

How to make two Laravel Eloquent queries into a single one (so I hit the database once instead of 2 times)

Say I have 3 tables in my database.
'my_recipe', 'my_inventory' and 'ingredient'.
The 'my_recipe' table stores a list of raw_id's based on the 'ingredient' table and the 'quantity' need for the recipe. The 'my_inventory' table stores a list of raw_id's and 'have_quantity'.
So let's take a look at what I currently have at the moment. I have the following 2 queries:
First Query:
$recipe = DB::table('my_recipe as tA')
->leftJoin('ingredient as tB', 'tA.raw_id', '=', 'tB.raw_id')
->select('tA.user as user', 'tA.raw_id as raw_id', 'tA.quantity as quantity',
'tB.ingredient_name as ingredient_name')
->where('user', '=', $user)
->where('raw_id', '=', $raw_id)
->get();
Second Query:
$inventory = DB::table('my_inventory as tA')
->leftJoin('ingredient as tB', 'tA.raw_id', '=', 'tB.raw_id')
->select('tA.user as user', 'tA.have_quantity as have_quantity',
'tB.ingredient_name as ingredient_name')
->where('user', '=', $user)
->get();
The first query returns results that look something like this:
{"user":"jack","raw_id":"853","quantity":2,"ingredient_name":"apple"},
{"user":"jack","raw_id":"853","quantity":4,"ingredient_name":"peach"}
The second query returns results that look something like this:
{"user":"jack","have_quantity":30,"ingredient_name":"apple"},
{"user":"jack","have_quantity":20,"ingredient_name":"apple"},
{"user":"jack","have_quantity":10,"ingredient_name":"apple"},
{"user":"jack","have_quantity":1,"ingredient_name":"peach"},
{"user":"jack","have_quantity":1,"ingredient_name":"peach"}
Notice in the second query results I have to get the sum of the ingredients based on the 'ingredient_name' for my ideal output.
How can I get my ideal output in a single query?
My ideal output would look something like this:
{"user":"jack","raw_id":"853","quantity":2,"ingredient_name":"apple","have_quantity":60},
{"user":"jack","raw_id":"853","quantity":4,"ingredient_name":"peach","have_quantity":2}
It's basically the results of the first query with 'have_quantity' totals from the second query.
EDIT:
my_recipe Model:
'user', 'raw_id', 'quantity'
my_inventory Model:
'user', 'raw_id', 'have_quantity'
ingredient Model:
'raw_id', 'ingredient_name'
Note: In the ingredient model there can be rows with the same 'ingredient_name' but have different 'raw_id'.
Based on our chat conversation I managed to get some extra information on the table structure and what was needed to do to get the wanted results.
For those interested the information can be found here
Anyway I ended up creating the query like this:
SELECT
my_recipe.user AS user,
my_recipe.raw_id AS raw_id,
my_recipe.quantity AS quantity,
ingredient.ingredient_name AS ingredient_name,
IFNULL(SUM(my_inventory.have_quantity),0) AS have_quantity
FROM my_recipe
LEFT JOIN ingredient USING(raw_id)
LEFT JOIN ingredient AS ingredients USING(ingredient_name)
LEFT JOIN my_inventory ON my_inventory.raw_id = ingredients.raw_id
WHERE my_recipe.recipe_id = 853
AND my_recipe.user = 'jack'
AND my_inventory.user = 'jack'
GROUP BY ingredient_name;
Now converting into the needed structure:
$inventory = DB::table('my_recipe')
->leftJoin('ingredient', 'my_recipe.raw_id', '=', 'ingredient.raw_id')
->leftJoin('ingredient AS ingredients', 'ingredient.ingredient_name', '=', 'ingredients.ingredient_name')
->leftJoin('my_inventory', 'my_inventory.raw_id', '=', 'ingredients.raw_id')
->select(DB::raw('my_recipe.user AS user,my_recipe.raw_id AS raw_id,my_recipe.quantity AS quantity,ingredient.ingredient_name AS ingredient_name,IFNULL(SUM(my_inventory.have_quantity),0) AS have_quantity'))
->where('my_recipe.recipe_id', '=', $recipe_id)
->where('my_recipe.user', '=', $user)
->where('my_inventory.user', '=', $user)
->groupBy('ingredient_name')
->get();
Maybe this can solve you problem
//replace count by sum
$inventory = DB::table('my_inventory as tA')
->leftJoin('ingredient as tB', 'tA.raw_id', '=', 'tB.raw_id')
->leftJoin('my_recipe as tC', 'tC.raw_id', '=', 'tB.raw_id')
->select(DB::raw('tA.user as user, tB.ingredient_name as ingredient_name, SUM(tA.have_quantity) have_quantity'))
->where('user', '=', $user)
->groupBy('tB.ingredient_name, tA.user')
->get();
Let's try this:
$result = DB::table('ingredient as ing')
->rightJoin('my_recipe as rcp', 'ing.raw_id', '=', 'rcp.raw_id')
->rightJoin('my_inventory as inv', 'ing.raw_id', '=', 'inv.raw_id')
->select(DB::raw('
rcp.user as user,
ing.ingredient_name as ingredient_name,
rcp.have_quantity quantity,
SUM(inv.have_quantity) have_quantity
'))
->where('rcp.user', '=', $user)
->where('rcp.raw_id', '=', $raw_id)
->groupBy('rcp.user, ing.ingredient_name')
->get();

Laravel Eloquent - Select MAX with other columns

I'm trying to select a number of columns along with MAX. The raw query would be something like: SELECT users.id, ..., MAX(ur.rank) AS rank but I cannot figure out how to do it using the query builder supplied by Laravel in Eloquent.
This is my attempt:
$user = User::join('users_ranks AS ur', function($join) {
$join ->on('ur.uid', '=', 'users.id');
})
->where('users.id', '=', 7)
->groupBy('users.id')
->first(['users.id', 'users.username', 'MAX(ur.rank) AS rank']);
I simply cannot figure it out. What I want to achieve is I'm selecting a user where users.id = 7, and I'm wanting to select the MAX rank that's in users_ranks where their users_ranks.uid = users.id.
I was told to avoid sub-queries as when working with large result sets, it can slow things down dramatically.
Can anyone point me in the right direction? Thanks.
I think you should rewrite it like this:
DB::table('users')
->select(['users.id', 'users.username', DB::raw('MAX(ur.rank) AS rank')])
->leftJoin('users_ranks AS ur', 'ur.uid', '=', 'users.id')
->where('users.id', '=', 7)
->groupBy('users.id')
->first();
No sense to use User:: if you use table names later and want to fetch not all of the fields ( 'users.id', 'users.username' ).

Laravel Query Builder - sum() method issue

I'm new to laravel and I have some issues with the query builder.
The query I would like to build is this one:
SELECT SUM(transactions.amount)
FROM transactions
JOIN categories
ON transactions.category_id == categories.id
WHERE categories.kind == "1"
I tried building this but it isn't working and I can't figure out where I am wrong.
$purchases = DB::table('transactions')->sum('transactions.amount')
->join('categories', 'transactions.category_id', '=', 'categories.id')
->where('categories.kind', '=', 1)
->select('transactions.amount')
->get();
I would like to get all the transactions that have the attribute "kind" equal to 1 and save it in a variable.
Here's the db structure:
transactions(id, name, amount, category_id)
categories(id, name, kind)
You don't need to use select() or get() when using the aggregate method as sum:
$purchases = DB::table('transactions')
->join('categories', 'transactions.category_id', '=', 'categories.id')
->where('categories.kind', '=', 1)
->sum('transactions.amount');
Read more: http://laravel.com/docs/5.0/queries#aggregates
If one needs to select SUM of a column along with a normal selection of other columns, you can sum select that column using DB::raw method:
DB::table('table_name')
->select('column_str_1', 'column_str_2', DB::raw('SUM(column_int_1) AS sum_of_1'))
->get();
You can get some of any column in Laravel query builder/Eloquent as below.
$data=Model::where('user_id','=',$id)->sum('movement');
return $data;
You may add any condition to your record.
Thanks
MyModel::where('user_id', $_some_id)->sum('amount')

Categories