Reputation: 3308
class Batch extends Eloquent {
public function coupons() {
return $this->hasMany('Coupon');
}
}
class Coupon extends Eloquent {
public function batch() {
return $this->belongsTo('Batch');
}
public function price() {
$batch = $this->batch;
return $batch->price;
}
}
$coupon->price
gives me this error:-
LogicException Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
However, $coupon->batch->price
works just fine.
What am I missing?
Upvotes: 3
Views: 24549
Reputation: 14202
Your issue here is that you define a non-relationship method price()
but you call it as if it was a relationship method (i.e. you call it as a property and not as a method).
The code you should be using to get the Coupon
's price is:
$coupon->price();
The reason the relationship thing works (i.e. $coupon->batch
over $coupon->batch()
) is that Laravel has some special logic in - basically it catches you trying to access a property (->batch
in this case) and checked to see if there's a corresponding method (->batch()
). If there is one, it calls it and expects it to return a relationship, and then it calls ->first()
of ->get()
on that relationship depending on whether it's a single-result or a multiple-result relationship.
So what's happening in your code here is that you call $coupon->price
and Laravel, behind the scenes, decides that being as there's a ->price()
method it must be a relationship. It calls the method, checks that it returns one of the Laravel relationship types, and when it doesn't, throws that LogicException
.
The general rules of thumb is this:
Also, sometimes there is a good reason to call a relationship as the method rather than the property - calling the method returns something you can add query builder constraints on to, whereas calling the property gets you all the results. So say Coupon
s can be enabled or disabled (for example), the following holds:
$batch->coupons
gets you all coupons that the batch has$batch->coupons()->whereEnabled(1)->get()
gets you all enabled coupons for a given batch$batch->coupons()->orderBy('order')->get()
gets you all the coupons that the batch has, ordered by a field called order
$coupon->batch
gets you the given coupon's batchHopefully that explains the difference between Eloquent's use of methods versus properties for relationships, and why all augmented behaviour (like price on coupon in your example, but not price on batch, which is inherent behaviour) must be called as a method.
Upvotes: 8
Reputation: 4285
Take a moment to realize what objects you actually have here.
When you call $this->batch;
you're no longer chaining the relationship queries - you've actually retrieved the information from the database already. In order to define that query you'd have to do it one of several ways including:
Coupon::with('batch.price')->get();
You could of course do it with relationships but it's late and I'm not sure where exactly Price belongs in the scheme of this since I don't see a method for it associated with batch. Presumably you could do:
public function price()
{
return $this->batch->price;
}
if Price is a derivative of Batch.
Upvotes: 0