Excluding Laravel appends values from model results - php

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);

Related

Laravel replace variable on Model

I would like to be able to override a variable on the model, so that a normal field is instead replaced by a relationship's field, i.e.
Where product.image might normally be a field, I want to run a function which will go through all of the resulting products from a query and replace the image field with something like the following --
(Product.php) Model
...
public function variantImages(){
return $this->image = $this->variants()->first()->pluck('image_url');
}
...
So the default product image field is replaced by the "first product variant's image". I don't want to do this in a collection once I have already got the data, the problem here is being able to do this at a Model level.
Is there a way to do this within a scope?
You can create an accessor instead of a normal function:
// Singular because it only gets one
public function getVariantImageAttribute(){
return $this->image = $this->variants()->first()->pluck('image_url');
}
This will make it available under $product->variant_image
Then you can ensure it is always appended to your model (if you want) by adding it to the appends e.g.:
$appends = [ 'variant_image' ];
Since this is not the best idea since it will force load the relationship every time you get a product (even if you didn't request it) you can conditionally control when to append it via e.g.:
return response()->json($product->append('variant_image'));
Note that the append method also works for collecitions of eloquent models.

How to use append attributes with Laravel Eloquent and pagination

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();

Get specific columns using โ€œonly()โ€ function in Laravel Eloquent

I have two tables user and post.
in my posts method I want to return users posts with custom fileds.
none of below solutions dose not works
class UserController
{
public function posts(User $user)
{
return $user->only(['username', 'name', 'posts.body' // solution one
return $user->only(['username', 'name', 'posts'=>function($q){
$q->select(['body']
}])// solution two
Does anyone have a work around?
in post only get one column value in one row then used value method
Post::where('user_id')->value('body')
if you want to get multiple value(rows) from single column used pluck method
Post::where('user_id')->pluck('body') //this will get on the array
otherwise used select method
Post::select('body')->where('user_id')->get(); // this will get on collection
Post::select('body')->where('user_id')->get()->toArray(); // this will get on array

Lumen/Laravel get attribute when using select and with

I've run into this issue a couple of times now and I can't seem to find a solution - I feel like I'm missing something obvious.
I am making a JSON API with Lumen, building up specific routes for specific use cases. To speed up load times, I only want to output the fields I will be using.
My model has a combination of fields, relations and attributes. I am struggling to specify the attribute when limiting output
For example:
A task has time_records. Each time_record has a value of time.
I want to output the task names, time record values plus an attribute of totalTime which I have set on the task like the below (this isn't the full code but hopefully it gets across the idea)
class Task extends Model {
protected $appends = [
'totalTime'
];
public function timeRecords() {
return $this->hasMany('TimeRecord');
}
public function getTotalTimeAttribute() {
$total = $this->timeRecords()->map(function($time_record){
return $time_record->value;
});
return array_sum($total);
}
}
If I then do the following:
Task::get();
And output that, I get all the tasks with the totalTime attribute. However, as mentioned I want to only output some attributes:
Task::select('id', 'name')
->with([
'timeRecord' => function($query){
$query->select('id');
}
])
No matter what I do, I can't seem to get the attribute output with that. The value is there in the array, but is null.
To resolve this, you have to select the fields that the attribute accesses.
Because I was only returning the ID of the time_record, the attribute was unable to calculate the total_time as the value field was not available.
(Was only while writing this question out did I discover the answer... ๐Ÿ™„)
You can get that using with() method without callback. Callback method provide you to filter results..
Task::select('id', 'name')
->with("timeRecords:id") // use comma for multiple columns
->get();

How to save multiple request parameters in laravel 5.2 ?

I am just learning laravel now. And I have this problem. I have passed 2 request parameters to my controller function. First request parameter holds an object value, but I converted it to a serialized form since the field of my table where it will be saved has a text datatype. The second request parameter holds a overall_total calculated value and it has a float datatype field. My problem is, how would I store it in my database? I have tried to use the create function but it returns an error. Some forums regarding this are not so clear. I just can't figure it out yet. Can somebody help me with this? Here are my codes.
function store
public function store(Request $request){
$serialize_po = serialize($request['purchase_orders']);
$overall_total = $request['overall_total'];
$purchase_orders_save = PurchaseOrder::create(?);
}
How would I save 2 parameters using create? or is there other way I can saved it?
Inside of $request['purchase_orders'] is shown in the image below
Inside of $request['overall_total'] is just a number. E.g. 310
My Database Table Structure is shown below
The create() function in Laravel accepts an associative array, where the array keys are the names of the columns, and the array values are the corresponding values for the columns.
So your store function might look something like this:
public function store(Request $request){
$serialize_po = serialize($request['purchase_orders']);
$overall_total = $request['overall_total'];
$purchase_orders_save = PurchaseOrder::create([
'purchase_orders' => $serialize_po,
'overall_total' => $overall_total
]);
}
One other thing to note is that as a safety precaution, Laravel does not allow the properties of a model to be filled in this fashion out of the box. You will need to list in your model using the $fillable property which keys you will allow to be filled by passing in an associative array. If you don't, you'll likely get a MassAssignmentException.
So in your model, you will likely need to have at least the following:
class PurchaseOrder extends Model
{
protected $fillable = ['purchase_orders', 'overall_total'];
}
There are more options and ways to do this, but that is the basic and typical scenario.
More info on using the create() method and handling mass assignment is available in the documentation: https://laravel.com/docs/5.2/eloquent#mass-assignment
Considering your model name is PurchaseOrder, you first need to create a new instance of it. Then, use the save method.
public function store(Request $request)
{
$purchaseOrder = new PurchaseOrder;
$purchaseOrder->overall_total = $request['overall_total'];
$purchaseOrder->purchase_orders = serialize($request['purchase_orders']);
$purchaseOrder->save();
}
See https://laravel.com/docs/5.2/eloquent#basic-inserts for more info.
Something like this :
DB::table('purchaseOrder')->insert(
['purchase_orders' => $serialize_po,'overall_total' => $overall_total]
);
See doc if you want to explore more.

Categories