Skip to content
Advertisement

Laravel: pass field to resolveRouteBinding from route definition

I have a seemingly stupid question here but I can’t find how one is supposed to define explicit model binding resolution logic using resolveRouteBinding method on a Model.

I managed to write down its logic which is based on the value of the field parameter but I can’t seem to work out how to pass a value to this parameter when I define the route.

Can anyone please help me out?

EDIT: The proper question is how to tell the framework what value to pass to the field parameter?

EDIT 2: Here’s an example of what I’m doing without luck.

Basically I want to implement a custom logic to retrieve a class representing a many-to-many relationship.

These are the master classes.

class Competition extends Model {
    // ...

    /**
     * Get the editions of the competition.
     */
    public function competitionSeasons() {
        return $this->hasMany(CompetitionSeason::class);
    }
}
class Season extends Model {
    // ...

    /**
     * Get the editions of the competitions in this season
     */
    public function competitionsSeasons() {
        return $this->hasMany(CompetitionSeason::class);
    }
}

This is the relationship class.

class CompetitionSeason extends Model {
    // ...

    /**
     * Get the competition of this competition edition.
     */
    public function competition() {
        return $this->belongsTo(Competition::class);
    }

    /**
     * Get the season of this competition edition.
     */
    public function season() {
        return $this->belongsTo(Season::class);
    }

    public function resolveRouteBinding($value, $field = null) {
        error_log("[resolveRouteBinding] Field: " . $field);

        // TODO: make the query
    }

    public function resolveChildRouteBinding($childType, $value, $field = null) {
        error_log("[resolveChildRouteBinding] Field: " . $field);

        // TODO: make the query
    }
}

What I want now is to have a CompetitionSeason injected in the controller as a nested resource under Season (and also under Competition later on).

So this is how the route has been defined.

// api.php

Route::apiResource('seasons.competitionSeasons', MockController::class)->scoped([
    'competitionSeason' => 'competition'
]);

Since I’m using slugs on the routes rather than IDs, competition here would be needed in any of the resolveBinding methods to navigate the relationship and get the slug from the Competition instance.

Basically, the request would be like this:

GET .../api/seasons/19-20/competitionSeasons/premier-league

where premier-league is the slug of the referenced Competition, the other end of the many-to-many relationship basically.

This is the Controller.. completely useless for now.

class MockController extends Controller {

    public function show(Season $season, CompetitionSeason $competitionSeason) {
        error_log($competitionSeason);
    }
}

I know how to implment the missing logic, I have it very clear, what I would like to see is the $field being printed when the framework attempts to resolve the required instance of CompetitionSeason so that I can get the slug of the associated resource as well as to know which relationship to navigate when CompetitionSeason will be a child resource of Competition too.

Advertisement

Answer

I find out how it is supposed to work even though I don’t know why it’s like this.. (some considerations below).

Basically the public function resolveChildRouteBinding I should override is the parent, so Season in this case.

So now Season looks like this:

class Season extends Model {
    // ...

    public function competitionsSeasons() {
        return $this->hasMany(CompetitionSeason::class);
    }


    public function resolveChildRouteBinding($childType, $value, $field = null) {
        error_log("[resolveChildRouteBinding] Field: " . $field);

        if ($childType == 'competitionSeason') {
            if ($field == 'competition' {
                // TODO: make the query
            }
        }
    }
}

and here I can see $field being competition with $value equal to premier-league.

Now some considerations: why should the parent class be the one that returns the instance of the child?

Basically, if I go on and implement it like this, the resolveChildRouteBinding will be the one to return the CompetitionSeason while I would have expected the resolution logic on CompetitionSeason itself to be what determines the instance to inject. Am I the only one?

Thank you to @lagbox for pointing out the existence of resolveChildRouteBinding which I completely ignored for whatever reason (maybe deserves a little more space in the docs?)

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement