How to remove fields from Laravel JSON Api response - laravel

I have an API returning an object with one-to-one relation to another object. As the models behind the objects, do have timestamps, these are also delivered when asking API.
// Get all transactions
Route::get('transaction', function() {
return Transaction::with('Personone','Persontwo')->get();
});
How do I prevent Laravel from returning the timestamps of the objects in the API?
I googled, but only found some hints to middleware or response macros but found no example pointing me into the right direction. Maybe you can help me.

You can make attributes "hidden" so that they do not show up in json.
docs
class Transaction extends Model
{
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = ['timestamp'];
}

I'm not sure if I get the question correctly but if you want to select from eager loads there are two ways
first one is inline selecting
Route::get('transaction', function () {
return Transaction::with('Personone:id,foo,bar', 'Persontwo:id,foo,bar,foobar')->get();
});
second one is to pass a closure
Route::get('transaction', function () {
return Transaction::with([
'Personone' => function ($query) {
$query->select('id', 'foo', 'bar');
},
'Persontwo' => function ($query) {
$query->select('id', 'foo', 'bar', 'foobar');
}])->get();
});
Eager Loading Specific Columns You may not always need every column
from the relationships you are retrieving. For this reason, Eloquent
allows you to specify which columns of the relationship you would like
to retrieve:
$users = App\Book::with('author:id,name')->get();
Constraining Eager Loads Sometimes you may wish to eager load a
relationship, but also specify additional query constraints for the
eager loading query. Here's an example:
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%'); }])->get(); In this example, Eloquent will only eager load posts where the post's title
column contains the word first. Of course, you may call other query
builder methods to further customize the eager loading operation:
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc'); }])->get();

Related

Laravel, eloquent, query: problem with retriving right data from DB

I'm trying to get the right data from the database, I'm retriving a model with a media relation via eloquent, but I want to return a photo that contains the 'main' tag stored in JSON, if this tag is missing, then I would like to return the first photo assigned to this model.
how i assign tags to media
I had 3 ideas:
Use orWhere() method, but i want more likely 'xor' than 'or'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main')->orWhere();
}]);
return $models->paginate(self::PER_PAGE);
Raw SQL, but i don't really know how to do this i tried something with JSON_EXTRACT and IF/ELSE statement, but it was to hard for me and it was a disaster
Last idea was to make 2 queries and just add media from second query if there is no tag 'main'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}]);
$models_all_media = Model:: with(['media']);
return $models->paginate(self::PER_PAGE);
but i tried something like
for($i=0; $i<count($models); $i++) {
$models->media = $models_all_media
}
but i can't do this without get() method, beacuse i don't know how to change this to LengthAwarePaginator class after using get()
try using whereHas https://laravel.com/docs/9.x/eloquent-relationships
Model::with('media')
->whereHas('media',fn($media)=>$media->whereJsonContains('custom_properties->tags', 'main'))
->paginate(self::PER_PAGE);
as per your comment you can use
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}])
->leftJoin('media', function ($join) {
$join->on('models.id', '=', 'media.model_id')
->whereNull('media.custom_properties->tags->main');
})
->groupBy('models.id')
->paginate(self::PER_PAGE);
return $models;

I want to change my laravel database query to model relationship query for search data from database

I am learning laravel Eloquent relationship, I successfully created a relationship between models for eq city belongsTo State and belongsTo country.
public function state(){
return $this->belongsTo(State::class,'state_id', 'sr_id');
}
public function country(){
return $this->belongsTo(Country::class,'country_id', 'sr_id');
}
this is my old search code, I want to know how we search data using model relationship method, how we call each columns from their following table.
$city = city::leftjoin('master_country','master_country.sr_id','=','master_city.country_id')
->leftjoin('master_state','master_state.sr_id','=','master_city.state_id')
->where('master_city.sr_status',0)
->where('master_city.sr_id','LIKE','%'.$query.'%')
->orWhere('master_city.sr_name','LIKE','%'.$query.'%')
->orWhere('master_city.city_tel_code','LIKE','%'.$query.'%')
->orWhere('master_country.sr_name','LIKE','%'.$query.'%')
->orWhere('master_state.sr_name','LIKE','%'.$query.'%')
->orderBy('master_city.sr_id')
->paginate(3,array('master_city.*','master_country.sr_name AS c_name','master_state.sr_name As s_name'));
so it would we like..
City::with('state','country')->where etc
You can have a look at Constraining Eager Loads
$cities = City::with(
[
'state' => function ($query) {
$query->where('state_column', 'some_value');
},
'country' => function ($query) {
$query->where('country_column', 'some_value');
}
]
)->get();
If you don't want to retrieve the relation data, you can use the whereHas() method:
City::whereHas('state', fn (Builder $builder) => $builder->where('state_column', 'some_value')));
Using arrow function is not required.
More documentation on this

Limit attributes from joined tables in complex query build

