Laravel 6.x right way to handle Pivot tables - php

I want to connect my Customer model with my CustomerGroup model in Laravel using Pivot tables.
I tried it with the following: (as reference I used https://ben.lobaugh.net/blog/204838/how-to-use-custom-pivot-tables-in-laravel-6-x but I could have made something wrong)
I created the migration customer_group_customer and in the scheme I added following:
$table->unsignedBigInteger("customer_group_id");
$table->unsignedBigInteger("customer_id");
In the models Customer and Customer_groups I added a function. The function is like the following (this is the Customer model for example):
public function groups(){
return $this->belongsToMany("App\CustomerGroup","customer_group_customer","customer_id","customer_group_id");
}
Then I created a customer and a Group and then connected them manually with:
DB::table("customer_group_customer")->insert(["customer_group_id" => $group->id, "customer_id" => $customer->id]);
After that I fetched all Customers and saw that there aren't connected (via dd() I couldn't see any entry on groups or similar):
$customer = \App\Customer::create([]);
$group = \App\CustomerGroup::create(["name" => "hey"]);
DB::table("customer_group_customer")->insert(["customer_group_id" => $group->id, "customer_id" => $customer->id]);
dd(\App\Customer::first());
How do pivot tables get setup correctly?
And is there a better way to create a customer and assign it a group, without making it manually with the DB facade?

The reason your not seeing the groups relationship when you dd() the Customer is because it hasn't been loaded.
You can load the relationship by using with() when querying the model, or load() after you have an instance of the model, or simply by accessing the relationship as a property e.g. $customer->groups;
Eloquent Eager loading documentation
You can also use the relationship to attach different relations rather than using the DB facade to do it manually:
$customer = \App\Customer::create([]);
$group = \App\CustomerGroup::create(['name' => 'hey']);
$customer->groups()->attach($group);
dd($customer->groups);
Attaching / Detaching Documentation

Related

Does a one-to-one relationship in Laravel always need first()?

I have a one-to-one relationship between User and UserSettings models,
But (after $user = auth()->user()) when I try $user->settings()->something it throws an Undefined property error.
It's gone when I use $user->settings()->first()->something...
My question is, is this how it's supposed to work? or am I doing something wrong?
You cannot directly run $user->settings()->something.
Because when you call $user->settings(), it just return Illuminate\Database\Eloquent\Relations\HasOne object.
So it is not the model's object, you need to take the model's object and call its attribute like this.
$user->settings()->first()->something;
Dynamic Properties
Since you have one-to-one relationship between User and UserSettings.
If you have a one-to-one relationship in your User model:
public function settings()
{
return $this->hasOne('App\Models\UserSettings', 'user_id', 'id');
}
According to Laravel doc
Once the relationship is defined, we may retrieve the related record using Eloquent's dynamic properties. Dynamic properties allow you to access relationship methods as if they were properties defined on the model:
Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the get (for one-to-many relationships) or first (for one-to-one relationships) method. It will then be accessible via a dynamic property by the same name as the relation.
So you can use eloquent's dynamic properties like this:
$user->settings->something; // settings is the dynamic property of $user.
This code will give you a result of collection.
$user->settings;
So calling 'something' is not available or it will return you of null, unless you get the specific index of it.
$user->settings()->something
while this one works because you used first() to get the first data of collection and accessed the properties of it .
$user->settings()->first()->something
The first method returns the first element in the collection that passes a given truth test
see docs here laravel docs
If you want to get the user settings itself simply do this:
$user->settings
Then you can get the fields of the settings doing this:
$user->settings->something
When you do this $user->settings() you can chain query after that. E.g.
$user->settings()->where('something', 'hello')->first()
That's why the output of $user->settings and $user->settings()->first() are the same.
Auth only gives you user info;
Try the following code:
$user = User::find(auth()->user()->id);//and then
$user->settings->something;

How can I hide a relation on an Eloquent model

