How to use append attributes with Laravel Eloquent and pagination - php

How can I use append attributes with Laravel Eloquent and pagination? We are trying to load the data from Laravel Eloquent, wherein we have to append attributes. So we want to query based on the append attributes, which works fine when not using skip and take. But we need to add the pagination and need the option of skip and take, but return an error.
Model
protected $appends = ['isSlaBreach'];
public function getIsSlaBreachAttribute()
{
return true or false
}
Controller
$overdue_tickets->skip($skip);
$overdue_tickets->take($take);
$res = $overdue_tickets->get()->where('isSlaBreach', true);
Need guidance on the same.

but which error are you getting?
Note that querying over appended attributes it is not possible using directly the db: you are able to query over appended attributes by using Eloquent Collections.
I think in your code there are some problems:
1 - in the Model return true or false returns always true
2 - In the controller you should add a final all() in order to get filtered elements from the collection:
$res = $overdue_tickets->get()->where('isSlaBreach',true)->all();

Related

Why is it not possible to use groupBy() in an eager loading query - Laravel

ErrorException:
stripos() expects parameter 1 to be string, object given
For the groupBy() call in the with() method
$user = User::with([
'pricelists' => function($query) {
$query->groupBy(function($var) {
return Carbon::parse($var->pivot->created_at)->format('m');
});
}
])->where('id', $id)->get();
I already saw a few posts talking about how to manage this problem and that it shall not be possible to use groupBy() in eloquent but I do not really understand why...
To be clear:
User and Pricelist model got a many-to-many relationship with the default timestamps() method. I am trying to get the downloaded pricelists grouped by their months they were downloaded from the current user.
After a few attempts I just deleted the above shown => function($query... statement from the with() method and just left the with(['pricelist']) to fetch all datasets and tried this:
$user->pricelists = $user->pricelists->groupBy(function($var) {
return Carbon::parse($var->pivot->created_at)->format('m');
});
return $user->pricelists;
And it works fine and returns an array with multiple arrays for each month... But returning it like this:
return $user;
returns just 1 array with all entries... I do not really get the sense behind it right now...
The two groupBy() method that you are using in the two code you provide are totally different methods.
The first groupBy() where you use it in the callback is actually being called by $query which is a query builder object. The groupBy() here is used to add SQL GROUP BY Statement into the query. And as per the documentation, it only take string variables as parameter.
The groupBy() in your second code is being called by $user->pricelists which is a laravel eloquent collection. The groupBy() method here is actually from the base collection class and is used to group the items inside the collection into multiple collections under the different key defined by the parameter passed to the function. Please read the documentation here.
For your case, the second groupBy() is the one you should be using since you plan to use a callback and will allow you to use more complicated logic.

Difference between laravel value() function and direct model access

I'm doing an Eloquent query in my Profession model, i ran an where query, retrieve the first value, then caught only the value in "id" column with ->value('id')
//This always return 1
$profession = Profession::where('name', $profession)->first()->value('id');
//This return the right value
$profession = Profession::where('name', $profession)->first()->id;
Does value() function depends on some other aspect of the query/configuration? Couldn't find anything in Laravel query builder documentation.
Profession::where('name', $profession)->first() returns a model. You cannot call value on a model. However, you may call it on a query builder as explained in the docs?
So instead of calling
$profession_id = Profession::where('name', $profession)->first()->id;
you could achive the same thing with
$profession_id = Profession::where('name', $profession)->value('id');
There is a helper function in laravel with the name value . It will always returns that value it is given for example value(true) is true
But for eloquent if you want to access the fields either you use pluck function like ->pluck(id) it will give you ids only

Overwrite paginator items in Laravel

I have a list of ids that come from a complicated query. I paginate the response of that complicated query and then use those ids to get the eloquent models. I then put it through a resource with the pagination meta data.
The laravel AbstractPaginator class protects the items attribute so you cannot easily overwrite them. I have a solution to use a ReflectionProperty but I'm after a simpler solution.
The below works but it is not particularly elegant.
// $studentIds == Long complicated query that would return 1000s of students
$data = $studentIds->paginate(); // Execute the query limited to 15.
// Use ids to get eloquent models for our students
$students = Student::whereIn('id', $data->pluck('id'))->get();
// Overwrite paginate `items` attribute so that our response contains pagination meta.
$rp = new \ReflectionProperty('Illuminate\Pagination\AbstractPaginator', 'items');
$rp->setAccessible(true);
$rp->setValue($data, $students);
return new StudentResourceCollection($data);
Use the setCollection() method:
$data->setCollection($students);

1 to 1 inverse relationship "belongsTo" giving a collection laravel instead of model

In my Profile model I setted this relationship
public function lease()
{
return $this->belongsTo(Lease::class, 'lease_id', 'id');
}
And in my Lease model I seeted this way
public function profile()
{
return $this->hasOne(Profile::class, 'lease_id', id);
}
As longs as I know in laravel you could do
$profile = factory(App\Profile::class)->create();
$profile->lease()->get();
And then responds correctly with the model inside of a collection
And if I do
$profile->lease
Responds correctly directly with the model
It isn't supposed that dynamic propertis execute the query right away like a shortcut of ->lease()->get()? Why it gives different formatted results?
When you are calling get on a builder you are getting a collection always. When you call first on a builder like that you will get a model or null. The dynamic property for the relationship, based upon the relationship object, will either query with get or first respectively when it loads it. Which is why $model->relationship is returning you the result you expect.
The relationships that are singular, cause a find and the ones that are many cause a get.
Laravel 5.4 - Docs - Eloquent - Relations - Relationship Methods vs Dynamic Properties

Excluding Laravel appends values from model results

Laravel has the option to add an $appends array to each model making additional values automatically available as if they are database attributes by adding accessors for each.
This is normally pretty handy, except in this case I need to ONLY get the fields I put into select() because DataTables is expecting only what I send to it.
Example:
Item::select(['image', 'name', 'color']);
Will return appended fields after color in the attributes.
How do I force the exclusion of the appends values when returning results?
Or alternatively, how do I get DataTables to ignore certain attributes?
Not sure which is the least time costly route.
Currently using yajra/laravel-datatables package to send data to the jQuery DataTables AJAX request.
You can call each function in the collection object and then use setHidden method to exclude the unwanted fields like this
$item= Item::select(['image', 'name', 'color'])->get()->each(function($row){
$row->setHidden(['appendedField1', 'appendedField2']);
});
And for the yajra/laravel-datatables you can use something like
$item= Item::select(['image', 'name', 'color']);
return Datatables::of($item)->remove_column('appendedField1');
To solve this I added this method to my Item model:
public static function getAppends()
{
$vars = get_class_vars(__CLASS__);
return $vars['appends'];
}
Then used the following code in the controller:
$items = Item::select(['image', 'name', 'color']);
$DT = Datatables::of($items);
call_user_func_array([$DT, 'removeColumn'], Item::getAppends()); // Has to be called this way with yajra/laravel-datatables-oracle v3.* if passing an array.
return $DT->make(true);

Categories