How to add a momentary value to a sql query in Laravel - laravel

I have this query in Laravel 8:
$articles = Article::whereIn('category_id', $categories)->where(function ($query) {
$query->where(function ($query2) {
$query2->where('draft', 0)->whereNull('publication_date');
})->orWhere(function ($query2) {
$query2->where('draft', 0)->where('publication_date', '<=', DateHelper::currentDateTime());
});
})->orderBy('publicated_at', 'DESC')->get();
I need to add a momentary id to this query named desc_id, so that the values $articles that i query have a desc_id based on the publication_date.
The result that i need to achieve is like this:
id
name
category
publication_date
desc_id
3
bananas
news
2022-01-16 17:30:00
1
27
kiwis
news
2021-12-05 21:30:00
3
50
apples
news
2022-01-07 09:14:00
2
I've tried adding it by passing all the elements of $articles into an empty array adding a value:
$count =0;
$allArticles = [];
foreach ($articles as $article) {
$count++;
$allArticles[] = $article->with('desc_id', $count);
}
I know that my method is incorrect but i hope that it helps understanding my question.

It looks like you want to have the line number of each row which can be achieve like this :
Article::whereIn(...)->select()
->addSelect(DB::raw('RANK() OVER(ORDER BY publicated_at DESC) as desc_id'))
->orderBy('publicated_at', 'DESC')
->get();

Related

check count in one to many relation in laravel

I have products table that each of the product has many orders,product.php
public function orders()
{
return $this->hasMany(Order::class,'product_id');
}
I can get products that order by order_count with this code:
$products = Product::withCount('orders')->orderBy('orders_count', 'desc')->get();
Now I want to get products in controller that their orders count is bigger than 5,
How I can do it?
One way to do this would to use whereHas():
$products = Product::withCount('orders')
->whereHas('orders', null, '>', 5)
->orderBy('orders_count', 'desc')
->get();
I don't think it's mentioned in the docs but you don't have to pass a closure as the 2nd param, and the 3rd and 4th can be used to pass the operator and the count.
Alternatively, you could use having() instead:
$products = Product::withCount('orders')
->having('orders_count', '>', 5)
->orderBy('orders_count', 'desc')
->get();
You can use mapping on your collection to filter out records e.g. :`
$collection = collect($products)->map(function ($product) {
return $product->orders->count() > 5;
})
->reject(function ($name) {
return $product->orders->count() < 5;
});`
This is just an example, you can put more conditions if required.
Hope this helps.
The below code should give you just the orders with a count greater than 5
$products = Product::withCount('orders')->where('orders_count', '>', 5)->orderBy('orders_count', 'desc')->get();
Adding ->where() should do it.
Hope that helps.
Update
Try
->whereRaw('count("orders.id") > 5')
This should work to get products with more than 5 orders.

Laravel Where Count > N

I have 2 models in my app:
1. Customer.php
2. Car.php
Now I would like to run a query that returns all customers that have less than 2 cars. Where 2 is a number that can be changed by the user.
I have tried this but it didn't work, it just returns all customer records:
$customers = Customer::whereHas("cars", function($query) {
$query->selectRaw("count(*) < ?", [2]);
})
->get();
Edit:
The two models are linked in a pivot table, meaning A customer can have more than 1 car and a Car can belong to more than 1 customer.
Use this:
$customers = Customer::withCount('cars')
->having('cars_count', '<', 2)
->get();
So , here is the result.
Relation in model Customer.php
public function cars()
{
return $this->belongsToMany('App\Car','car_customer','car_id','customer_id');
}
Query to get all customers with N cars:
$userInput = 2;
$data = Customer::with('cars')
->withCount('cars')
->has('cars', '<', $userInput)
->orderBy('cars_count', 'desc')
->get();
Where the $userInput is your 'N'.
This is the best way:
$customers = Customer::has('cars','<', 2)->get();
Have you tried this approach?
$input = 2;
$customers = Customer::whereHas("cars", function($query) use ($input) {
$query->where(DB::raw("count(cars.id)"), "<", DB::raw($input))
})->get();

How to sorting by current login user first in Laravel?

Below code is to divide list view by permission in the Job table.
public function index(Request $request)
{
$user_name = Auth::user()->name;
$jobs = Job::orderBy('created_at', 'desc') //#default
->where('is_trash', '==', 0)
->paginate(15);
if(!Auth::user()->hasPermissionTo('Admin')){ //check by permission
$jobs = Job::orderBy('created_at', 'desc')
->where('user_name', '=', $user_name)
->where('is_trash', '==', 0)
->orderby('updated_at', 'desc')
->orderBy('deleted_at', 'desc')
->paginate(15);
}
return view('jobs.index')
->with('jobs', $jobs);
}
For example, current login user name is C and there has A, B, D users.
Then I wish to show list as
------------------------------
C
A
B
D
------------------------------
How do I sort the currently logged in users first in the list?
Thank you in advance.:)
To place a row above all other rows and then order the remaining rows in ascending / descending order you need to do something like this SELECT * FROM table ORDER BY field = value ASC
So for your query something like this should work
$user_name = Auth::user()->name;
$jobs = Job::orderByRaw("user_name = ':name' ASC", ['name' => $user_name])
->orderBy('created_at', 'desc')
->where('is_trash', '==', 0)
->paginate(15);
I'm not entirely sure which query you wish to order and by which field but this example should give you enough to adapt on. It is untested but if :name isn't working as per the example try wrapping the query in \DB::raw() to add the binding $user_name.
You could use collections for that. Just tested, works perfectly:
list($user, $collection) = $collection->partition(function($user) {
return $user->id === auth()->id();
});
$collection = $user->merge($collection);

