Sorting data order by duration between start_date and end_date - laravel

I'm using Laravel 7. I have a table named assistances witch has some columns and those two columns:
- start_date
- end_date
I'd like to sort data order by the duration between start_date and end_date:
$query = Assistance::join('users', 'assistances.user_id', '=', 'users.id')
->select('assistances.*')
->orderBy('duration between start_date and end_date');
;
How can I do this ?

Please try the following - bare in mind that this is not hugely efficient
DATEDIFF returns the days between two dates. You may also want to use TIMESTAMPDIFF which accepts a first parameter such as 'hour' where you can get more granular control
$query = Assistance::join('users', 'assistances.user_id', '=', 'users.id')
->select('assistances.*')
->orderBy(DB::raw('DATEDIFF(start_date, end_date)'))
->get();
Please also use DB; in your class.

Related

Convert DB::Select to Query Builder

i has raw query in laravel like this
public function getPopularBook(){
$book = DB::select("
with totalReview as(
SELECT r.book_id , count(r.id)
FROM review r
GROUP BY r.book_id
)
SELECT *
from totalReview x
left JOIN (
SELECT b.*,
case when ((now() >= d.discount_start_date and now() <= d.discount_end_date) or (now() >= d.discount_start_date and d.discount_end_date is null)) then (b.book_price-d.discount_price)
ELSE b.book_price
end as final_price
FROM discount d
right JOIN book b
on d.book_id = b.id
) as y
on x.book_id = y.id
ORDER BY x.count DESC, y.final_price ASC
LIMIT 8"
);
return $book;
}
so when i want to return a paginate, it doesn't work so can i convert this to query build to use paginate
This is a very un-optimized raw query in itself. You are performing too many Join in Subquery just to sort by price
i'm assuming the database table:
books[ id, name, price ]
reviews[ id, book_id ]
discounts[ id, book_id, start_date, end_date, discount_price]
Look how easy it is if you just use Eloquent:
Book::withCount('reviews')->orderBy('reviews_count')->get();
this will give you all the Books order by number of reviews
now with the final price, this can be a bit tricky, let's take a look at a query when we don't consider discount time
Book::withCount('reviews')
->withSum('discounts', 'discount_price') //i'm assuming a book can have many discount at the same time, so i just sum them all
->addSelect(
DB::raw('final_price AS (books.price - discounts_sum_discount_price)')
)
->orderBy('reviews_count', 'asc') // =you can specify ascending or descending
->orderBy('final_price', 'desc') //in laravel chaining multiple orderBy to order multiple column
->get();
I dont even need to use Subquery!! But how do we actually only add the "active" discount?, just need to modify the withSum a bit:
Book::withCount('reviews')
->withSum(
[
'discounts' => function($query) {
$query->where('start_date', '<=', Carbon::now())
->where('end_date', '>=', Carbon::now())
}
],
'discount_price'
)
->addSelect(
DB::raw('final_price AS (books.price - discounts_sum_discount_price)')
)
->orderBy('reviews_count', 'asc') // =you can specify ascending or descending
->orderBy('final_price', 'desc') //in laravel chaining multiple orderBy to order multiple column
->get();
and it is done
What about pagination? just replace the get() method with paginate():
Book::withCount('reviews')
->withSum(['discounts' => function($query) {
$query->where('start_date', '<=', Carbon::now())->where('end_date', '>=', Carbon::now())
}],'discount_price')
->addSelect(DB::raw('final_price AS (books.price - discounts_sum_discount_price)')) //just format to be a bit cleaner, nothing had changed
->orderBy('reviews_count', 'asc')
->orderBy('final_price', 'desc')
->paginate(10); //10 books per page
WARNING: this is written with ELoquent ORM, not QueryBuilder, so you must define your relationship first

How to get the default price of the product with different prices from different date periods from the period closest to today?

