Laravel Accessor on demand on relationship - php

Problem: I have accessor in Size.php model which is called in relationship with Item.php Model, in API i need the accessor to work, but in other controllers i want to disable the accessor. I have removed unnecessary/unrelated code from all files.
What i want to do:
In ItemControllerAPI i need accessor to work, but i want to disable accessor in other controllers.
I have already seen:
I have already seen these links but didn't work for me.
1: https://laracasts.com/discuss/channels/eloquent/accessor-on-demand-but-not-on-every-results
2: Create dynamic Laravel accessor
3: https://laracasts.com/discuss/channels/general-discussion/eager-load-accessors
Size.php (Model)
class Size extends Model
{
protected $appends = ['addons'];
public function items()
{
return $this->belongsToMany('App\Item', 'items_sizes')->withPivot('price');//->withTimestamps();
}
public function getAddonsAttribute($value)
{
$addon = Addon::where('addons.category_id', $this->category_id)
->where('size_id', $this->id)
->get();
return $addon;
}
}
Item.php (Model)
class Item extends Model
{
public function options()
{
return $this->belongsToMany('App\Option', 'items_options')->withTimestamps();
}
public function sizes()
{
return $this->belongsToMany('App\Size', 'items_sizes')->withPivot('price');//->withTimestamps();
}
}
ItemControllerAPI.php (Controller)
class ItemControllerAPI extends BaseControllerAPI
{
public function show($id)
{
// If i call the Size model directly by using setAppends([]) its working fine,
// its removing the appended array from Size model
// $size = Size::all();
// $size->each->setAppends([]);
// return $size;
// If i use it with relationship it won't work.
// $itemSingleQuery = Item::with(['sizes' => function($query)
// {
// Doesn't work
// $query->setAppends([]);
// Doesn't work
// $query->each->setAppends([]);
// }])
// ->with('options')
// ->where('id', $id)
// ->get();
// query for getting data with relationships
$itemSingleQuery = Item::with('sizes')
->with('options')
->where('id', $id)
->get();
return $this->respondSuccess('content found', $itemSingleQuery);
}
}

I've found out how to do this:
After getting the entire collection we need to call setAppends() in each model, which contains appends, to add or remove them before serialization to array or JSON.
$itemSingleQuery = Item::with('sizes')->get();
$itemSingleQuery->each(function ($item) {
$item->sizes->each->setAppends(['addons']);
});

I would suggest that you set $appends when you need it:
$itemSingleQuery->pluck('sizes')->collapse()->each->setAppends(['addons']);

Related

Laravel - Model Eloquent without relationships

