How to define Laravel Eloquent relationships via a intermediate table - laravel

I have three tables that look like this:
Users
id
name
Things
id
name
ThingsAssigned
id
user_id
thing_id
I want to know how to set up my model relationships and how to write a query where I can pass user_id and get back this structure (will be sent as JSON):
"things": [
{
"name":"thing1"
},
{
"name":"thing2"
}
]

For many to many tables, the naming of the pivot table has to be alphabetical and singular. So the correct name for the table is thing_user. There from it is pretty straight forward.
For your User.php model.
class User {
public function things(): BelongsToMany {
return $this->belongsToMany(Thing::class);
}
}
For the Thing.php model.
class Thing {
public function users(): BelongsToMany {
return $this->belongsToMany(User::class);
}
}
Relations in Laravel is about consistently follow the naming conventions and you have less problems.
To access things, you can do it like so.
$user->things;
To include things with the users you can do.
User::with('things')->get();

After you've set up what has been suggested by #mrhn, you can use it in the following way:
public function randomFunction() {
$user = User::find($user_id);
return $user->things();
}

Related

Laravel BelongsTo relation - where on instance attribute

I have the following model:
class Order extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'shipping_email_address', 'email_address')
->where('customer_id', $this->customer_id);
}
}
Now when I call Order::with('user')->get(), it doesn't load the users.
I can access the user just fine when using Order::first()->user.
Is it possible to eager load a relationship with a where clause on a model instance attribute (like $this->customer_id)? Or is there another way to make a relationship based on two columns?
You can do this :
Your relation :
public function user()
{
return $this->belongsTo(User::class);
}
Then you can make query like this :
$userId = 5;
$result = Order::whereHas('user',function($q) use ($userId){
return $q->where('id',$userId);
});
Reply to your comment:
Having this relation :
public function user()
{
return $this->belongsTo(User::class);
}
Use this :
Order::with('user')->get()
This will retrieve all orders with its users. If you have some problem on that query then you have a wrong relationship. Make sure you have a foregin key in Orders table, if you dont espcify some foreign key on eloquent relationship, eloquent will understand than foreign key is : user_id, if not, especify putting more arguments to this function :
$this->belongsTo(User::class,...,...);
With function make join according to relationship configuration, just make sure the relation is ok. And all work fine !
If you want to keep your current flow, i would do it like so. Thou the josanangel solution is most optimal.
When getting orders include them using with. All these are now eager loaded.
$orders = Order::with('user');
Now utilize eloquent getters to filter the user by customer_id. This is not done in queries, as that would produce one query per attribute access.
public function getUserByCustomerAttribute() {
if ($this->user->customer_id === $this->customer_id) {
return $this->user;
}
return null;
}
Simply accessing the eloquent getter, would trigger your custom logic and make what you are trying to do possible.
$orders = Order::with('user')->get();
foreach ($orders as $order) {
$order->user_by_customer; // return user if customer id is same
}
Your wrong decleration of the relationship here is what is making this not function correctly.
From the laravel's documentation:
Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with a _ followed by the name of the parent model's primary key column. So, in this example, Eloquent will assume the Post model's foreign key on the comments table is post_id.
in your case the problem is that laravel is searching for the User using user_id column so the correct way to declare the relation is
public function user()
{
return $this->belongsTo(User::class, 'customer_id'); // tell laravel to search the user using this column in the Order's table.
}
Everthing should work as intended after that.
Source: documentation

How to define multiple belongsTo in laravel

