Support the ongoing development of Laravel.io →
posted 10 years ago
Requests
Last updated 2 years ago.
0

I am actually looking for the exact same thing.

Last updated 2 years ago.
0
Last updated 2 years ago.
0

@zenry AFAIK, that won't work.

Route::filter('api.version', function ($route, $request, $value = null) {
    // Could also be something like:
    // if ($request->header('accept') === 'application/json') {
    if ($request->header('version', '1') === '2') {
        // Use alternate controller or method, say 'ResourceApiController'.
    }
});

Route::get('resource/{id}', ['uses' => 'ResourceController@show', 'before' => 'api.version']);

What I want is to be able to swap out the controller method (it could be on the same controller, or a different one). Otherwise, the route parameters stay the same. But redirecting from a filter requires the second route to exist, which means it needs a unique URL. In my case, I want to URL to stay the same. What needs to be possible for this to work is altering the route definition in the filter.

This is all easy enough to do when using closure-based routing, as you just have a conditional based on the header, and return what you return. But when using controller-based routing, there doesn't seem to be a way to alter the method called during the request. That's what I'm looking for.

(Why? Separation of concerns. My controller methods should just do what is necessary for the specific request. The problem is, on the web requests are not only unique based solely on URL, headers can and do alter requests...)

Last updated 2 years ago.
0

+1 on this.

I created the filter and it retrieves the version fine from the Accept header but how can I serve the correct version to the user?

Last updated 2 years ago.
0

Hello everyone. This is how I've done this in the past. I usually create an interface that defines the API I want to expose. For this example, lets just use this interface:

// In APIInterface.php
<?php

interface APIInterface {

    /**
     * Just says hello.
     * 
     * @return string
     */
    public function sayHello();

}

Then I can create two different versions of this API like so:

// In APIVersionOne.php
<?php

class APIVersionOne implements APIInterface {

    public function sayHello()
    {
        return 'Hello. I am API Version One.';
    }

}

And...

// In APIVersionTwo.php
<?php

class APIVersionTwo implements APIInterface {

    public function sayHello()
    {
        return 'Hello. I am API Version Two.';
    }

}

Then inside the controller, I usually have two functions, one called getVersion() and one called makeAPI(). The getVersion() function simply gets the API version number from the request headers, and the makeAPI() function just creates an instance of the API, either by newing-up something directly, or resolving an API from the IoC based on the version number.

Then, typically, the controller would contain the same methods as the API, or expose some way to call the methods of the API. The methods in the controller then wouldn't contain the logic themselves, but would rather pass the request onto an instance of the API and return the value, or take some other action.

An example controller might look like this:

// In controllers/APIController.php
<?php

class APIController extends BaseController {

    /**
     * The API implementation.
     * 
     * @var APIInterface
     */
    protected $api = null;

    /**
     * Returns the API version number based on headers.
     * 
     * @return string String of version number. Default '1'.
     */
    private function getVersion()
    {
        return Request::header('api_version', '1');
    }

    /**
     * Makes an instance of the API.
     * 
     * @return APIInterface The APIInterface implementation.
     */
    private function makeAPI()
    {
        switch($this->getVersion())
        {
            case '2':
                $this->api = new APIVersionTwo;
                break;
            default:
            
                // I put this in the default section, so not specifying an API
                // version number will default to version one. Sometimes, though,
                // you might want to handle each version explicitly and then
                // throw an error in the default section stating that the API
                // version is not supported.
                $this->api = new APIVersionOne;
                break;
        }
    }

    /**
     * Creates a new instance of the APIController
     *
     * @return APIController
     */
    public function __construct()
    {
        // Make sure we make an instance of the API!
        $this->makeAPI();
    }

    /**
     * Just says hello.
     * 
     * @return string
     */
    public function getSayHello()
    {
        return $this->api->sayHello();
    }

}

This way you can still use just one Route, like this

<?php

Route::controller('api', 'APIController');

and still have your controller change it's behavior based on the request headers.

Using this method, if I send the header api_version with a value of 1 (or don't send it all, since 1 is the default) I get the following response when I hit the route in the URL:

Hello. I am API Version One.

When I send the header api_version with a value of 2 I get this response:

Hello. I am API Version Two.

And if you wanted to add a third version at some other time, or change a version's implementation, just update your makeAPI() method, or adjust the IoC bindings, or however you decided to resolve your API implementations.

I hope this helps!

Last updated 2 years ago.
0

Sign in to participate in this thread!

Eventy

Your banner here too?

Moderators

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.