I have a basic mvc like framework, and I would like to use pimple for dependance injection, but I don't know how to use it inside the framework. This is my app structure.
x-framework
- config
- app
controller
homeController.php
- core
- vendor
pimple
lib
pimple.php
- public
Now, in homeController.php I would like to use Pimple, but without actually doing new Pimple as seen in this example.
use vendor\pimple;
class homeController
{
function index(){
$app = new Pimple();
$app['orm'] = $app->share({ return new vendor\orm; });
$orm = $app['orm'];
$orm->table('foo');
$orm->findFirst(['name'=>'john']);
}
}
It seems as seen in this example, it would be a very cumbersome task to initialize the pimple class on every controller. How is this done correctly?
My answer was not relevant, though the principle of abstract classes stays interesting. Now:
I would like to use Pimple, but without actually doing new Pimple as seen in this example.
At some point you have to instantiate an object, if you want to use it.
Pimple uses a container to store and retrieve services and parameters:
$container = new \Pimple\Container();
// define some services
$container['session_storage'] = function ($c) {
return new SessionStorage('SESSION_ID');
};
this exemple from the doc defines an anonymous function which returns a session storage object
integrating a container
Pimple, or any container, can be made available using the dependency injection pattern.
either pass it as a parameter to the index
function index(\Pimple $app){
or pass it to homeController's constructor
function __construct(\Pimple $app){
$this->app = $app;
then use it as a property or a variable
$orm = $app['orm']; // through index() parameters
$orm = $this->app['orm']; // through constructor
abstract classes allow you to define a method for every extending classes, or forcing every extending classes to define a method.
here, we define a constructor for every extending classes, typehinting the Pimple class so that php will ensure your controller receives a real pimple object
abstract class Pimpleized {
function __construct(\Pimple $pimple) {
$this->app = $pimple;
}
}
then your controller
class homeController extends Pimpleized {
function foo() {
$this->app->accessSomePimpleMethod();
}
}
that way, you only have to create your Pimple object once, then pass it to your controllers:
$pimp = new Pimple();
$controller = new homeController($pimp);
Just extend HomeController class with pimple
class HomeController extends Pimple {
public function __construct() {
$this['orm.class']= 'vendor\orm';
$this['orm'] = $this->share(function($c){ return new $c['orm.class']; });
}
}
//and use it directly just after instanciation
$controller = new HomeController();
// you can modify parameters if you need
$controller['orm.class'] = 'myothervendor\orm';
//And get class
$orm = $controller['orm'];
$orm->table('foo');
$orm->findFirst(['name'=>'john']);
i hope it's you want :) cheers
Related
I am using PHP-DI 6 Container in my PHP Project. At the very begging of my program I just initialize the container and get Application class with all dependencies injected.
$container = new Container();
$application = $container->get(Application::class);
$application->initialize();
$application->run();
On the image below you can see classes that I use in my project.
Asterisk Dispatcher gets injected into Application class.
private $asteriskDispatcher;
public function __construct(AsteriskDispatcher $asteriskDispatcher)
{
$this->asteriskDispatcher = $asteriskDispatcher;
}
Then, inside AsteriskDispatcher class I need to create a list of Asterisk Manager instances, which is going to contain some dependencies as well in near future.
I don't want to inherit container through the all classes. Is there a way to initialize PHP-DI container as singleton, so I can use it any moment I want to create some objects?
This is how I am doing this right now, I just create a new instance of PHP-DI container inside my AsteriskDispatcher class and this looks so damn awful.
class AsteriskDispatcher implements AsteriskDispatcherInterface
{
private $listOfAsteriskManagers;
public function __construct()
{
$configurations = AsteriskConnectionsConfiguration::$connectionsConfiguration;
$this->listOfAsteriskManagers = new \SplDoublyLinkedList();
$container = new Container();
foreach ($configurations as $configuration)
{
$this->listOfAsteriskManagers->push($container->make(AsteriskManager::class,
array('configuration' => $configuration)));
}
}
}
I really want to understand how can I use PHP-DI container without breaking SOLID principles.
From the documentation:
If you need to use the make() method inside a service, or a controller, or whatever, it is recommended that you type-hint against FactoryInterface *. That avoids coupling your code to the container. DI\FactoryInterface is automatically bound to DI\Container so you can inject it without any configuration.
*emphasis mine
So you should change your AsteriskDispatcher constructor to be like:
public function __construct(FactoryInterface $factory) {
// your code ...
// more of your code ...
$factory->make(AsteriskManager::class, ['configuration' => $configuration]);
// the rest of your code.
}
PS: Singletons are evil (mostly).
I have made a repository pattern app, having a repo and interface:
class UserRepository extends EloquentRepository implements UserRepositoryInterface
{
public function __construct()
{
$this->model = new User();
}
...
}
The repository and interfaces as well as extended class and its interface is registered in service provider and is called on app boot.
The questions I have are:
Is there a need to watch out for the order of registering? For example, should EloquentRepository class be loaded before the
repo, or does Laravel handle that on its own?
In case I inject UserRepositoryInterface in a controller, is the constructor method called automatically even though I didn't really new-up a class?
How long does the DI injection "live"? If I inject it in a page controller which calls some other controller and needs the same dependency, does the constructor call twice then, and operate separately in each controller?
Is there a difference if I call it like App::make() instead of DI?
Is there a need to watch out for the order of registering? For example, should EloquentRepository class be loaded before the repo, or does Laravel handle that on its own?
I don't quite understand where you would load EloquentRepository as (from the code posted) it seems you're only extending it. Which shouldn't be a problem.
In case I inject UserRepositoryInterface in a controller, is the constructor method called automatically even though I didn't really new-up a class?
Yes. Most of Laravel's main classes (controllers included) are loaded with DI in mind and the dependencies will be resolved automatically.
That being said, since you are injecting an interface and an interface by default cannot be initialized as a class, since it has no implementation - you need to bind an implementation to the interface first in order to use it.
How long does the DI injection "live"? If I inject it in a page controller which calls some other controller and needs the same dependency, does the constructor call twice then, and operate separately in each controller?
My understanding is that a new instance of the class will be created when the next controller is initialized. Unless you bind a class as a singleton.
Is there a difference if I call it like App::make() instead of DI?
App::make(some::class) will automatically resolve the dependencies of class some.
For example:
namespace App;
use App\Dependancy;
class Test
{
protected $d;
public function __construct(Dependancy $d)
{
$this->d = $d;
}
}
If you call this in the controller: $a = new \App\Test() you will get an error that \App\Test constructor expects class Dependency as first parameter.
But if you initialize it like this: $a = \App::make(\App\Test::class) the Dependency will be automatically resolved.
try to make the repositories abstract in the controllers and inject these through constructor.
like this here:
public function __construct(EloquentRepository $repository)
{
$this->repository = $repository;
}
And in the AppServiceProvider you can inject repositories you will need.
public function boot()
{
// provides any Repository in SomeController
$this->app->when(SomeController::class)
->needs(EloquentRepository::class)
->give(function (Application $app) {
return $app->make(SomeRepositoryInterface::class)
});
}
Let's say I have the following case:
<?php
abstract class Service {
protected $config;
public function __construct($config)
{
$this->config = $config;
}
}
class ClientService extends Service {
}
class ProductService extends Service {
}
Is it possible to register in my service provider the dependency injection for the Abstract parent class of my services ?
I have an API which is generated dynamically from a specification, and each one of those classes must extend the abstract Service so it can inherit for basic functionalities.
How can I Inject dependencies in my abstract service when I instantiate a child Service ?
EDIT: This question was specifically asked for Abstract class injection, without the possibility to bind the child classes which are generated automatically.
In your example, you have to manually pass the config object every time you instantiate from Service class or a child class.
So when you want to directly instantiate a child service, you could use something like, $cs = new ClientService(new Config());
However, you can use the real advantage of DI (since you are using Laravel), by type hinting the class name in the constructor like below.
public function __construct(\Config $config)
This way, if you do not pass a parameter when instantiating, it would by default create an object of the type-hinted class and inject it. So you could then use it like.
$cs = new ClientService();
This would inject a Laravel Config instance into the ClientService object.
There are two possible things you could do here. First, if $config is a class, then you can type hint it in the abstract class:
abstract class Service {
protected $config;
public function __construct(ClassName $config)
{
$this->config = $config;
}
}
Then every time the child classes get resolved via injection or by calling App::make('ClientService'), the config class will be injected.
If the config is not a class and can't be type hinted, you will have to bind the child classes into the container individually:
App::bind('ClientService', function () {
// Get $config from somewhere first
return new ClientService($config);
});
App::bind('ProductService', function () {
// Get $config from somewhere first
return new ProductService($config);
});
Then you will be able to call App::make('ClientService') or have it resolved via DI.
I'm having trouble figuring out how to get ServiceManager instance from inside the custom class.
Inside the controller it's easy:
$this->getServiceLocator()->get('My\CustomLogger')->log(5, 'my message');
Now, I created a few independent classes and I need to retrieve Zend\Log instance inside that class.
In zend framework v.1 I did it through static call:
Zend_Registry::get('myCustomLogger');
How can I retrieve the My\CustomLogger in ZF2?
Make your custom class implement the ServiceLocatorAwareInterface.
When you instantiate it with the ServiceManager, it will see the interface being implemented and inject itself into the class.
Your class will now have the service manager to work with during its operations.
<?php
namespace My;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
class MyClass implements ServiceLocatorAwareInterface{
use ServiceLocatorAwareTrait;
public function doSomething(){
$sl = $this->getServiceLocator();
$logger = $sl->get( 'My\CusomLogger')
}
}
// later somewhere else
$mine = $serviceManager->get( 'My\MyClass' );
//$mine now has the serviceManager with in.
Why should this work?
This works only in the context of the Zend\Mvc, which I assume you're using because you mentioned a controller.
It works because the Zend\Mvc\Service\ServiceManagerConfig adds an initializer to the ServiceManager.
$serviceManager->addInitializer(function ($instance) use ($serviceManager) {
if ($instance instanceof ServiceLocatorAwareInterface) {
$instance->setServiceLocator($serviceManager);
}
});
Give it a try and let me know what happens.
I am using Laravel 3 (new to it). I have an API helper class that I'm using as a library. I want to have that class instantiated so I can use it within all my models to access the API. I am struggling with figuring out how to do it without instantiating it once in each model. An example would be awesome. Thanks.
There are a few ways you can go about doing this, the easiest would probably be just creating a base model where you instantiate the API helper class, then extending that base model for all of the models which you want to access the API.
It might look something like:
// base.php
class Base {
public static function api()
{
return new YourApiClass;
}
}
// user.php
class User extends Base {
public static function name()
{
return parent::api()->callApiMethod();
}
}
You could also use Laravel 3's IoC container, which might be the better choice depending on what you are doing.
Use an IoC container.
Instantiate your class:
IoC::register('mailer', function()
{
$transport = Swift_MailTransport::newInstance();
return Swift_Mailer::newInstance($transport);
});
And then when you need to access your instance you just have to:
IoC::instance('mailer', $instance);
Reference: http://laravel.com/docs/ioc