Laravel how to create empty relationship? - php

I have a condition inside a relationship where if the user is logged in it will return the relation and if not i want it to return empty relationship.
here is what i want :
public function dummy()
{
return (auth()->user()) ? $this->hasOne(blah::class) : emptyrelationship();
}

You should check with DD() what is being returned as you like.
If there's no data for the relationship to show, it will just return no data.

To return an empty relationship instead of null, you can try this:
public function item()
{
return $this->belongsTo(Item::class)
->withDefault(function () {
return new Item();
});
}

Try This example
public function shop(){
if(true) {
return $this->newQuery(); // or newQueryWithoutScopes()
}
return $this->belongsTo('App\Models\Shop');
}

If there is no user in the user table collection will return relationship as "relationship: user: []" as blank only if you do dd($var) on your query,then you can check though conditions in your code;

Eloquent has a method for that newModelInstance
Best to keep the eloquent model standard relationship and move logic elsewhere
public function dummy()
{
return $this->hasOne(blah::class);
}
$dummy = $model->dummy;
if (!$dummy) {
$dummy = $model->dummy()->newModelInstance();
}

Related

the relation is not appending to the model in laravel

i want to append a relation to my model in laravel i know its possible with resource but i need it to be appened to model so here its like below what i do :
protected $appends = ['accommodation_rooms'];
public function getAccommodationRoomsAttribute(){
return $this->accommodationRooms();
}
and my relation is :
public function accommodationRooms()
{
return $this->Hasmany(AccommodationRoom::class);
}
but when i run my api it returns null but when i call the relation it has the relation and it has no problem . any idea what i am doing wrong ??
EDIT
$data = Accommodation::with('city', 'accommodationFacilities', 'gallery')
->where('is_deleted', 0)->Paginate(env('PAGINATE_NUMBER'));
return $data;
return $this->accommodationRooms() will return an instance of query builder. That is probably why it was showing empty.
Change it to
public function getAccommodationRoomsAttribute(){
return $this->accommodationRooms;
}
Call it without ()
public function getAccommodationRoomsAttribute(){
return $this->accommodation_rooms;
}
And update your relation to:
public function accommodationRooms()
{
return $this->hasMany(AccommodationRoom::class);
}
And make sure you have accommodation_id column in your accommodation_rooms table

Laravel: create a fake belongsToMany withPivot relationship

