Laravel - Eloquent - How to sum related data and query agaist it? - php

I'm looking for help with database queries, not collection solution
I have the following models:
User
Order
Product
User hasMany orders, and orders belongsToMany products
I'm in a place where I would need to query users and select all sold products, meaning the sum of all products quantities that are attached to the orders.
quantity value is stored in the order_product pivot table.
Table name: users, orders, products & order_product
Ideally, I would like to make queries like: select all users that have sold at least 100 products, for example.
DB::raw() & selectRaw is most likely the way to go, I think(?), but I'm not sure about the syntax and how to actually make the query, with and without where clause.
Thanks a lot in advance, this has bothered me for a while
Database Schemas
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
});
Schema::create('orders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->index();
});
Schema::create('order_product', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('order_id')->index();
$table->unsignedBigInteger('product_id')->index();
$table->integer('quantity')->unsigned()->default(1);
});
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
});
UPDATE
This far I have come:
\App\User::addSelect([
'sold_products_count' => \App\Order::whereColumn('user_id', 'users.id')
->join('order_product', 'orders.id', '=', 'order_product.order_id')
->select(DB::raw('sum(order_product.quantity) as qty')),
])->where('users.sold_products_count', '>=', 100);
HOWEVER, the last statement where('users.sold_products_count', '>=', 100) throws error, cuz there's no sold_products_count column.
So I think I'm on the right track, but how I can use the new sum column in where clause?
Can I use addSelect, or do I have to use something else?

Finally, I solved this
Here's the answer:
\App\User::addSelect([
'sold_products_count' => \App\Order::whereColumn('user_id', 'users.id')
->join('order_product', 'orders.id', '=', 'order_product.order_id')
->select(DB::raw('sum(order_product.quantity) as qty')),
])->having('sold_products_count', '>=', 100);
The idea is to first count the sum via addSelect and then we can query against the value using having, neat

DB::table('users')
->join('orders', 'orders.user_id', '=', 'users.id')
->join('order_product', 'orders.id', '=', 'order_product.order_id')
->select('users.*', DB::raw('sum(order_product.quantity) as qty'))
->having('qty', '>=', 100)
->get();

Related

Select statement with group_concat not working when other columns are called in laravel

