getting model belongsTo attributes

17,843

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:

  • If you want an actual property (i.e. anything defined on the table) or the results of a relationship query, use property access
  • If you want anything else (i.e. behaviour you're defined using a method) you must call the method directly

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 Coupons 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 batch

Hopefully 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.

Share:
17,843

Related videos on Youtube

Zayin Krige
Author by

Zayin Krige

Hello! I'm Zayin I have a love for development and have been coding for fun and professionally for 33 years. I started out with GWBasic(3 years) and have worked in Turbo Pascal (5 Years), ASM (2 Years), Delphi (7 Years), C/C++ (4 Years), Java (10 years), Kotlin (3 Years), Swift (5 Years), Objective-C (9 Years), Javascript (5 Years), PHP (6 Years), Python (2 Years) and PERL (2 Years) ​ I have built numerous mobile apps for both iOS and Android, as well as cross platform apps using React Native. I have built NodeJs backends hosted on AWS, and PHP/Laravel backends which are self hosted. ​ I have a wide range of experience across many different development related fields and bring a vast depth of systems knowledge with me. ​ I have built systems using Interbase, Firebird Database, SQL Server, MySQL, CouchDB and DynamoDB on AWS. ​ I owned and sold a point of sale company that had installations across Southern Africa. ​ I co-founded a vending (TopItUp) startup in 2009 and sold my shares in 2013

Updated on September 14, 2022

Comments

  • Zayin Krige
    Zayin Krige over 1 year
    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?

  • Zayin Krige
    Zayin Krige about 10 years
    strangely enough, $this->batch->price also didnt work. I did try that option along the way. I think @alexrussel has nailed it
  • Zayin Krige
    Zayin Krige about 10 years
    thanks for the excellent explanation of the laravel magic. I assumed there was something like that happening behind the scenes, but I couldn't quite pin it down
  • alexrussell
    alexrussell about 10 years
    Yeah it's something that's not really documented. The collection versus query builder is mentioned in the Eloquent relationships docs, but not the fact that it can't be used for other things. Glad to be of help, anyway.
  • Richard Fu
    Richard Fu over 5 years
    I think you have a typo: ->first() or ->get()