skip to Main Content

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

2

Answers


  1. Chosen as BEST ANSWER

    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.


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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search