Custom hasMany relationship query with select() - laravel

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

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

Laravel getting data from a deep relationship

I have 3 tables:
Users
Baskets
Items
id
id
id
user_id
basket_id
price
I'm trying setup a relationship in eloquent by which I can get all the all the Items and the corresponding Baskets where the price of items is X. I want it so that I can simply use $user->items(x) and get the results.
I'm unsure if this can be done using Relationships alone or will I have to resort to writing custom queries.
Any, and all, help and guidance will be appreciated!
The relationship you are looking for is hasManyThrough
https://laravel.com/docs/7.x/eloquent-relationships#has-many-through
User Modal
public function items()
{
return $this->hasManyThrough(Item::class, Bucket::class);
}
The way you want to use is not possible to achieve I think.
Possible Usage
$user->items()->where('price', x);
of if you define custom scopes
Item Modal
public function scopeWherePrice($query, $value)
{
return $query->where('price', $value);
}
Usage
$user->items()->wherePrice(x);
EDIT
If you really want to write a code like $user->items(x) you can define a method on the User Modal.
Note that this is not a relationship, just another method which fetch the result.
User Modal
public function items($price)
{
return this->items()->where('price', $price)->get();
}
Using hasManyThrough Define the relationship in your models:
User Model
/**
* Get all of the items & baskets for the user.
*/
public function items($price)
{
return $this->hasManyThrough('App\Items', 'App\Baskets')
->with('basket')
->where('price',$price);
}
Basket Model
/**
* Get the Items's Basket.
*/
public function basket()
{
return $this->belongsTo('App\Basket','basket_id');
}
And then call it like this:
$user->items(x);
This will return all user items with their corresponding baskets in a specific price.

Laravel detaching HasManyThrough relationships with ease

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]);

Combine two eloquent relationships in a single Model and combine their data

I have multiple eloquent relationships set up through the backend in a laravel application. I need to sort of combine a few of them in a return and I'm not sure how to go about it. I'm considering a hasManyThrough relationship but I feel like since I get the data I need through other relational calls there is probably some way to combine the two. Anyways here are my call in my Quote model.
/**
* The items that belong to the quote.
*/
public function items()
{
return $this->belongsToMany('App\Item')->withPivot('waste', 'subtotal', 'required');
}
/**
* The categories that have been added to a quote.
*/
public function categories()
{
return $this->belongsToMany('App\Category')->withPivot('priority', 'expanded')->with('items');
}
What I'd like to do is somehow combine these two relationships so that categories() returns everythign that it does but it attaches the pivot table from the items method to the eager load items. I don't quite konw if I've explained it properly but I hope so.

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