laravel eloquent query group by last id

Hi I'm trying to get the last register in DB for each equipment
I got like
id | id_equip
-----+----------
1 | 3
2 | 3
3 | 3
4 | 2
5 | 2
I want to query like the last id of each equip like:
id | id_equip
-----+----------
3 | 3
5 | 2
I tried this:
$calibracao = $this->calibracao->orderBy('id', 'DESC')->groupBy('id_equip')->get();
thanks for the help!
A simple way, without join, using max:
$query = DB::table('equip')
->select(DB::raw('*, max(id) as id'))
->groupBy('id_equip')
->orderBy('id', 'asc')
->get();
(Assuming, of course, that you can count on your id to be in date order.)
You can also do this with Eloquent, using a self-referencing one-to-one relationship. The query takes longer, but it's much more Laravel-like:
Define the relationship in your model:
class myModel extends Eloquent {
public function latestEquipment()
{
return $this->hasOne('myModel', 'id_equip', 'id_equip')->latest();
}
}
(Note that in this case, we're using latest() so that we don't rely on the order of ids to determine the most recent entry, but rather on the created_at date of each record. This is more accurate.)
To retrieve all the records, with their latest equipment entry:
$latest = myModel::with('latestEquipment')->groupBy('id_equip')->get();
// loop over the results
foreach ($latest as $l) {
if ($l->equipment) {
echo('ID: ' . $l->id . 'ID_EQUIP: ' . $l->id->id_equip . '<br>');
}
}
I'm not sure if there's an easier way and this is kind of convoluted because of the join, but it should give the result you want:
DB::table('equip as e')
->select('e.*')
->join(DB::raw("(SELECT id_equip, MAX(id) id FROM equip GROUP BY id_equip) as _e"), function ($join) {
$join->on('e.id', '=', '_e.id')->on('e.id_equip', '=', '_e.id_equip');
})
->orderBy('id', 'asc')
->get();
public function latest($equipamentos)
{
foreach ($equipamentos as $equip)
{
$latest[$equip->cod] = DB::table('tb_calibracao')
->where('cod_equipamento', $equip->cod)
->where('parecer', '1')
->orderBy('cod', 'Desc')
->first();
}
return $latest;
}
That worked, thx for the help!

Laravel how do I get the row number of an object using Eloquent?

I'd like to know the position of a user based on its creation date. How do I do that using Eloquent?
I'd like to be able to do something like this:
User::getRowNumber($user_obj);
I suppose you want MySQL solution, so you can do this:
DB::statement(DB::raw('set #row:=0'));
User::selectRaw('*, #row:=#row+1 as row')->get();
// returns all users with ordinal 'row'
So you could implement something like this:
public function scopeWithRowNumber($query, $column = 'created_at', $order = 'asc')
{
DB::statement(DB::raw('set #row=0'));
$sub = static::selectRaw('*, #row:=#row+1 as row')
->orderBy($column, $order)->toSql();
$query->remember(1)->from(DB::raw("({$sub}) as sub"));
}
public function getRowNumber($column = 'created_at', $order = 'asc')
{
$order = ($order == 'asc') ? 'asc' : 'desc';
$key = "userRow.{$this->id}.{$column}.{$order}";
if (Cache::get($key)) return Cache::get($key);
$row = $this->withRowNumber($column, $order)
->where($column, '<=',$this->$column)
->whereId($this->id)->pluck('row');
Cache::put($key, $row);
return $row;
}
This needs to select all the rows from the table till the one you are looking for is found, then selects only that particular row number.
It will let you do this:
$user = User::find(15);
$user->getRowNumber(); // as default ordered by created_at ascending
$user->getRowNumber('username'); // check order for another column
$user->getRowNumber('updated_at', 'desc'); // different combination of column and order
// and utilizing the scope:
User::withRowNumber()->take(20)->get(); // returns collection with additional property 'row' for each user
As this scope requires raw statement setting #row to 0 everytime, we use caching for 1 minute to avoid unnecessary queries.
$query = \DB::table(\DB::raw('Products, (SELECT #row := 0) r'));
$query = $query->select(
\DB::raw('#row := #row + 1 AS SrNo'),
'ProductID',
'ProductName',
'Description',
\DB::raw('IFNULL(ProductImage,"") AS ProductImage')
);
// where clauses
if(...){
$query = $query->where('ProductID', ...));
}
// orderby clauses
// ...
// $query = $query->orderBy('..','DESC');
// count clause
$TotalRecordCount = $query->count();
$results = $query
->take(...)
->skip(...)
->get();
I believe you could use Raw Expresssions to achieve this:
$users = DB::table('users')
->select(DB::raw('ROW_NUMBER() OVER(ORDER BY ID DESC) AS Row, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
However, looking trough the source code looks like you could achieve the same when using SQLServer and offset. The sources indicates that if you something like the following:
$users = DB::table('users')->skip(10)->take(5)->get();
The generated SQL query will include the row_number over statement.
[For Postgres]
In your model
public function scopeWithRowNumber($query, $column = 'id', $order = 'asc'){
$sub = static::selectRaw('*, row_number() OVER () as row_number')
->orderBy($column, $order)
->toSql();
$query->from(DB::raw("({$sub}) as sub"));
}
In your controller
$user = User::withRowNumber()->get();

Resources