Laravel detaching HasManyThrough relationships with ease - laravel

SCENARIO
Let's say that there's a system where companies could add other companies to their list of favorites. To fetch this record I would do something like the following:
// Returns a list of favorite companies for the current user's company
$user->company()->favorites;
Note that the favorites represent the hasManyThrough relationship.
public function favorites()
{
return $this->hasManyThrough(Company::class, CompanyFavorite::class, 'company_id_owner', 'id', 'id', 'company_id_selected');
}
IDEAL SOLUTION I'm looking for a solution where I could remove the record from the intermediary relationship table making use of the hasManyThrough object. Example:
$user->company()->favorites()->detach([...companies_ids]);

HasManyThrough is not the right relationship here, use BelongsToMany instead:
public function favorites()
{
return $this->belongsToMany(
Company::class, 'company_favorites', 'company_id_owner', 'company_id_selected'
);
}
This relationship provides all the necessary methods like attach() and detach():
$user->company()->favorites()->detach([...companies_ids]);

Related

Eloquent Relationship on the same model

I am using the User model and want to reference other users on a One to Many relationship.
With two models, this would be done by a Many to Many but this attempt at it is obviously wrong:
public function relatedUsers()
{
return $this->belongsToMany(User::class, 'related_user', 'user_id', 'user_id');
}
Is there a better way I can achieve my goal? I don't need an inverse method.
You can use hasMany() or belongsTo() (according to your need) relation for same model relationship
Define hasMany relationship in User model:
public function relatedUsers() {
return $this->hasMany('User','user_id');
}
Example:
Consider you have one User object
$user = User::where('id',$id)->first();
If you want to access related records
$related_users = $user->relatedUsers; // this will return all related users for particular object

Custom hasMany relationship query with select()

Each company on my app can store its own events.
A company and its events are related though the following hasMany relationship:
/**
* The events that belong to the company.
*/
public function events()
{
return $this->hasMany('App\Event');
}
To list a company's events, I use:
Auth::user()->company->events
Since each event stores, a lot of data that I don't need when I query the hasMany, I would like to customize the relationship to only select the id and name columns instead of the whole row.
My hasMany relationship query would be something like
DB::table('events')->where('company_id', Auth::id())->select('id', 'name')->get();
How do I take the collection returned from this result and use it as the query for my hasMany relationship, which will then correctly return a collection of event instances?
Add the filter inside your events method like :
public function events()
{
return $this->hasMany('App\Event')->select('id', 'company_id', 'name');
}
Then call it like:
Auth::user()->company->events
Important update: 'company_id' is necessary for company->events link, otherwise 'events' relationship always returns empty array
How about
Auth::user()->company()->with('events:id,name')->get();
https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
Go to Eager Loading Specific Columns

Laravel relationships with three tables

I am busy building a online ordering system, but now I am stuck with this one table relationship.
I want the User to be able to create many orders, and the order will have many products.
I have looked at many-to-many relationships, however it does not include a third table.
The basic many-to-many relationship releases by pivot table and declares in laravel like belongsToMany
In your situation you can use hasManyThgough relationship to access all products which are connected with user through orders
User.php
public function orders() {
return $this->hasMany('App\Order');
}
public function products() {
return $this->hasManyThrough('App\Product', 'App\Order');
}
Order.php
public function products() {
return $this->hasMany('App\Product');
}
Link to laravel relationship hasManyThrough documentation

Eloquent hasOne vs belongsTo degenerate to same function if both keys are specified?

For the case of a one-to-one relationship, if I fully specify the keys in the method calls, is there a difference between hasOne and belongsTo relationships? Or, asked differently, if I used hasOne on both sides of the relation, would it be the same result?
Yes it works for some cases to specify the keys and make the relation work. And with some cases I mean mainly retrieving results. Here's an example:
DB
users profiles
----- --------
id id
etc... user_id
etc...
Models
Using "wrong" relations with hasOne twice
class User extends Eloquent {
public function profile(){
return $this->hasOne('Profile');
}
}
class Profile extends Eloquent {
public function user(){
return $this->hasOne('User', 'id', 'user_id');
}
}
Queries
Let's say we wanted to get the user from a certain profile
$profile = Profile::find(1);
$user = $profile->user;
This is working. But it's not working how it's supposed to be. It will treat the primary key of users like a foreign key that references user_id in profiles.
And while this may work you will get in trouble when using more complicated relationship methods.
For example associate:
$user = User::find(1);
$profile = Profile::find(2);
$profile->user()->associate($user);
$profile->save();
The call will throw an exception because HasOne doesn't have the method associate. (BelongsTo has it)
Conclusion
Whereas belongsTo and hasOne may behave similar in some situations. They are clearly not. More complex interactions with the relationship won't work and it's nonsense from a semantic point of view.

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