Hello my laravel code is
$productDetails = DB::table('products')
->select(DB::raw('products.name, GROUP_CONCAT(sizes.name) as sizesName'))
->join('subcategories', 'products.subcategories_id', '=', 'subcategories.id')
->join('size_categories', 'subcategories.categories_id', '=', 'size_categories.categories_id')
->join('sizes',function($join){
$join->on(DB::raw("FIND_IN_SET(sizes.id, size_categories.size_id)"),">",DB::raw("'0'"));
})
->where('products.id', $request->id)
->get();
This doesnt work, when i useproducts.name or any other column name in select statement
but when i use only group_concat inside Db::raw and nothing else, the query works.
So how do i fetch other columns?
Please help.
I am stuck on it for quite a while
The query i want is
select GROUP_CONCAT(sizes.name),`products`.`name`, `products`.`image`, `products`.`id`, `products`.`image_second`, `products`.`description`, `products`.`min_order`, `size_categories`.`size_id` from `products`
inner join `subcategories` on `products`.`subcategories_id` = `subcategories`.`id`
inner join `size_categories` on `subcategories`.`categories_id` = `size_categories`.`categories_id`
join sizes on (FIND_IN_SET(sizes.id,size_categories.size_id)>0) where `products`.`id` = '7'
Please note that the above query is working fine. I just cant make it in laravel to work. Only the group_concat part.
This is the screenshot from my database, when i dont use group_concat
Also the DISTINCT part is doing nothing there, please ignore it.
I was just trying that out
This is the migration of create_products_table
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('units_id');
$table->unsignedBigInteger('selections_id');
$table->unsignedBigInteger('subcategories_id');
$table->string('name');
$table->string('image');
$table->text('description');
$table->string('min_order');
$table->timestamps();
$table->index('units_id');
$table->index('selections_id');
$table->index('subcategories_id');
});
}
migration of create_subcategories_table
public function up()
{
Schema::create('subcategories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('categories_id');
$table->timestamps();
$table->index('categories_id');
});
}
data of size_categories table
public function up()
{
Schema::create('size_categories', function (Blueprint $table) {
$table->id();
$table->string('size_id');
$table->unsignedBigInteger('categories_id');
$table->timestamps();
$table->index('categories_id');
});
}
migration of categories table
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
migration of sizes table
public function up()
{
Schema::create('sizes', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
In sizes table data is in this form
id name
1 2m
2 3m
3 4m
First, you need to specify select columns separately. Like so:
->select(DB::raw('products.name'), DB::raw('GROUP_CONCAT(sizes.name) as sizesName'))
Next, since group concat is an aggregate column, you need to group the sizes, and product name since it's in the select list and it is not related to size.
->groupBy('size_categories.size_id', 'products.id') //edit after your comment. group by prodcuts.id to be able to select columns from products table.
So your final query should look like this:
$productDetails = DB::table('products')
->select(DB::raw('products.name'), DB::raw('GROUP_CONCAT(sizes.name) as sizesName'))
->join('subcategories', 'products.subcategories_id', '=', 'subcategories.id')
->join('size_categories', 'subcategories.categories_id', '=', 'size_categories.categories_id')
->join('sizes',function($join){
$join->on(DB::raw("FIND_IN_SET(sizes.id, size_categories.size_id)"),">",DB::raw("'0'"));
})
->where('products.id', 7)
->groupBy('size_categories.size_id', 'products.id')
->get();

Laravel eloquent build query

I want to write a query using the Laravel Eloquent structure. I want to query these values ​​after entering the min and max values. I have a product table. Column product table with discount (%) and price. I want to get min and max value according to the discounted value of the product.
I want this to be based on the query including the discount.
$products->where('price', '<=', $max)
->where('price', '>=', $min)
->orderByDesc('id')
->paginate(9);
Product migration:
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('upper_category_id')->index();
$table->unsignedBigInteger('category_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->string('name');
$table->longText('text');
$table->float('price')->index();
$table->integer('stock');
$table->float('discount');
$table->timestamps();
});
Thank you very much in advance.
If I correctly understood your question, you can try do it this way:
$products->where(DB::raw('price - price * discount'), '<=', $max)
->where(DB::raw('price - price * discount'), '>=', $min)
->orderByDesc('id')
->paginate(9);
https://laravel.com/docs/5.8/queries#raw-expressions

Only show items that are not in other table laravel

I would like to only show the schools that are not favorited.
to get the ones favorited I use:
$favorite_schools = DB::table('favorite_schools')
->select('favorite_schools.*', 'schools.name')
->leftJoin('schools', 'schools.id', 'favorite_schools.school_id')
->get();
Schools table:
Schema::create('schools', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('active');
$table->timestamps();
});
Favorite_schools table:
Schema::create('favorite_schools', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('school_id');
$table->timestamps();
});
How can I only get the schools that have not been favorited yet?
You need to go the other way. Get the schools, and left join on the favorites table, then get the results that does not have a result in the favorite_schools table.
$favorite_schools = DB::table('schools')
->select('schools.name', 'schools.id')
->leftJoin('favorite_schools', 'schools.id', 'favorite_schools.school_id')
->whereNull('favorite_schools.school_id')
->get();
Add WhereNull('favorite_schools.school_id') to the query you use to get the favorite ones. that will give you the not favorite schools.
Good luck
a slightly different approach would be to use whereNotExists
usage would be something like this:
$schools = DB::table('schools')
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('favorite_schools')
->whereRaw('favorite_schools.school_id = school.id');
})
->get();

Optimize Laravel Eloquent whereHas() query - very slow