My table has many foreign key for example prefecture_id, gender_id and status_id.
And I made model for those table.
So I want to define multiple belongsTo method like following for get all data with query builder..
But In fact belongsTo can't use like this.
public function foreign(){
return $this->belongsTo([
'App/Prefecture',
'App/Gender',
'App/Status',
]
}
And if the only way is defining multiple method for belongs to.
How do I get all belongstos data in querybuilder.
Please give me advice.
As far as I am aware, there's not a way to get multiple belongsTo from a single method. What you have to do is make one method for each relationship and when you want to load the relationships you can do the following.
Model
public function prefecture()
{
return $this->belongsTo(\App\Prefecture::class);
}
public function gender()
{
return $this->belongsTo(\App\Gender::class);
}
public function status()
{
return $this->belongsTo(\App\Status::class);
}
Query
// This will get your model with all of the belongs to relationships.
$results = Model::query()->with(['prefecture', 'gender', 'status'])->get();

How get users from section table ? laravel

The idea is that I have relation between two table's section and user
the section_id exist in users table it relation with id section
SO for ex : section_id = 2 > want to bring all users belongs to id section
My code here use id from url and I don't want to do this I want without id bring all users :
public function getAllUsers($id)
{
$data = User::where('section_id',$id)->get();
}
If I'm understanding you correctly, you want to get all the users for a particular Section that have the relationship to that section.
Assuming you have the relationship set up correctly on the Section model:
public function users() {
return $this->hasMany('App\User');
}
I suggest you go about this 'backward' and just pull the users from the Section model itself (eager loading allows one query):
$section = Section::with('users')->first();
$usersForThisSection = $section->users;
If you setup a One to Many relationship you can say things like: $user->section to return the section a user belongs to...and $section->users to get a collection of all the users in a section -- the getAllUsers() is redundant in this case.
Make sure your User and Section models contain these methods. Sounds like your migration was setup properly if you have a section_id column on your users table.
More info on one-to-many here: https://laravel.com/docs/5.8/eloquent-relationships#one-to-many
// app/User.php
class User extends Model
{
public function section() {
return $this->belongsTo('App\Section');
}
}
// app/Section.php
class Section extends Model
{
public function users() {
return $this->hasMany('App\User');
}
}
Test out your relationships in php artisan tinker like so...
$user = User::find(1);
$user->section
$section = Section::find(1);
$section->users
you need a unique identifier to receive section_id's users. receive it from url(get) or ajax(post) . if you worry about users access then use "auth"

Laravel hasManyThrough with ManyToMany pivot

I am making a game and I have users which have facilities and for this I use ManyToMany
user_facilities
-user_id
-facility_id
But each relation must have a facility level, so I've added facility_levels table and each of this levels must be connected to the ManyToMany relation. So user_facilities now looks like this
user_facilities
-user_id
-facility_id
-level_id
level_id is the connections between the facility which the user owns and which level it is.
My question is how do I connect this in the models?
The User model now has this
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities');
}
And Facility
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities');
}
So how do I get the level of the facility which the user owns?
In blade I hope there is a way I can use something like
{{ $user->facility->level->property }}
level is part of the user_facilities table not of facility
Therefore, you should be able to access the level_id from the many to many relationship of user and facility
One thing you can do is to access the immediate table (also called pivot table).
First, edit your relationship to include the extra attributes.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->withPivot('level_id');
}
public function users()
{ // if you omit this EDIT/UPDATE, you cannot do this:
// $facility->users()->first()->pivot->level_id;
return $this->belongsToMany('App\User', 'user_facilities')
->withPivot('level_id');
}
Take note that when accessing a many to many relationship, Laravel will immediately assign a pivot attribute onto the result which contains details about the pivot table of the two models
Now try accessing the extra column:
$facility = $user->facilities->first();
$level_id = $facility->pivot->level_id;
// now you can use $level_id for finding the level.
$level = Level::find($level_id);
Now, since you can do that, you can also create a model for the many to many relationship of user and facility that will have that property of level_id
Let's create a new model called UserFacility that will extend Pivot.
This will be your Pivot model for many to many relationship of user and facilities.
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserFacility extends Pivot
{
}
Then update your users and facilities relationships as follows.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->using('App\UserFacility');
}
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->using('App\UserFacility');
}
Notice that using method.
$userfac = $users->facilities->pivot; // <-- pivot will now be an instance of App\UserFacility
echo $userfac->level_id;
Lastly,
If you don't want the pivot attribute name, you can change it using the as method, chain it after the belongsToMany method, like this:
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->as('UFac')
->using('App\UserFacility');
}
$userfac = $users->facilities->UFac; // <-- you can now access the pivot table using the property `UFac`
echo $userfac->level_id;
It may also be possible that your pivot table has a relationship with a level since it has a level_id. Don't worry, it's possible, just add this function in your UserFacility model.
public function level()
{
return $this->belongsTo('App\Level');
}
Now you can do this!
$user->facilities->first()->UFac->level; // <-- this will be an instance of App\Level
source: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

Laravel - Pivot table for three models - how to insert related models?

I have three models with Many to Many relationships: User, Activity, Product.
The tables look like id, name. And in the each model there are functions, for example, in User model:
public function activities()
{
return $this->belongsToMany('Activity');
}
public function products()
{
return $this->belongsToMany('Product');
}
The pivot table User_activity_product is:
id, user_id, activity_id, product_id. The goal is to get data like: User->activity->products.
Is it possible to organize such relations in this way? And how to update this pivot table?
First I suggest you rename the pivot table to activity_product_user so it complies with Eloquent naming convention what makes the life easier (and my example will use that name).
You need to define the relations like this:
// User model
public function activities()
{
return $this->belongsToMany('Activity', 'activity_product_user');
}
public function products()
{
return $this->belongsToMany('Product', 'activity_product_user');
}
Then you can fetch related models:
$user->activities; // collection of Activity models
$user->activities->find($id); // Activity model fetched from the collection
$user->activities()->find($id); // Activity model fetched from the db
$user->activities->find($id)->products; // collection of Product models related to given Activity
// but not necessarily related in any way to the User
$user->activities->find($id)->products()->wherePivot('user_id', $user->id)->get();
// collection of Product models related to both Activity and User
You can simplify working with such relation by setting up custom Pivot model, helper relation for the last line etc.
For attaching the easiest way should be passing the 3rd key as a parameter like this:
$user->activities()->attach($activityIdOrModel, ['product_id' => $productId]);
So it requires some additional code to make it perfect, but it's feasible.
The solution was found with some changes.
In the models relationships look like:
// User model
public function activities()
{
return $this->belongsToMany('Activity', 'user_activity_product', 'user_id', 'activity_id')->withPivot('product_id');
}
public function products()
{
return $this->belongsToMany('Product', 'user_activity_product', 'user_id', 'product_id')->withPivot('activity_id');
}
To update pivot table:
$user->products()->save($product, array('activity_id' => $activity->id));
- where product and activity ids I get from Input.
And, for example, to check if "user -> some activity -> some product is already exists":
if ($company->activities->find($activity_id)->products()->where('product_id', '=', $product_id)->wherePivot('company_id', $company_id)->get()->count() > 0) {
// code...
}
I think it needs improvements but it works for me now.

Resources