i trying to load all rows from a model without the relationship.
The attributes $with it not event set on my Event model but when i do
$events = Event::all();
all my relationship are loaded, and i can see all the query with the dbquerylog.
i don't understand why theses relationship are loaded,
Please help me !
Thanks you.
I'm using Laravel 8.
here's an example.
class Event extends Model {
public function items() {
return $this->hasMany(Item::class);
}
public function items2() {
return $this->hasMany(Item2::class);
}
public function items3() {
return $this->hasMany(Item3::class);
}
public function items4() {
return $this->hasOne(Item4::class);
}
}
$events = Event::all();
If you have a single instance of a model object, you can do:
$obj->withoutRelations();
As laravel documentations says you can use without: https://laravel.com/docs/8.x/eloquent-relationships
Model
protected $with = ['item1','item2','item3','item4'];
Controller
$events = Event::without(['item1','item2','item3','item4'])->get();
I met this problem one day, and it turned out that I was using relation in scope method. Because of this relation values were added to response.
Check out this example:
class Event extends Model {
public function items() {
return $this->hasMany(Item::class);
}
[...]
public function scopeItemsGreen() {
return $this->items->every(function ($item) {
return $item->color == 'green';
});
}

laravel: unable to use model methods in controller to get attribute

I've been trying to do this for a while now. Most of the time, I solve it by using accessors. I'm currently trying to get if the column exists and I created a function in my model which is suppose to return boolean.
Model code:
class Inventory extends Model
{
protected $attributes = ['inventory'];
protected $appends = ['colorThumb'];
public function hasAttribute($attr){
return array_key_exists($attr, $this->attributes);
}
}
Controller code:
public function allInvOperation(Request $request){
$inv = Inventory::where('is_deleted', 0)->with(['product', 'size','color'])->orderByDesc('id')->get();
if(!is_null($request->searchText)){
dd($inv->hasAttribute('inventory'));
$inv = Inventory::where('is_deleted', 0)->with(['product', 'size','color'])->orderByDesc('id');
if($request->inv_filter == 'inventory'){
$inv = $inv->where('inventory', 'like', "%".$request->searchText."%")->get();
}
if($request->inv_filter == 'code'){
$inv = $inv->whereHas('product', function ($q) use ($request){
$q->where('code', "%".$request->searchText."%");
})->get();
}
}
ERROR
Method Illuminate\Database\Eloquent\Collection::hasAttribute does not exist.
The code you are doing hasAttribute on is a Collection of objects, you need to use first on that query to get a single result on which you can later do hasAttribute

Laravel Dingo get data from multiple tables

I'm trying to retrieve data from two tables with relationship one-to-many. I need it to return in one response since it will be displayed simultaneously. However, I don't understand how to return extended object. For now I have something like this in controller
public function show(Site $id)
{
foreach ($id->features() as $feature) {
$id->features[] = $feature;
}
return $id;
}
And this is my model
class Site extends Model
{
protected $fillable = ['path', 'site_link'];
public $timestamps = false;
public function features() {
return $this->hasMany('App\SiteFeature');
}
}
For now it returns an empty array of features property.
If you are using implicit model binding then Lazy eager Load features relationship in controller action because model is already loaded
public function show(Site $site)
{
$site->load('features');
return $site;
}
If no implicit model binding then Eager load features using with at time of model loading
public function show($id)
{
$site = Site::with('features')->find($id);
return $site;
}
Check details for loading relationship https://laravel.com/docs/5.6/eloquent-relationships#constraining-eager-loads

Load Eloquent Model Relationships Child Nodes in Laravel 5.1

I can't return $parent->child() | I get null;
Parent Model
class PrinterSetting extends Model
{
protected $table = 'printer_settings';
//fillables here
protected $with = ['copies'];
public function copies(){
return $this->hasMany(\App\DB\PrinterCopy\PrinterCopy::class, 'printer_id');
}
}
Child Model
class PrinterCopy extends Model
{
protected $table = 'printer_copies';
public function printer(){
return $this->belongsTo(\App\DB\PrinterSetting\PrinterSetting::class, 'id', 'printer_id');
}
}
Code in Controller:
$availablePrinters = $this->printerSetting->where('is_active', 1)->get();
foreach($availablePrinters as $availablePrinter)
{
//working
foreach($availablePrinter->copies() as $copy)
{
//Not working
}
}
I can't find the reason why it's not working.
I Tried to dump $availablePrinter->copies and it work, but ofcourse I can't insert it on foreach() loop.
Please help.
$availablePrinter->copies() return Query builder, you have to use $availablePrinter->copies which is Collection that you can add in loop, Try this
$availablePrinters = $this->printerSetting->where('is_active', 1)->get();
foreach($availablePrinters as $availablePrinter)
{
//working
foreach($availablePrinter->copies as $copy)
{
dd($copy)
}
}
Side note, also fix your belongsTo relation in PrinterCopy model, it should be like this
public function printer(){
return $this->belongsTo(\App\DB\PrinterSetting\PrinterSetting::class,'printer_id');
}

How in laravel to add a condition for a eager load relation?

there are the following models:
class Page extends Model {
protected $with = ['blocks'];
public function blocks() {
return $this->belongsToMany('\App\Block');
}
}
class Block extends Model {
protected $with = ['fields'];
public function fields() {
return $this->hasMany('\App\Fields');
}
}
class Field extends Model {
protected $with = ['data'];
public function data() {
return $this->hasOne('\App\Data');
}
}
And there is a controller in which I get the data:
public function get() {
$page_id = 1;
$data = Page::find($page_id);
}
The result is the following structure:
Page
- Block 1
- field 1
- Some data
- field 2
- More data
- Block 2
- Field 1
- Other data
The block may belong to multiple pages.
The field belongs to the block.
But the data stored in this field belong to both the field and the page at the same time.
So, I need to pass the variable $page_id to the data() function of the Field model. To get something like this:
public function data() {
return $this->hasOne('\App\Data')->where('page_id', '=', $page_id);
}
Tell me, please, how to do this?
I solved this problem by removing $with from models and selecting the data with this code:
Page::with(['blocks.fields.data'=> function($query) use ($page_id){
$query->where('page_id', $page_id);
}])->find($page_id);
But it's not beautiful. I just want to write Page::find($page_id);
Thanks!
I like force loading relation but in your case maybe this what you looking for :
https://laravel.com/docs/master/eloquent-resources#conditional-relationships
you can do this as the following :
Page::whereHas('blocks.fields.data', function($query) use ($page_id){
$query->where('page_id', $page_id);
})->find($page_id);

Categories