Q:

How to organise controllers for REST relationships? [Laravel routes]

Imagine entities Genre and Book.
Each have API resource endpoints /genre and /book. In Laravel routes that might be:
 $app->resource('/genre', GenreController::class);

I want an endpoint for the relationship. GET /genre/1/book, to get books under the Genre #1.
What is best practice here? Place the handlers in GenreController, BookController or maybe a whole new controller?
On a sidenote, I am using the dingo-api package, but I don't suppose that makes any difference.

A:

So @Chris suggested a dedicated controller for the relationship, and @RossWilson has a genius way to re-use a controller for the relationship (at least for actions that load Book).
Unfortunately Lumen's RouteProvider returns a simple array, and as such does not have the convenience of $request->route($param) and more importantly, $request->route()->forgetParameter($param).
=== New solution ===
I ended up doing basically exactly the same as @RossWilson suggested, just in a way that was supported by Lumen. Rather than getting the Route parameter in the Controller's __construct, I made a middleware that moved the Route parameter onto both the Request's input and query arrays. 
The Middleware looks something like this:
public function handle($request, $next)
{
    if ($genre_id = Arr::get($request->route()[2], 'genre')) {
        // Add 'genre_id' to the input array (not replacing it if it already exists).
        $request->merge(['genre_id' => $request->input('genre_id', $genre_id)]);
        // Add 'genre_id' to the query array.
        $request->query->add('genre_id', $genre_id);

        // Forget the route parameter
        // Has to be done manually, because Lumen...
        $route = $request->route();
        $request->setRouteProvider(function() use ($route) {
            Arr::forget($route[2], 'genre');
            return $route;
        });
    }

    // Pass the updated $request to $next.
    return $next($request);
}

In my implementation I only set the query parameters for GET and DELETE requests, and input parameters for POST and PUT.
Then you can re-use the BookController for the genre.book resource, filtering from $request->query('genre_id') and associating the relationship from $request->input('genre_id').
=== Original solution ===
Instead I ended up with a with a dedicated relationship controller GenreBookController, that inherits from the non-relationship controller BookController. It's not as elegant as it could be, because of the method declarations that need to match (see how $book_id = null below to work-around this), but it is quite slim and dry.
GenreBookController extends BookController:
protected function addGlobalScope($genre_id)
{
    // Thanks Ross Wilson for the global scope suggestion.
    Book::addGlobalScope('genreScope', function ($query) use ($genre_id) {
        $query->where('genre_id', $genre_id);
    });
}

public function show(Request $request, $genre_id, $book_id = null)
{
    $this->addGlobalScope($genre_id);
    return parent::show($request, $book_id);
}

and for BookController, the show method is business as usual (it doesn't need to know about the extra parameter on show).
I also came up with a simple way for GenreBookController to pass through the genre parameter to the store method:
public function store(Request $request, $genre_id)
{
    // This way lets an input genre_id override the Route parameter.
    $request->merge(['genre_id' => $request->input('genre_id', $genre_id)]);

    // This way forces the Route parameter to be used over input parameters.
    $request->merge(['genre_id' => $genre_id]);

    return parent::store($request);
}

and again, for BookController it is business as usual, and it may of course make any validation/authorization for the genre passed through $request->input('genre_id'). That way there is no duplicated validation and authorization logic.
A note on FormRequests
If you are using FormRequests to validate the genre_id, the validation takes place before the GenreBookController can set the genre_id input variable from the route parameter.
As I see it you have two options: 

Use a middleware to move the route parameter onto the Request input.
Put it on the authorize method of your FormRequest (I haven't tested this, as I don't like to put such logic here).

Laravel: If you are not using Lumen, I suggest taking a look at @RossWilson's answer, it is a little cleaner in my opinion.

