Laravel Eloquent order by using relation data - laravel

I have the following 2 tables:
Cars
----
id | created_at
and
Repaired
id | cars_id | updated_at
I want to display all cars, ordered by created_at desc, BUT also ordered by updated_at from the Repaired pivot table. Repaired can have many entries of specific cars_id.
So if carA is created_at 2017-02-20, and carB is created_at 2016-01-10, but the updated_at in Repaired for carB is on 2017-02-22, i want to display carB BEFORE carA, because the relation is "newer".
I also don't want do load the data from the relation table.
I tried the follwing:
$cars = $model->load(['repaired' => function($query) {
return $query->orderBy('updated_at', 'desc');
}])->orderBy('created_at', 'desc');
and
$model->join('repaired', 'repaired.cars_id', '=', 'cars.id')
->orderBy('repaired.updated_at', 'desc')
->orderBy('cars.created_at', 'desc')
->select('cars.*');
^ this query return all items from the Repaired table, even if the updated_at field is NULL. And i can't group them.

You can use the touch parent timestamps feature in Eloquent, to update the updated_at for the car, every time Repaired is updated.
In your Repaired model just add this line:
protected $touches = ['car'];
This is assuming your relation method is named car() of course.
Then you can do this:
Cars::orderBy('updated_at');
Every time a row in the Repaired table is added or updated, the car specified in the car relation will have its updated_at set to the same time. That way you can order by it.

$popular_posts = Post::withCount('favorite_to_users')
->orderBy('view_count', 'desc')
->orderBy('favorite_to_users_count', 'desc')
->take(5)->get();

Related

Count and Groupby a value of a nested relationship in Laravel Eloquent

I have 3 models that are as follows
Orders
|id, customer_id, created_at, updated_at|
Customers
|id, name, zip_code, gender, created_at, updated_at|
Genders
|id, name, created_at, updated_at|
I am working on some sort of statistical dashboard. What I would like to do is to basically group the customers based on their gender so that I can have an output as below
Male = 60,
Female = 65 etc.
The Gender table basically contains records for each gender whilst the gender column on the customers table contains the gender id from which the gender name can be determined.
I have set up all the necessary relationships in the Model and I am able to run the code below which produces me a list with the genders however, I am struggling to group and then count how many customers are which.
$orders = Order::with('customer.gender')
->get()
->groupBy(['gender.name']);
You could do it with a couple of joins.
Gender::query()
->selectRaw('count(*) as count', 'genders.name')
->join('customers as c', 'genders.id', 'c.gender')
->join('orders as o', 'o.customer', 'c.id')
->groupBy('genders.name')
->get();
The model you start from (in this case Gender) doesn't really matter. Just that you join the other two tables.
Alternatively, with your approach maybe this would work:
Gender::query()
->with(['customers' => fn($customer) => $customer->withCount('orders')])
->get()
->mapWithKeys(fn($gender) => [
$gender->name => $gender->customers->sum('orders_count')
]);
Try this:
$orders = Order::with('customer.gender')
->select(DB::raw('count(*) as total'))
->get()
->groupBy(['gender.name']);

Laravel ordering and limit results of a left join

One to many relationships, each product has many prices
table products
id | name | slug | created_at | update_at
table prices
id | product_id | title | price | link
I want to get the data limit of product limit 20 table and order created_at and each product get the lowest price, order column price. I want to use the query builder.
i tried
$products = DB::table('products')
->leftJoin('prices', 'products.id', '=', 'prices.product_id')
->select('products.id', 'products.name')->distinct()
->selectRaw('max(prices.price) as price')
->groupby('products.id')
->orderby('products.updated_at')
->get();
but it get all products and table prices order column id
You can use Eloquent Like This
public function price() {
return $this->hasMany('App\model\Prices' , 'product_id' ,'id');
}
and get like this , It will give you the mapped price only for your specific product
$product = Product::with('price')
->where(do the stuffs)
->orderBy(do the stuffs)
->get();
Or you can use the callback as well in with()

Laravel get just updated rows in date range - using WhereBetween and Where in query

I have jobs table in the database with the default columns created_at and updated_at.
I want to return the number of jobs created and the number updated.
However as the updated_at column is populated at the point of creation, I want to exclude any jobs where the updated_at value is the same as the created_at value (in order to get just the jobs that have been updated since being created).
I use the following code to get the number of updated jobs between a time range.
$jobsUpdated = Job::where('id', $id)
->whereBetween('updated_at', [$value['fromDate'], $value['tillDate']])
->count();
How can I add another where clause to exclude those results which have the same value as created_at.
In theory something like:
->andWhere('updated_at', '!=' , 'created_at')
But andWhere does not exist, so this is just to show my thinking.
Thanks!
$jobsUpdated = Job::where('id', $id)
->whereBetween('updated_at', [$value['fromDate'], $value['tillDate']])
->whereColumn('updated_at', '!=' , 'created_at')
->count();
to compare column value use whereColumn() function referenced by official doc

laravel - Updating row changes created_at column

When I update my row with the query below, it changes not only the updated_at column for the row, but also the created_at column. Why? How can I prevent this so that it only changes the updated_at column
Post::where('id', Input::get('post_id'))
->where('user_id', getUserID())
->update(array('message' => Input::get('message')));
From Laravel documentation at
Insert-update-delete
You can use following method to perform update
$x = Post::find(Input::get('post_id')); // may need to use intval()
$x->message = Input::get('message');
$x->save();
This should update the record with only updating 'updated_at' timestamp.

Laravel - Get an user who has specific relationships

I have a user with countries relationship. (hasMany)
user_id 1
country_id 1
user_id 1
country_id 2
...
What I want is to get those users who have both countries (country 1 and country 2) How can I do this? I'm reading http://laravel.com/docs/eloquent#querying-relations but I'm not very sure what to do.
edit: almost the solution
$users = \User::whereHas('countries', function($query) {
$countries_ids = [1, 2, 4];
$query->whereIn('users_countries.country_id', $countries);
})->get();
This will work as long as there are no duplicates in the pivot table (unique index for pair of fk keys for example):
$countries = [1,5,9];
User::whereHas('countries', function ($q) use ($countries) {
$q->whereIn('users_countries.country_id', $countries);
}, '=', count($countries) )->get();
In case there are duplicates, like:
user_id | country_id
1 | 1
1 | 2
1 | 1
Then you would need raw count(distinct ...) so you couldn't use whereHas, so I suggest this, which will be much faster:
$countries = [1,5,9];
User::join('users_countries as uc','uc.user_id', '=', 'users.id')
->whereIn('uc.country_id', $countries)
->having(DB::raw('count(distinct uc.country_id)'), '=', count($countries))
->groupBy('users.id')
->get(['users.*']);
You can do it the other way. Make the query on the country model. Add a function in the country model
public function user(){
return $this->belongsTo('user');//or if there is no user_id column in the countries table, use the ('model','local_key','foreign_key') pattern.
}
Then create an array with the country_ids, and que the country model.
$country_ids=array(4,67);
$countries=Country::where('id',$country_ids[0])->where('id',$country_ids[1])->get();
In order to get the users you should loop through like this
foreach($countries->user as $user)

Resources