I have a belongsToMany() relationship between a User and a Group. The user has a level within any group he belongs to.
public function groups()
{
return $this->belongsToMany('App\Group', 'user_group', 'user_id', 'group_id')
->withPivot('level');
}
This works great.
However if the User is an admin, I would like the groups function to return ALL Groups with level = 3, regardless of whether that relationship exists in the pivot table or not.
I can successfully create a Collection which mirrors the data structure as follows:
\App\Group::all()->transform(function ($item, $key) use ($uid) {
$item->pivot = collect(['user_id'=>$uid,'group_id'=>$item->id,'level'=>3]);
return $item;
});
However, I cannot substitute the two outputs as one returns a belongsTo relationship instance and the other returns a Collection. This means I can call ->get() on the former but not the latter.
I thought about using the DB:: facade and creating a Builder for the latter, but I cannot add the Pivot values manually.
Any thoughts on how to achieve this?
-- UPDATE --
I am currently cheating by adding the ->get() inside the groups() method, but this is messy and I would still like to know if there is a better way to solve this problem.
public function groups()
{
if ($this->isAdmin()) {
return \App\Group::all()->transform(function ($item, $key) use ($uid) {
$item->pivot = collect(['user_id'=>$uid,'group_id'=>$item->id,'level'=>3]);
return $item;
});
} else {
return $this->belongsToMany('App\Group', 'user_group', 'user_id', 'group_id')
->withPivot('level')->get();
}
}
So this solution should work(not tested), but it is not the "cleanest" it would be better to access all groups through some other mechanism but because I don't know your admin implemention it is hard to guess.
public function groups()
{
return $this->belongsToMany('App\Group', 'user_group', 'user_id', 'group_id')
->withPivot('level');
}
public function scopeSpecialGroups($query)
{
return $query->when($this->role === 'admin',function($query){
return Group::where('level', '>', 3');
})->when($this->role != 'admin',function($query){
return $query->with('groups');
});
}
Then you should be able to call User::specialGroups()->get();

Eloquent all records relationship

I'm trying to build an alternative relationship that returns all records instead of only related records. I have tried returning a query builder, but that doesn't work, it must be a relationship. What should I return to make this work?
public function devices()
{
if ($this->admin) {
// return all devices relationship instead
} else {
return $this->belongsToMany('Device', 'permissions');
}
}
Fiddle: https://implode.io/XXLGG8
Edit: I'd like to continue building the query in most cases, not just get the devices.
The devices() function in your model is expected to return a relation, you shouldn't add the if statement there. Make your devices() function like this:
public function devices()
{
return $this->belongsToMany('Device', 'permissions');
}
In your User model add a new function:
public function getDevices() {
if($this->admin === true) {
return Device::all();
}
return $this->devices();
}
Now you can do:
$admin->getDevices(); // will return all devices
$user->getDevices(); // will return only relations
I actually went a slightly different way and used a scope:
protected function scopeHasAccess($query, User $user)
{
if ($user->admin) {
return $query;
}
return $query->join('permissions', 'permissions.device_id', "devices.id")
->where('permissions.user_id', $user->user_id);
}
Add devices accessor method to the User model and implement your logic there.
public function getDevicesAttribute() {
if ($this->admin) {
return Device::all();
}
return $this->getRelationValue('devices');
}
See updated "fiddle".

How to return all models in BelongsToMany relationship

I have relationship on User model like below:
public function brands() {
$roles = config('constants.roles');
if ($this->hasRole($roles['brand_site_admin'])) {
return $this->belongsToMany(Brand::class, 'brand_has_users');
} else
if ($this->hasRole($roles['client_admin'])) {
return $this->belongsToMany(Brand::class, 'brand_has_client_admin');
}
// For admin role I want to return all brands, from Brand Model
// ??
}
For Admin role I want to return all rows from Brand model, How can I get that?
And that should be instance of BelongsToMany class, then only it won't break code in my controller.
Update:
When I do $user->brands() I want all the brands from brands table if $user is an admin (In above code if it doesn't goes in any condition then it's Admin).
I think you should try this as suggested in this SO post, first create the relationships like this
$roles = config('constants.roles');
public function siteAdminBrands()
{
return $this->hasMany(Brand::class, 'brand_has_users');
}
public function clientAdminBrands()
{
return $this->hasMany(Brand::class, 'brand_has_client_admin');
}
public function brands($query)
{
return $query
->when($this->hasRole($roles['brand_site_admin']),function($q){
return $q->with('siteAdminBrands');
})
->when($this->hasRole($roles['client_admin']),function($q){
return $q->with('clientAdminBrands');
});
}
}

Cache Eloquent Relationship query

How can I cache this Eloquent query:
dd($user->roles);
Because above will somehow trigger the $user->roles() query I assume.
I have tried with this:
public function roles() {
return \Cache::remember('user_' . $this->id . '_roles', 10, function() {
return $this->hasMany('App\Role');
});
}
But it does not work, because it has to return a array, not eloquent query.
Any suggestions?
Here is my approach:
public function bookmarks(): HasMany
{
return $this->hasMany(Bookmark::class);
}
protected function getBookmarksCacheKey(): string
{
return sprintf('user-%d-bookmarks', $this->id);
}
public function clearBookmarksCache(): bool
{
return Cache::forget($this->getBookmarksCacheKey());
}
public function getBookmarksAttribute(): Collection
{
if ($this->relationLoaded('bookmarks')) {
return $this->getRelationValue('bookmarks');
}
$bookmarks = Cache::rememberForever($this->getBookmarksCacheKey(), function () {
return $this->getRelationValue('bookmarks');
});
$this->setRelation('bookmarks', $bookmarks);
return $bookmarks;
}
You can't store a relationship in the cache. You need to cache the actual data retrieved from the database. So you'll have something like this:
public function roles()
{
return \Cache::remember('user_' . $this->id . '_roles', 10, function()
{
return $this->hasMany('App\Role')->get()->toArray();
});
}
And now you have to access it as a method, not a property, because it's not returning a relation anymore (and Eloquent would throw an exception):
$user->roles();
Now you should get an array as you want.
If you want to cache user together with its roles you can do it this way:
$user = User::find(1);
$user->load('roles');
Cache::put('users_'.$user->id, $user, 10);
I don't know why, but you need to use load here instead of with. If you used with you would get error that you cannot cache PDO instance.

Categories