I have two models.
A "Vehicle" and a "Tenant".
They have following relationships with each other.
A Tenant hasMany vehicles. A vehicle belongsTo a single Tenant.
For Tenant.php:
public function vehicles()
{
return $this->hasMany('\App\Models\Vehicle');
}
For Vehicle.php:
public function tenant()
{
return $this->belongsTo('\App\Models\Tenant');
}
Executing this :
$this->user = $request->user();
$userTenant = $this->user->tenant();
$vehicle= $userTenant->vehicles()->first();
results in an error
Call to undefined method Illuminate\Database\Query\Builder::vehicles()
Pointing to this line :
$vehicle= $userTenant->vehicles()->first();
I am not so sure why is this happening =\
I can't see from your post what the relations are with a User, but the tenant() (with parentheses) probably returns a BelongsTo or other Relation instance that is being assigned to $userTenant. Try changing that line to a version without parentheses after tenant to get the Tenant Model instance instead:
$userTenant = $this->user->tenant;
Update from comments
when you call a relation as method, e.g.
$myModel->relation()
you get the corresponding relation class. When used as a getter, e.g.
$myModel->relation
it's essentially the same thing as calling
$myModel->relation()->get() for relations that target multiple models, or calling
$myModel->relation()->first() for relations that target a single model.
Checkout the docs for more info on relationship methods vs. dynamic properties
Related
I'm trying to use a HasMany relation in a HasOne.
I have following Models:
class Auction extends Model
{
//...
public function bids(): HasMany
{
return $this->hasMany(Bid::class, 'auction_id');
}
public function approvedBids(): HasMany
{
return $this->bids()->approved();
}
public function topBids(): HasMany
{
return $this->approvedBids()->orderByDesc('price')->take(10);
}
public function topBid(): HasOne
{
//return $this->topBids()->firstOfMany(); // Not Working
//return $this->hasOne(Bid:class, 'auction_id)->ofMany('price','max')->approved(); // not working
//return $this->hasOne(Bid:class, 'auction_id)->approved()->ofMany('price','max'); // not working
//return $this->hasOne(Bid::class, 'auction_id')->ofMany('price', 'max'); // working but not as I expecting
}
}
class Bid extends Model
{
//...
public function scopeApproved(Builder $query): Builder
{
return $query->where('state', BidState::STATE_APPROVED);
}
//...
}
As you can see in the source, I'm looking for a way to make a relation that retrieve the Top Bid (ONE BID) from topBids() relation, but I don't know how, and none of my approaches works:
$this->topBids()->firstOfMany(); // Not Working
$this->hasOne(Bid:class, 'auction_id')->ofMany('price','max')->approved(); // not working
$this->hasOne(Bid:class, 'auction_id')->approved()->ofMany('price','max'); // not working
Unfortunately these shouldn't be a relationships
Real question is why are you trying to make these relationships?
Usually you should be using relationships on model to describe how they are correlating together within the database, the rest of the things you should be defining as a scope on a query or a model, or as an attribute.
So, what I'm trying to say is this:
Keep bids as a relationship, as that is actually a relationship to the Bid model
Update approvedBids to be a scope (or an attribute)
Update topBids to be a scope (or an attribute)
Then, you will be able to find top bid easily by doing something like this:
$this->topBids->first() -> if it is an attribute
$this->topBids()->first() -> if it is a scope
This is how you can create a scope: https://laravel.com/docs/9.x/eloquent#local-scopes
In the end, you can even create an attribute that will allow you to retrieve topBid like this:
public function getTopBidAttribute(){
$this->bids()->approved()->orderByDesc('offered_token_price')->first();
}
Then later you can just do $this->topBid.
I think I've found the solution
public function topBid(): HasOne
{
return $this->hasOne(Bid::class, 'auction_id')
->approved()
->orderByDesc('price');
}
You see the problem was in ofMany() function, which creates a huge SQL and I don't know why!
I've returned a HasOne object here, which supports all kinds of query manipulations. Basically HasOne class, tells the main query, to:
Retrieve the first record of the query I've provided.
So if we use orderBy it only provides an order for HasOne's query. and the main query will take cares of the rest and selects the first record.
I have a problem with how to get the method name that was used in other models.
There is a list of models and some of them have used different method name on its relationship to other models.
For example, I have a model name of Member Rate Detail wherein it belongs to Member Rate model. The method that connects from Member Rate Detail to Member Rate is head() method.
Here is the sample code for head() method:
public function head()
{
return $this->belongsTo(MemberRate::class, 'member_rate_head_id')->withTrashed();
}
And for Customer Detail model it belongs to Customer model. And the connector method name that was used is group()
Here is the sample code for group():
public function group()
{
return $this->belongsTo(Customer::class, 'head_id', 'id')->withTrashed();
}
So the problem is I don't know if this model is using head() or group() or another method name.
Is there a Laravel Relationship concept way which can get a list or an array type of its foreign key's method used?
I'm expected to get the method name so that I can direct it to its instance class.
For example:
$memberRateDetail->getForeignMethod()->created_by;
**OR**
$customerDetail->getForeignMethod()->created_by;
Thank you so much!!!
No, there are not unless you declare them yourself.
Let's say you declare a method in your all your models where you declare the method for the class foreign relation. You can also implement it as an interface.
You can also just use code hinting if you have a good IDE, declaring a relation in PHP docs as * #property Collection head can help too.
When I use belongsTo without default keys it won't connect..
My users table has user_id which is some rand and unique string that presents that user. My urls table has user_id and uri columns where user_id contains users table user_id.
In Url model I have:
public function user() {
return $this->belongsTo('App\User','user_id','user_id');
}
In User model I have:
public function uri() {
return $this->hasOne('App\Url', 'user_id', 'user_id')->first()->uri;
}
By using $user->uri() I get uri from urls table connected with user_id.
But when I use $url->user() I get return null or BelongsTo class inside laravel tinker.
Anyone know why?
Your User::uri() method is not a relationship method.
You are utilizing a relationship method inside of it, but you are querying it for the first result and returning the uri property.
Your Url::user() method IS a relationship method because you are actually returning a relationship (BelongsTo) instance. Eloquent relationships are used as follows:
// To get a related entity on a BelongsTo relationship, you access it as a property:
$url->user
// To query a relationship, you use it as a method:
$url->user()->where(...)->first();
Edit:
I dont think its the same issue as:
https://stackoverflow.com/questions/40022929/laravel-relationship-error-undefined-property-illuminate-database-eloquent-col
because in that issue hasMany relationship is used which returns an array but i used belongsTo which should return a certain object.
I have a database structure where i have a many to many relationship between users and companies table. For that i have a crossreference table company_user. Also each user has a certain role in a company so the crossreference table also has a role_id. The problem is that for some reason i get an exception when i try retrieve the role from the crossreference table.
Here is how i defined the relationship in Company model:
public function users() {
return $this->belongsToMany('App\User')->withPivot('role_id')->using('App\CompanyUser');
}
Now if i just try to get role_id from pivot everything works fine:
#foreach($company->users as $user)
{{$user->pivot->role_id}} // this displays correct role_id
#endforeach
But i also need the data of the role so i defined a relationship in my custom pivot. Here is the whole model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CompanyUser extends Pivot
{
public function role()
{
return $this->belongsTo('App\Role');
}
}
And i tried to access it like this:
#foreach($company->users as $user)
{{$user->pivot->role()->id}}
#endforeach
But this gives me an exception:
Undefined property: Illuminate\Database\Eloquent\Relations\BelongsTo::$id
What am I missing?
try changing to
#foreach($company->users as $user)
{{$user->pivot->role->id}}
#endforeach
The exception itself gives you your answer.
Undefined property: Illuminate\Database\Eloquent\Relations\BelongsTo::$id
It's telling you you're trying to access the $id property on the belongs to builder instance and not the actual related model.
The difference is small but is worth understanding as it will make your laravel life much happier.
Access the relation builder: $user->role()
This will directly call the role() method and return exactly what you see in your definition. Accessing this method is handy for aggregate functions - things like getting the count of related records for HasMany or BelongsToMany relations. eg: $user->role()->count().
Access the related record: $user->role
This will actually retrieve the related record from the database and hydrate it for you, thus giving you the power of your related eloquent model and access to it's columns as properties. :D
You can see why/how this works by diving into Laravel's source and checking out the Illuminate\Database\Eloquent\Model class. Specifically the getAttribute() method.
I have model called Organisation, which has a many to one relationship with a model called Client, it looks like this,
public function clients() {
return $this->hasMany('Client', 'owner_id')->orderBy('name', 'asc');
}
Client has a one to many relationship with projects that looks like this,
public function projects()
{
return $this->hasMany('Project');
}
The project model / table as a column called total_cost within an organisation I can gets it's clients, and then the clients projects, what I wanting to do is get the value of the client by aggregating all the clients projects total_cost entries, I am trying to do this with the following,
public function clientsValue() {
return $this->clients()->projects()->select(DB::raw("SUM(total_cost) as client_value"));
}
In my mind this getting the projects through the clients relationship and then running a select on the project model, however I am getting the following error,
Call to undefined method Illuminate\Database\Query\Builder::projects()
But I am not sure why as the clients has a projects relationship.
$this->clients() returns relation definition - object of class HasMany, not a Client object. HasMany class does not have a projects() method, that's why you're getting an error.
In order to calculate total clients value, you first need to define additional relation between organization and projects:
public function projects() {
return $this->hasManyThrough('Project', 'Client');
}
This will tell Eloquent how to fetch projects for given organization using intermediate Client model.
Once you have the relation you can call aggregate functions on it:
public function clientsValue() {
return $this->projects()->sum('total_cost');
}
You can read more about aggregate functions here: http://laravel.com/docs/5.1/queries#aggregates
You can read more about hasManyThrough relations here: http://laravel.com/docs/5.1/eloquent-relationships#has-many-through