Laravel 4.2 Sort by Eloquent Relationship Problems - laravel

I'm working with a datatable that I'm outputting from various relationships.
Most of the data comes from a meters table that has a Meter model, but some of it is pulled from other tables via relationships. For instance, I'm having an issue with sorting by the calibrations table.
The datatable has sortable columns that work just fine. The columns that sort based on other relationships have the joins in place so that they sort without any query errors.
All the sorting and joins work except for one, last_calibration_date.
There is no column called last_calibration_date. In fact, each meter could have multiple calibrations.
In the Meter model I grab the last_calibration_date from the calibrations table via the calibration_date column this way:
public function getLastCalibrationDateAttribute()
{
if (isset($this->relations['calibrations']) && $this->relations['calibrations']->count())
return $this->relations['calibrations']->sortBy('calibration_date', SORT_REGULAR, true)->first()->calibration_date->format('Y-m-d');
}
This works superbly when I'm not sorting by the last_calibration_date column, but returns a sql error if you try to sort by it without a join.
Here's my attempt at the join:
if ($sort == 'last_calibration_date')
{
$query->join('calibrations', 'calibrations.meter_id', '=', 'meters.id');
$sort = 'calibrations.calibration_date';
}
While this doesn't return an error it also doesn't return the actual last_calibration_date.
Just a little more info, the calibrations table is set up like so
calibrations
- id
- calibration_date
- next_calibration_date
- meter_id
So, as was said previously, any meter may have multiple calibrations.
Any ideas on how I could replicate my Meter method in my join? Or maybe another way of sorting by last_calibration_date?

Alrighty, well, I seem to have solved my problem without quite understanding why.
if ($sort == 'last_calibration_date')
{
$query->select('meters.*');
$query->join('calibrations as calibration', 'calibration.meter_id', '=', 'meters.id');
$sort = 'calibration.calibration_date';
}
Adding that $query->select('meters.*'); has solved it. Again, not sure why. My understanding is that is selecting a particular table's columns, not a model's relationships.
Anyways, it's working now.

Related

Livewire and Eloquent - Sums of multiple columns in filtered collection

quite a noob in Laravel and Livewire, I guess if there is a better way to accomplish what I have here:
I have some checkboxes in a Livewire view that allow the user to dynamically choose how to filter the data being shown in a HTML table.
On the controller I have an eloquent query that retrieves a subset of the records in the db whenever a checkbox changes state.
Like this (I tried to simplify the structure):
$filtered_items = Item::select('item_color', 'item_quantity', 'item_value')
->whereIn('item_color', [ /* array controlled by checkboxes values in view */ ])
->get();
I need to also show the sums of some columns for that specific filter setup.
As I don't want to make new queries, I try to work on the already available collection with a ->sum() for each column I want to sum:
$sums['item_quantity'] = $filtered_items->sum('item_quantity');
$sums['item_value'] = $filtered_items->sum('item_value');
It works as intended, as I have an array with the sums, but I was wondering if there is a better way to do this, or even write a function to get more columns sum at once, maybe passing an array.
Thanks for any idea or link!
Here is an example of a single query for all sums (two in this example)
$filtered_items = \DB::table('items')
->selectRaw('SUM(item_quantity) as sum_quantity, SUM(item_value) as sum_value')
->whereIn('item_color', [ /* array controlled by checkboxes values in view */ ])
->first();
The advantage is that it will return one entry/row instead of a collection of all the items which is way faster.

Using Laravel Eloquent to count how many times something exists in an efficient manner

I have a table called rentals, within each row are columns state,city,zipcode which all house ids to another table with that info. There are about 3400 rentals. I am pulling each column to display the states,city and zipcode distinctly. I need to show how many rentals are in each one. I am doing this now via ajax, the person starts typing in what they want to see and it auto completes it with the count, but its slow because of the way im doing it.
$rentals_count = Rentals::where('published',1)->get();
foreach($states as $state) {
echo $state.”-“.$rentals_count->where(‘state’,$state->id)->count();
}
Above is roughly what im doing with pieces removed because they are not related to this question. Is there a better way to do this? It lags a bit so the auto complete seems broken to a new user.
Have you considered Eager loading your eloquent query? Eager loading is used to reduce query operations. When querying, you may specify which relationships should be eager loaded using the with method:
$rental_counts = Rentals::where('published',1)->with('your_relation')->get();
You can read more about that in Laravel Documentation
$rentals = Rentals::wherePublished(true)->withCount('state')->get();
When you loop through $rentals, the result will be in $rental->state_count
Setup a relation 'state' on rentals then call it like this
$rentals_count = Rentals::where('published',1)->with('state')->get()->groupBy('state');
$rentals_count->map(function($v, $k){
echo $v[0]->state->name .' - '. $v->count();
});
Meanwhile in Rentals Model
public function state(){
return $this->hasOne(State::class, 'state'); //state being your foreign key on rentals table. The primary key has to be id on your states table
}