I am using Laravel 5.4
I have 3 models: Order, OrderLine and Product.
Order hasMany() OrderLines
OrderLine hasOne() Product via the product_id in the OrderLine model (I have properly indexed this, at least I think!)
My requirement is to retrieve all Orders and OrderLines where the Product is for a certain brand name.
Here is my eloquent query. I know the query works but it seems to infinitely run when put on a large dataset (circa 10,000 Orders, 12,000 OrderLines/Products)
$orders = Order::whereBetween('order_date', [$this->start_date,$this->end_date])
->whereHas('lines', function ($q1){
$q1->whereHas('product', function ($q2){
$q2->where('brand', 'Brands>SanDisk');
});
})->with('lines')->with('lines.product')->get()->toArray();
This produces the following SQL when debugging via toSql() method.
select
*
from `orders`
where
`order_date` between ? and ?
and
exists (select * from `order_lines` where `orders`.`id` =`order_lines`.`order_id`
and
exists (select * from `products` where `order_lines`.`product_id` = `products`.`id` and `brand` = ?))
My 3 migrations to create the tables are as follows (I have removed anything except keys for simplicity):
Schema::create('orders', function (Blueprint $table) {
$table->increments('id');
});
Schema::create('order_lines', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id');
$table->integer('order_id');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
});
I then added the following index:
Schema::table('order_lines', function (Blueprint $table) {
$table->integer('product_id')->unsigned()->change();
$table->foreign('product_id')->references('id')->on('products');
});
Results of EXPLAIN syntax as follows:
1 PRIMARY orders ALL 91886 Using where
2 DEPENDENT SUBQUERY order_lines ALL 93166 Using where
3 DEPENDENT SUBQUERY products eq_ref PRIMARY PRIMARY 4 mymemory_main.order_lines.product_id 1 Using where
Try this:
mpyw/eloquent-has-by-non-dependent-subquery: Convert has() and whereHas() constraints to non-dependent subqueries.
mpyw/eloquent-has-by-join: Convert has() and whereHas() constraints to join() ones for single-result relations.
$orders = Order::query()
->whereBetween('order_date', [$this->start_date, $this->end_date])
->hasByNonDependentSubquery('lines.product', null, function ($q) {
$q->where('brand', 'Brands>SanDisk');
})
->with('lines.product')
->get()
->toArray();
That's all. Happy Eloquent Life!

represent my sql query in laravel Eloquent

I have an application tha i want it to make bookings but i'am having trouble making my Eloquent query
I have two modals "Booking.php" and "Room.php" my bookings migrations table is as follows
Schema::create('bookings', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->integer('phone');
$table->integer('time_in');
$table->integer('room_id');
$table->integer('time_out');
$table->string('days');
$table->string('type');
$table->integer('expiry_status'); /*expiry status 0->expired 1->not expired*/
$table->timestamps();
});
and my rooms migrations table is as follows
Schema::create('rooms', function(Blueprint $table)
{
$table->increments('id');
$table->integer('room_no');
$table->integer('price');
$table->timestamps();
});
in my view i have got inputs for satrt of reservation(time_in) and end of reservation(time_out) now if there is a reservation that does not end before or start after the reservation we want, the room is considered busy therefore i do not want to show that room show those rooms for my reservation dates.
My problem is I want to know the rooms available between my reservation dates,Can someone help me to write the eloquent to get the available rooms from the above table structures. I'm using mysql as the database engine. Thanks in advance.
I found this query to be useful but how to i implement it in the form of Eloquent model
SELECT r.id
FROM rooms r
WHERE r.room_id NOT IN (
SELECT b.room_id FROM bookings b
WHERE NOT (b.time_out < '2012-09-14T18:00'
OR
b.time_in > '2012-09-21T09:00'))
ORDER BY r.room_id;
You can create subselects and advanced where conditions in Laravel Eloquent.
This script worked for me. I tried to build it for your schema.
DB::table('rooms')
->whereNotIn('room_id', function($query)
{
$query->select('room_id')
->from(with(new Booking)->getTable())
->where(function($query)
{
$query->where('time_out', '<', '2012-09-14T18:00')
->orWhere('time_in', '>', '2012-09-21T09:00');
});
})
->orderBy('room_id')
->get();
https://gist.github.com/DengoPHP/d3e95441a7b9e27299b7

Categories