# Handling Controllers for API versioning

**URL:** <https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282>\
**Category:** Questions\
**Created:** [March 15, 2017, 7:50pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282 "2017-03-15T19:50:48Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dark](https://avatars.discourse-cdn.com/v4/letter/d/a88e4f/32.png) [@Dark](https://discourse.slimframework.com/u/Dark)\
**Post date:** [March 15, 2017, 7:50pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/1 "2017-03-15T19:50:48Z")

</div>

I have found answers on how to handle API versioning rouites, simply by using groups like ‘v1’, ‘v2’, …, ‘vx’ etc. like explained there: [Slim 3 API Versioning](http://discourse.slimframework.com/t/slim-3-api-versioning/293)

But what is the proper way to handle Controllers or other version dependand code in Slim? Should i create all Controllers from scratch for new version of API in namespace of new API version?

For example i have structure like:

src/Controllers/api/v1/UsersController.php

When i release v2 is there any other way than creating new Controller for this version like:

src/Controllers/api/v2/UsersController.php

This way code becomes hard to maintain especially when bugfix applies somehow to all versions. What is the proper way you handle versioning of code for new API versions?

---

<div class="post-metadata">

**Author:** ![eko3alpha](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/eko3alpha/32/350_2.png) [@eko3alpha](https://discourse.slimframework.com/u/eko3alpha)\
**Post date:** [March 17, 2017, 2:49pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/2 "2017-03-17T14:49:46Z")

</div>

I use [Class:Method container resolution](https://www.slimframework.com/docs/objects/router.html#container-resolution) so its fairly trivial. This is what I’ve done with my projects with great success.

I have the following directory structure, each class is properly namespaced as well.

```
/controllers/api/v1/UserController.php
/controllers/api/v2/UserController.php

```

I also have a routes.php configuration file that looks something like this

```
$app->group('/api', function() {
    $app->group('/v1', function() {
        $this->get('/user/{id:\d+}/', 'Controller\Api\V1\UserController:get');
        $this->post('/user/', 'Controller\Api\V1\UserController:post');
    }
    $app->group('/v2', function() {
       $this->get('/user/{id:\d+}/', 'Controller\Api\V2\UserController:get');
    }
}

```

You could extend your original controller and change the implementation of old methods, or create new ones.

```
#/controllers/api/v2/UserController.php

namespace Controller\Api\V2

# use the previous version of this class
use Controller\Api\V1\UserController as UserControllerV1

# extend from previous version
class UserController extends UserControllerV1
{
    public function get($req, $res, $args)
    {
        # New implementation here
    }
}

```

With your new api endpoint you can add new methods, alter existing implementation while still inheriting all of V1’s methods. You can continue this inheritance chain to as many versions as you want.

---

<div class="post-metadata">

**Author:** ![wreade](https://avatars.discourse-cdn.com/v4/letter/w/278dde/32.png) [@wreade](https://discourse.slimframework.com/u/wreade)\
**Post date:** [May 11, 2017, 7:21pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/3 "2017-05-11T19:21:37Z")

</div>

@eko3alpha: Thanks for your response to this post. I have some follow-up questions.

The [Class:Method container resoltuion](https://www.slimframework.com/docs/objects/router.html#container-resolution) seems to indicate that I don’t need to create a factory in the container that instantiates the controller with the dependencies if the class does not have an entry int he container. In such a case, Slim will pass the container’s instance to the constructor. I can’t seem to get the code to work, though.

Here’s my directory structure.

```
<root dir>/src/controllers/v2/LoanController.php
<root dir>/includes/loan.php

```

I have this in the loan.php file.

```
<?php

	use \Psr\Http\Message\ServerRequestInterface as Request;
	use \Psr\Http\Message\ResponseInterface as Response;

	$app->group('/api', function () use ($app) {
		$app->group('/2.0', function() use ($app) {
			$app->get('/loan/{clientnumber}/{loannumber}', 'Controller\V2\LoanController:getClientLoan');
});
	});

?>

```

I have this in my LoanController.php file.

```
<?php

namespace Controller\V2;

Class LoanController
{
	protected $container;

	// constructor receives container instance
	public function __construct(ContainerInterface $container) {
		$this->container = $container;
	}

	public function getClientLoan(Request $request, Response $response, $args) {
}
	}

?>

```

Unfortunately, I am getting a Slim Application Error of “Callable Controller\V2\LoanController does not exist.” I think I am missing something small, but I just can’t figure it out. Any help is greatly appreciated.

Thanks!

---

<div class="post-metadata">

**Author:** ![eko3alpha](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/eko3alpha/32/350_2.png) [@eko3alpha](https://discourse.slimframework.com/u/eko3alpha)\
**Post date:** [May 12, 2017, 7:09pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/4 "2017-05-12T19:09:56Z")

</div>

I’m assuming that you are using composer, what does your composer.json file look like? Did you dump autoload?

---

<div class="post-metadata">

**Author:** ![wreade](https://avatars.discourse-cdn.com/v4/letter/w/278dde/32.png) [@wreade](https://discourse.slimframework.com/u/wreade)\
**Post date:** [May 12, 2017, 7:29pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/5 "2017-05-12T19:29:04Z")

</div>

@eko3alpha: Here is what I have in my composer.json file.

```
{
    "require": {
        "slim/slim": "^3.0",
        "monolog/monolog": "^1.18"
    }
}

```

I’m not familiar with “dump autoload.” Can you tell me more about it?

---

<div class="post-metadata">

**Author:** ![tflight](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/tflight/32/1205_2.png) [@tflight](https://discourse.slimframework.com/u/tflight)\
**Post date:** [May 13, 2017, 11:46am UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/6 "2017-05-13T11:46:15Z")

</div>

From the command line  
`composer dump-autoload -o`

---

<div class="post-metadata">

**Author:** ![eko3alpha](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/eko3alpha/32/350_2.png) [@eko3alpha](https://discourse.slimframework.com/u/eko3alpha)\
**Post date:** [May 14, 2017, 6:01pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/7 "2017-05-14T18:01:45Z")

</div>

You will need to define [PSR4 namespace mapping](https://getcomposer.org/doc/04-schema.md#psr-4) so the PHP autoloader can locate your custom classes. If you look inside your vendor/composer directory you will notice a bunch of autoload\_\* files. These are created automagically for you when you install/update/dump your composer dependencies. This is where and how your app is able to locate all the packages via name spacing, instead of manually calling “require/include” on every file.

Based on your class locations your PSR4 mapping should look something like this:

```
{
    "require": {
        "slim/slim": "^3.0",
        "monolog/monolog": "^1.18"
    },
    "autoload": {
        "psr-4": {
            "Controller\\": "/src/controllers/",
        }
    }
}

```

then make sure to [dump autoload](https://getcomposer.org/doc/03-cli.md#dump-autoload) so composer can generate new class mappings and your application will be able to find your new classes.

Here are some more resources for you to look into:  
[PSR4](http://www.php-fig.org/psr/psr-4/)  
[SPL Autoloading](https://secure.php.net/manual/en/language.oop5.autoload.php) \<—what composer defines for you  
[Brief PSR4 intro](https://mattstauffer.co/blog/a-brief-introduction-to-php-namespacing)

---

<div class="post-metadata">

**Author:** ![wreade](https://avatars.discourse-cdn.com/v4/letter/w/278dde/32.png) [@wreade](https://discourse.slimframework.com/u/wreade)\
**Post date:** [May 15, 2017, 1:51pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/8 "2017-05-15T13:51:26Z")

</div>

@tflight and @eko3alpha: Thanks for your responses! I have made those changes, but I am still getting the same message. Does composer produce some files that I can look at? Is there something else I am missing?

---

<div class="post-metadata">

**Author:** ![tflight](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/tflight/32/1205_2.png) [@tflight](https://discourse.slimframework.com/u/tflight)\
**Post date:** [May 15, 2017, 5:58pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/9 "2017-05-15T17:58:12Z")

</div>

in `vendor/composer/` you should see `autoload_classmap.php` as one example.

---

<div class="post-metadata">

**Author:** ![eko3alpha](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/eko3alpha/32/350_2.png) [@eko3alpha](https://discourse.slimframework.com/u/eko3alpha)\
**Post date:** [May 15, 2017, 11:57pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/10 "2017-05-15T23:57:43Z")

</div>

I noticed a discrepancy in your first post outlining your structure as

```
src/Controllers/api/v1/

```

vs later as the following

```
/src/controllers/v2/

```

Make sure you have the correct path and that the namespace matches the directory path.

your mapping should look like:

```
"Controller\\V2\\": "/src/controllers/v2",

```

or

```
"Controller\\V2\\": "/src/controllers/api/v2",

```

You’re using custom namespace mappings so check out the following file

```
/vendor/composer/autoload_psr4.php

```

You should see something that looks like the following, this is what mine looks like after I run “composer dump-autoload”

```
return array(
    'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
    'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
    'Slim\\' => array($vendorDir . '/slim/slim/Slim'),
    'Service\\' => array($baseDir . '/app/services'),
    'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
    'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
    'Model\\Store\\' => array($baseDir . '/app/models/stores'),
    'Model\\Repository\\' => array($baseDir . '/app/models/repositories'),
    'Model\\Entity\\' => array($baseDir . '/app/models/entities'),
    'Middleware\\' => array($baseDir . '/app/middleware'),
    'Library\\' => array($baseDir . '/app/libraries'),
    'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'),
    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
    'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
    'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
    'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
    'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'),
    'Core\\' => array($baseDir . '/app/core'),
    'Controller\\Api\\V1\\' => array($baseDir . '/app/controllers/api/v1'),
    'Controller\\' => array($baseDir . '/app/controllers'),
);

```

You should see your mapping. It should look something like

```
    'Controller\\Api\\V2\\' => array($baseDir . '/src/controllers/v2'),

```

The error you are getting is telling you your mapping is wrong and it cant find your class. Double check your directory path, namespace and make sure you “dump auto-load” after editing your composer.json

---

<div class="post-metadata">

**Author:** ![wreade](https://avatars.discourse-cdn.com/v4/letter/w/278dde/32.png) [@wreade](https://discourse.slimframework.com/u/wreade)\
**Post date:** [May 16, 2017, 4:37pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/11 "2017-05-16T16:37:15Z")

</div>

@tflightand and @eko3alpha: You guys rock! Both of your comments helped.

@eko3alpha: You are correct that I had a discrepancy. That fixed my issue with the error.

@tflight and @eko3alpha: Looking at the files you mentioned, I can see my controller listed correctly in each.

So, my next question is this: how does the controller work with the container? I’m new to all of this, so if you want to answer me by directing me to a tutorial, I will happily take that answer. 🙂 I read through _Routing_ section of the _User Guide_ on the Slim Framework site, but I’m still not understanding it.

To give a practical example: my index.php creates a configuration array where it stores database connection information. The same file adds that configuration array to a \Slim\App object. How do I now access this information in the controller? Do I access it through the container? (Obviously, if the way in which I am doing it is not the best practice, please feel free to say that and correct me.)

---

<div class="post-metadata">

**Author:** ![wreade](https://avatars.discourse-cdn.com/v4/letter/w/278dde/32.png) [@wreade](https://discourse.slimframework.com/u/wreade)\
**Post date:** [May 16, 2017, 8:08pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/12 "2017-05-16T20:08:00Z")

</div>

I think I finally figured this out.

Within my LoanController class, I added:

```
protected $container;

public function __construct($container) {
    $this->container = $container;
}

```

In the loan.php file I put this after the _use_ lines and before the `$app->group` lines.

```
$container['LoanController'] = function($c) {
	return new \Controller\V2\LoanController($c);
};

```

I can now see the data in the `$container` using `$this->container` in any function in my class.

Thoughts?

---

<div class="post-metadata">

**Author:** ![tflight](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/tflight/32/1205_2.png) [@tflight](https://discourse.slimframework.com/u/tflight)\
**Post date:** [May 16, 2017, 8:15pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/13 "2017-05-16T20:15:51Z")

</div>

You can do it that way, however that isn’t considered best practice as you are using your container as a _service locator_. The best practice would be to use _dependency injection_ and give your controller the pieces it needs in the container rather than the controller asking for things out of the container.

See [Accessing Services in Slim 3](https://akrabat.com/accessing-services-in-slim-3/) and [DI Factories for Slim Controllers](https://akrabat.com/di-factories-for-slim-controllers/) for more discussion on the topic. For bonus reading there is also [the Add Dependencies section](https://www.slimframework.com/docs/tutorial/first-app.html#add-dependencies) of the First Application Walkthrough as well as [Registering a Controller with the Container](https://www.slimframework.com/docs/objects/router.html#registering-a-controller-with-the-container), both in the Slim docs.

---

<div class="post-metadata">

**Author:** ![wreade](https://avatars.discourse-cdn.com/v4/letter/w/278dde/32.png) [@wreade](https://discourse.slimframework.com/u/wreade)\
**Post date:** [May 16, 2017, 8:27pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/14 "2017-05-16T20:27:28Z")

</div>

@tflight: Thanks! Although I got it working, I would much rather use best practices. I will work through all your suggestions and see if I can get it to work using _dependency injection_. You will likely hear from me here either way. 🙂

---

<div class="post-metadata">

**Author:** ![eko3alpha](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.slimframework.com/eko3alpha/32/350_2.png) [@eko3alpha](https://discourse.slimframework.com/u/eko3alpha)\
**Post date:** [May 16, 2017, 8:32pm UTC](https://discourse.slimframework.com/t/handling-controllers-for-api-versioning/1282/15 "2017-05-16T20:32:57Z")

</div>

There are several ways you could do it. I would recommend that you don’t inject the container. Instead create an abstract BaseController for your common dependencies, or create a service and pass your dependencies to the constructor. I’ll show you both ways. This gets very opinionated depending on who you ask…

Also check out this [Slim Guide](http://jgrundner.com/slim-oo-001-getting-started-with-slim/) on one of the many ways to setup a Slim project.

The route can be the same for both methods

```
$app->get('/tags/', 'Controller\Tag\TagController:doSomething')->setName('tags');

```

Here is an example of extending a controller, this is good if you have a ton of controllers within a group that need the same dependencies.

### /controllers/tags/BaseClass.php

```
namespace Controller\Tag;

abstract class BaseTagController
{
    protected $input;
    protected $tagRepo;
    protected $view;
    protected $logger;

    public function __construct($c)
    {
        $this->input = $c['Input'];
        $this->tagRepo = $c['TagRepository'];
        $this->view = $c['View'];
        $this->logger = $c['Logger'];
    }
}

```

### /controllers/tags/TagController.php

```
namespace Controller\Tag;

class TagController extends BaseTagController
{
    public function doSomething(Request $request, Response $response, array $args)
    {

        $tags = $this->tagRepo->getById($this->input->get('id'));
        return $this->view->render('tags/listing', $tags);
    }
}

```

This is an example of using the container as a controller.

###/app/services/tag\_controllers.php  
$c[‘Controller\Tag\TagController’] = function($c){  
return new Controller\Tag\TagController(  
$c[‘Input’],  
$c[‘View’],  
$c[‘TagRepository’],  
$c[‘Logger’]  
);  
}

###/app/controllers/tag/TagController.php  
namespace Controller\Tag;

```
class TagController
{
    protected $input;
    protected $tagRepo;
    protected $view;
    protected $logger;

    public function __construct($input, $view, $tagRepo, $logger)
    {
        $this->input = $input
        $this->view = $view;
        $this->tagRepo = $tagRepo;
        $this->logger = $logger;
    }

    public function doSomething(Request $request, Response $response, array $args)
    {
        $tags = $this->tagRepo->getById($this->input->get('id'));
        return $this->view->render('tags/listing', $tags);
    }
}
```
