We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Setter injection problem

Hi Guys,

I am using Phalcon 3.2 (php 7.0.2). I created an AbstractController for all my controllers to collect similar codes into one place. I would like to inject a service to the AbstractController, so it will be available to each other ones. I am using setter injection, but my method never gets called. (My controllers are not in any namespace. I debugged it, and ImgStorage is in the DI)

Any help will be appreciated:

Here is a codesnippet

edited Nov '17

I'm not sure what kind of pattern you're trying to acheive...

This is fine, creates a global service by the name of ImgStorage:

$di->set('ImgStorage',
    [
        'className' => 'Gallery\Classes\AmazonS3'
    ]
);

You can acces it like this:

// controller context
$this->ImgStorage->someMethodOfAmazonS3();

This will be your cuplrit, you shouldn't create a controller class as a service. Even though it will be instantiated, that instance will be a different from what the Phalcon dispatcher will create.

$di->set('AbstractController',
    [
        'className' => 'AbstractController',
        'calls' => [
            [
                'method'    => 'setStorage',
                'arguments' => [
                    [
                        'type'  => 'service',
                        'name'  => 'ImgStorage'
                    ]
                ]
            ]
        ]
    ]
);

For this functionality (as i understand), you don't have to subclass an abstract controller, you can access the ImgStorage service from any controller:

// AbstractController.php:
abstract class AbstractController extends Controller { 
    // ???
}

// IndexController.php:
class IndexController extends AbstractController { 
    public function indexAction() {
        $this->ImgStorage->someMethodOfAmazonS3();
    }
}

Hi, Thank you for your comment. The reason I would not like to have service injection like this (but rather have constructor or setter injection) is because you need to mock the DI container too, if you want to do unit tests. Also, it is advised to check the ImgStorage each time you use it if it comes from the DI container (and not from constructor injection).

However, if it is not possible I guess I had just better check ImgStorage in the AbstractController like this:

abstract class AbstractController enxtends Controller {
    protected $imgStorage;

    public function initialize() {
        $this->imgStorage = $this->getDI()->get('ImgStorage');
        if(!$this->imgStorage instanceof ImgStorage) 
            throw new Exception('Some message to dev channel');
    }   
    // ...
}
 class AlbumController extends AbstractController {
    public function indexAction() {
        $this->imgStorage->getAlbums();
        // ...
    }
}

But I really am guessing. I am new to phalcon.

The reason I would not like to have service injection like this (but rather have constructor or setter injection) is because you need to mock the DI container too, if you want to do unit tests.

Yes, that is a valid concern, and I see now what you're trying to acheive. Your only problem was using a controller as a service.

I'd rather create an intermediate class for handling the setter/constructor injection. Basically, it would be a wrapper around the AmazonS3 class.

class AmazonGallery
{
    protected $storage;
    function setStorage($storage) {
        $this->storage = $storage;
    }
    function getStorage() {
        return $this->storage;
    }
    // custom logic
}
$di->set('ImgStorage',
    [
        'className' => 'Gallery\Classes\AmazonS3'
    ]
);
$di->set('MyService',
    [
        'className' => 'AmazonGallery',
        'calls' => [
            [
                'method'    => 'setStorage',
                'arguments' => [
                    [
                        'type'  => 'service',
                        'name'  => 'ImgStorage'
                    ]
                ]
            ]
        ]
    ]
);
class AbstractController {
    // empty
}

class GalleryController extends AbstractController {
    public function indexAction() {
        // access AmazonGallery instance
        $this->MyService->getStorage();
    }
}