my sqlfiddle eample
Hello there,
according to the above sqlfiddle example;
I have a table A where the products are listed and a table B with different prices for different periods associated with these products.
Here I show these prices according to the date the user has chosen. There is no problem.
However, if the user has not selected a date, I cannot show the price of the period closest to today by default.
In the example I gave, the sql query does this successfully, but I cannot write it successfully in the form of laravel query. Or as an Eloquent orm query
How can I do that?
$query->select(['tableA.*', 'tableB.start_date', 'tableB.end_date', 'tableB.price'])
->join('tableB', function($join) {
$join->on('tableA.id', '=', 'tableB.pro_id');
})->where(function($sq) use ($postFrom) {
$sq->when($postFrom[0]=='0', function ($syq) {
$syq->whereRaw('DAYOFYEAR(curdate()) <= DAYOFYEAR(tableB.end_date)');
}, function ($stq) use ($postFrom) {
$stq->whereDate('tableB.start_date', '<=', $postFrom[0])
->whereDate('tableB.end_date', '>=', $postFrom[0]);
});
})->orWhere(function($ssq) use ($postTo) {
$ssq->whereDate('tableB.start_date', '<=', $postTo[0])
->whereDate('tableB.end_date', '>=', $postTo[0]);
})->groupBy('tableA.id')->orderBy('tableB.price', $sortDirection);
note1: $postFrom and $postTo are the start and end dates from the user. If the user did not submit a date, $postFrom is displayed as 0.
note2: I show the default price when the $postFrom[0] == '0' condition is met.
note3: The '2021-03-07' value in the sqlfiddle example is used for example instead of the dynamic present value.
note4: According to this query, it takes the price value of the first period as default. But that's not what I want.
note5: I can't use 'joinSub' because Laravel version is 5.5.
note6:In the example I want to convert to Laravel Query form, the sql query that works without any problems:
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
right join(
SELECT id, start_date, end_date, pro_id, price, DATEDIFF(`tableB`.`end_date`, '2021-03-07') diff
FROM `tableB` GROUP BY id order by diff asc
) `tableB` on `tableA`.`id` = `tableB`.`pro_id` where (date(`end_date`) >= '2021-03-07')
group by `tableA`.`id` order by `price` desc
This is an equivalent query of your query. I haven't executed.
If Laravel Version is greater then 5.5
$query1 = DB::table('tableB')
->selectRaw("id, start_date, end_date, pro_id, price, DATEDIFF(end_date, '2021-03-07') AS diff")
->groupBy('id')->orderBy('diff','ASC');
TableA::select('tableA.*', 'tableB.start_date', 'tableB.end_date', 'tableB.price')
->joinSub($query1, 'tableB', function ($join)
{
$join->on('tableA.id', '=', 'tableB.pro_id');
})
->whereDate('tableB.end_date','>=','2021-03-07')
->groupBy('tableA.id')->orderBy('price','DESC')->get();
For Laravel 5.5
TableA::select('tableA.*', 'tableB.start_date', 'tableB.end_date', 'tableB.price')
->join(DB::raw("(SELECT id, start_date, end_date, pro_id, price,
DATEDIFF(`tableB`.`end_date`, '2021-03-07') diff
FROM `tableB` GROUP BY id order by diff asc) table2 "), function ($join)
{
$join->on('tableA.id', '=', 'table2.pro_id');
})
->whereDate('table2.end_date','>=','2021-03-07')
->groupBy('tableA.id')->orderBy('price','DESC')->get();

Need guidance on how to build a Laravel database query

In Laravel 6.18 I'm trying to figure out how to recreate the following Postgres query.
with data as (
select date_trunc('month', purchase_date) as x_month, date_trunc('year', purchase_date) AS x_year,
sum (retail_value) AS "retail_value_sum"
from coins
where user_email = 'user#email.com' and sold = 0
group by x_month, x_year
order by x_month asc, x_year asc
)
select x_month, x_year, sum (retail_value_sum) over (order by x_month asc, x_year asc rows between unbounded preceding and current row)
from data
I know how to build the main part of the query
$value_of_all_purchases_not_sold = DB::table('coins')
->select(DB::raw('date_trunc(\'month\', purchase_date) AS x_month, date_trunc(\'year\', purchase_date) AS x_year, sum(retail_value) as purchase_price_sum'))
->where('user_email', '=', auth()->user()->email)
->where('sold', '=', 0)
->groupBy('x_month', 'x_year')
->orderBy('x_month', 'asc')
->orderBy('x_year', 'asc')
->get();
but how do you build out the with data as ( and the second select?
I need the data to be cumulative and I'd rather do the calculation in the DB than in PHP.
Laravel doesn't have built-in method(s) for common table expression. You may use a third party package such as this - it has a very good documentation. If you don't want to use an external library, then you need use query builder's select method with bindings such as
$results = DB::select('your-query', ['your', 'bindings']);
return Coin::hydrate($results); // if you want them as collection of Coin instance.

query created_at date only equals to carbon::now() date only in laravel

How to do you query created_at date only equals to Carbon::now() date only in laravel query builder.?
App\Table::where('created_at', '=' ,Carbon/Carbon:now() )->get();
You can opt for the following kind of construct:
App\Table::whereDate('created_at', '=', now()->toDateString())->get();
You can use Mysql's default CURDATE function with laravel query builder's Raw sql.
DB::table('posts')->select(DB::raw('*'))
->whereRaw('Date(created_at) = CURDATE()')->get();
Or
App\Table::where('created_at', '=', Carbon::today())
No Need to use anything just check whereDate For Date
App\Table::whereDate('created_at', '=' ,Carbon/Carbon:now() )->get();
among this whereMonth for Month and whereDay for date
check this link https://laravel.com/docs/5.8/queries#whereDate%20/%20whereMonth%20/%20whereDay%20/%20whereYear%20/%20whereTime

Laravel join two tables with where that compares two date time columns

I have two tables that I want to run joint query on using the value of two date time columns, I have products table and sync_status tables, I want to return all products with updated_at date time greater than synced_at date time.
DB::table('products')
->join('sync_statuses', function ($join) {
$join->on('products.product_number', '=', 'sync_statuses.product_number')
->where('products.updated_at', '>', 'sync_statuses.synced_at')
->whereNull('products.deleted_at');
})
->select('products.product_number');
This SQL represents what I am trying to achieve using Eloquent:
SELECT products.product_number
FROM products
JOIN push_statuses
ON products.product_number = statuses.product_number
AND (
statuses.synced_at IS NULL
OR products.updated_at > statuses.synced_at
)
You have to use on() instead of where() to compare columns:
->on('products.updated_at', '>', 'sync_statuses.synced_at')
This worked for me:
DB::table('products')
->join('statuses', function ($join) {
$join->on('products.product_number', '=', 'statuses.product_number')
->where(DB::raw('products.updated_at'), '>=', DB::raw('statuses.synced_at'))
->whereNull('products.deleted_at');
})->select('products.product_number');
I needed to use DB::raw('products.updated_at') to reference each date time column in the where() clause.

Resources