I'm struggling with an Eloquent dummy issue. I have a model User which has a many to many relation with a model Like.
I'm getting my user model by its id with:
$likedUser = User::findorfail($user_id);
At some point in the code, I'm doing:
if ($likedUser->likes->where(...)->count()) {
...
}
Then, I want to return my $likedUser WITHOUT the likes (added in the IF condition above by Laravel). How can I do that? If I do that, it does not work:
return response()->json([
'is_matched' => $isMatched,
'liked_user' => $likedUser,
], 200);
I would love to have a $likedUser->without('likes') to remove the relation added by Laravel. I tried to $outputUser = $likedUser->replicate() but the unique field of my models disappears..
1) What's the elegant way of only returning my User model without its relationship?
2) Why is Laravel adding the relation even if I never set my variable but only use it in a IF?
Thanks a lot for your time guys!

Laravel 5 get relation data

I've made application with Laravel 5.5 and I use MySQL database. I have 2 tables
people: id, name, homeplanet_id
planets: id, name
And foreign key people.homeplanet_id references planets id
I also have 2 models: Person and Planet, and PersonController.
I can get Person's data in controller by using
Person::find($id)->getAttributes()
but it is like
[
id => 1
name => Name
homeplanet_id => 1
]
How can I get data look like next
[
id => 1
name => Name
homeplanet_id => Planet_name
]
You may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users.
https://laravel.com/docs/5.5/eloquent-resources
So, if you want to get JSON, use resources. If you want this data format for Blade views, it's a bad idea to transform it. Just work with the data in Blade template:
{{ $person->name }} lives on the planet {{ $person->planet->name }}
Where planet is name of relationship defined in Person model:
public function planet()
{
return $this->belongsTo(Planet::class, 'homeplanet_id')
}
Use with() method,
Something like
$person = Person::where(array())->with('planet')->get(); //You can fetch all related data by passing multiple values for method with()
To print
{{$person->planet->name}} //where planet indicated the relation on person model as follows
Where planet indicated the relation on Person model.
In our case
Getting data
$person = Person::where(array('id'=>$id))->with('planet')->get();
And in Person model do something like following to fetch planet using with() method:
Person extends model{
public function panet(){
//your relation one to one, one to many etc.....whatever
}
}
Try exploring eager loading in Laravel (eloquent) and scoped query too.
Visit the link for Eager loading and everything will be done by the ORM itself.Link

Getting data from a linked CakePHP3 entity

I have a User entity with a hasMany relationship to Profile via a user_id foreign key.
Within my User class I'm trying to create a virtual field so I can access the name property of the linked Profile entity
protected $_virtual = ['profile_name'];
protected function _getProfileName()
{
return $this->profile->name;
}
Whatever I try I get Trying to get property of non-object
I've also tried:
$this->_properties['profile']->name;
$this->profile->_properties['name'];
I know I can get this data by building a query up using Cake\ORM\Table but I has hoping to aovid that.
What am I doing wrong?
p.s. there is definitely linked data between the two tables.
The answer seems to be
That's not how it works
Instead, use 'eager loading' to get the linked entity data. That is to say use the contain method when using find():
e.g.
$users = TableRegistry::get('users');
$query = $users->find('all', ['contain' => ["Profiles"]])
->where(['id' => $userId])
->first();
or on the query object itself:
$query->contain(['Profiles']);
In this example $query will contain Profile entity objects along with the User entity
i.e. $query->profiles is an array of Profile entities

How can I instantiate a new hasMany Eloquent relationship with at least one empty model?

I have a model (let's call it PageModel) with a hasMany relationship (let's call it rulesList). When I create a new PageModel, I want to default rulesList with at least one empty model. How can I do this in Eloquent?
Code Sample:
// Normal instantiation
$this->rulesList; // Equals NULL
// I can set it manually like so, but is that right?
$this->rulesList = Collection::make([new RulesListModel]);
// NOTE: Doing this does not create an empty model when PageModel is output as JSON
There isn't (to my knowledge) a way to do this within the relationship itself. The reason is that an eloquent model is defined by the database query return values.
You could set up some sort of null row in your database, but I would advise against that.
One way that may work: I think it is possible to create an eloquent model without running a query. I think something like EloquentModel::fill($attributes) would do it. Where $attributes is an array of attributes for your model e.g. array('title' => null, 'description' => null);
You'd have to create this manual model and then add it to your relationship.

Categories