So my question is a follow in from the one I posted here:
Laravel 8 - Limit joined tables
And using the suggestion in the accepted answer, this is what I ended up with
User::with(['profile'])->with('collections.game')
->whereHas('collections', function (Builder $query) {
return $query->whereHas('game', function (Builder $query) {
return $query->where('status', '<>', 'denied');
});
})
->paginate(10)
It works great. I get the information about the user from both the users and profiles table as well as all the collections the user owns of games that do not have a denied status. Sweet! Thanks #matiaslauriti!
The problem I'm seeing is that all of the columns are getting pulled in. This includes information about the user such as their email address (from the user's table) and their physical mailing address (from the profile table) among other things. Information that I would prefer not to disclose.
Under normal circumstances, this wouldn't be a problem because once the blade template was parsed and the markup for the page is generated, all of that data would get trashed and not get returned to the browser. But my circumstance is different. I'm using InertiaJS with React so all of that data is getting returned from the server via an XHR request and passed in as props to my component. This makes it so that all the data is completely visible and I definitely don't want that.
I've played around with ::without() and ::withOnly() but I couldn't get them to work. Not just not work for User but for the nested Profile data as well. So is there a way to do what I'm doing to get all the data I need but without all the sensitive data I'd rather not return to the client?
thnx,
Christoph
It can be done one by passing a closure function in with() as second index of array.
Example:
Post::query()
->with(['user' => function ($query) {
$query->select('id', 'username');
}])
->get()
It will only select id and username from other table.
Remember that the primary key (id in this case) needs to be the first param in the $query->select() to actually retrieve the necessary results.*
IF you are using a JSON response (e.g. you have an API) and IF you want to have a standard way to create "views" in your data for the purposes of e.g. displaying them in specific responses you can do that using JSON resources(if these two don't apply to you then you can ignore this answer)
In your case you could create two resources e.g. UserResource and ProfileResource and implement them as:
UserResource:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'profile' => new ProfileResource($this->profile),
];
}
ProfileResource:
public function toArray($request)
{
return [
'public_info' => $this->public_info,
];
}
As the docs mention you can send this response as:
$users = User::with(['profile'])->with('collections.game')
->whereHas('collections', function (Builder $query) {
return $query->whereHas('game', function (Builder $query) {
return $query->where('status', '<>', 'denied');
});
})
->paginate(10);
return UserResource::collection($users);
Calling the whereHas method will never select the related records. So if you want to run only the query on relations remove with in query
User::query()
->whereHas('collections', function (Builder $query) {
return $query->whereHas('game', function (Builder $query) {
return $query->where('status', '<>', 'denied');
});
})
->paginate(10);
You can also use dotted relation
User::query()
->whereHas('collections.game', function (Builder $query) {
return $query->where('status', '<>', 'denied');
})
->paginate(10);

How to use where by second table Laravel?

I have the following query:
$objects = Object::with("prototypes");
As you can see I do request to model Object and join it with prototypes.
The prototypes table has structure:
prototype_id
name
How to make where in above query like as:
$objects = Object::with("prototypes")->where("prototype_id", 3);
Object model:
public function prototypes()
{
return $this->belongsToMany('App\Prototype', 'object_prototype', 'object_id');
}
Prototype model:
public function objects(){
return $this->belongsToMany("App\Object", "object_prototype", "prototype_id", "object_id");
}
If the relationship is correctly set in your Eloquent models, you can use the WhereHas function to query relationship existence.
$objects = Object::with('prototypes')
->whereHas('prototypes', function ($query) {
$query->where('prototype_id', 3);
})
->get();
Just to add on #Jerodev answer.
If you need even more power, you may use the whereHas and
orWhereHas methods to put "where" conditions on your has queries.
These methods allow you to add customized constraints to a
relationship constraint, such as checking the content of a comment:
// Retrieve all posts with at least one comment containing words like foo%
$posts = Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
You can read more about Eloquent Relationships
And in your case you can also construct a your query like.
Object::whereHas('prototypes', function ($query)){
$query->where('prototype_id', 3);
})->get();

Laravel eloquent: get data with model wherePivot equal to custom field

I have an eloquent object Performer that has Albums and Albums have Images
Here is setup:
Model Performer->albums():
public function albums()
{
return $this->belongsToMany('Album','performer_albums','performer_id','album_id');
}
Model Album->images()
public function images()
{
return $this->belongsToMany('Image','album_images','album_id','image_id')->withPivot(['type','size']);
}
I have performer object stored as such:
$performer = Performer::where...->first();
Now I need to get Performer's Albums with images where size is 'large'
So to avoid nesting queries, can I use with()?
I tried
$performer->albums()
->with('images')
->wherePivot('size','large')
->get();
But laravel tells me it's trying to use wherePivot for Performer-Album relationship (M-2-M)
PS. I am also aware that I can do this,
$performer = Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
but question remains the same.
You need eager load constraints:
$performer->albums()
->with(['images' => function ($q) {
$q->wherePivot('size','large');
}])
->get();
And btw, no, you can't do this:
Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
instead you could do:
Performer::with(['albums.images' => function ($q) {
$q-> .....-conditions for additional fields in album_images....
}])->get();

Resources