Using Laravel 5, how do I get all() and order by belongsToMany relationship by the pivot table

I have Deals and Faq's. I have functional relationships working and I can reference $deal->faqs() and it returns the right faqs.
The problem I am trying to solve comes up as I administer the faqs related to a deal. In my Deal admin view (new / edit) I am getting all the Faq's.
$faqs = \App\Faq::all();
This works great, and I am even able to check if an faq is related to a deal through my checkbox: in the view:
{!! Form::checkbox('faqlist[]', $faq->id, $deal->faqs->contains($faq->id) ? true : false) !!}
So now we have a list of all the faqs and the correct ones are checked.
I have setup an order column on the pivot table (deal_faq). That table consists of:
deal_id
faq_id
timestamps
order
In my form, I have a drag and drop ordering solution (js) built and working. By working I mean, I can drag/drop and a hidden field value is updated to reflect the correct order.
When creating a deal, this is no problem. Get all the faq's, check a few to associate, drag to order, then save.
When editing a deal, I need to load based on the order column in the deal_faq table. This is my issue.
I have tried a few things but always get an error. An example of what I have tried is:
$faqs = \App\Faq::orderBy('deal_faq.order', 'asc')->get();
This returns an error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'deal_faq.order' in 'order clause' (SQL: select * from `faq` order by `deal_faq`.`order` asc)
I think the issue is that I am trying to get all, but order by a field that only exists for the related faqs since the order field is on the deal_faq. Just not sure how to solve.
In essence you need to join the pivot table and then apply the order
$faqs = \App\Faq::join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc')
->get();
You may need to adjust table and column names to match your schema.
Now you can extract this logic into a scope of the Faq model
class Faq extends Model
{
...
public function scopeOrdered($query)
{
return $query->join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc');
}
}
and then use it like this
$faqs = \App\Faq::ordered()->get();
UPDATE:
This works to get the FAQ's in order but it only get the ones that
have been associated. There will be FAQ's that are not associated and
thus not ordered.
In this case you just need to use an outer join - LEFT JOIN. The scope definition would then look like this
public function scopeOrdered($query)
{
return $query->join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id', 'left')
->orderBy('deal_faq.order', 'asc');
}
or a bit more expressively
public function scopeOrdered($query)
{
return $query->leftJoin('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc');
}
Please consider adding a secondary order (i.e. by id, or any other field(s) in faqs table that make sense). This way you'd have a deterministically ordered resultsets each time regardless of whether you have an explicit order defined in a pivot table or not.

How do I define relation between users and points in Eloquent ORM

I have 3 tables:
Users - for storing users
User_point - for associacion between users and points(has only user_id and point_id)
Points for description of points(id, amount, description)
How do I define a relation between these? I tried
public function points(){
return $this->belongsToMany('\App\Point', 'user_point');
}
but when I do
return $user->points()->sum('amount');
it returns just one
Edit:
At first I tried making it like this as it makes more sense:
public function points(){
return $this->hasMany('\App\Point');
}
But it wouldn't work
SUM is an aggregate function and so it should only return one row.
$user->points will be a collection of points attached to that user.
$user->points() is a query that you can do additional work against (i.e. $user->points()->whereSomething(true)->get()).
As user ceejayoz pointed out, using user->points() is going to return a builder which you can do additional work on. I believe using sum() on that will look at the first row returned which is what you indicated is actually happening.
Likely, what you really want to do is $user->points->sum('amount');
That will get the sum of that column for the entire collection.

return separate result for each relation in many to many

Hi i have a many to many relationship with the following structure:
services
apps
service_app
I would like to have an eloquent query to return a separate result for each relationship(basically the pivot table). I have the following :
$all = App::with('services')->get();
this will return an app with nested services, I would like to have this return a separate result for each app-service combination along with data from the pivot table. how is this possible using eloquent?
It's a bit strange, but it can easily be done if you don't think of the pivot table as a pivot table, but as an AppService.
So what you can do is create a model for it, probably named AppService. In that model, you would then have 2 belongsTo() relationships. One for App and one for Service.
Then you can query your pivot table directly and use those relationships to get what you need.
$appServices = AppService::all();
foreach($appServices as $appService) {
echo $appService->app->description;
echo $appService->service->description;
}

Resources