Global access to symfony2's configuration values - php

How can I get configuration values (custom) from anywhere in the app?
I want to do it outside a controller in an entity's method prePersist. Dependency injection sounds illogical here too.
Isn't there some static way of getting the config class or the kernel..?

Dependency Injection is the Symfony 2 way to use configuration: create services for your logic, inject your configuration in services and inject services in other services using OO logic. As for your specific question (using config in the entity's prePersist) the answer is that if you need to access configuration the prePersist callback is not the right place to perform your logic since entities should not be aware of anything that belong to higher software layers (i.e. service/configuration layers).
You can find some more explanation here: How to use the translator service inside an Entity?

What about my own approach of using a custom made ConfigClass? You then should just add it in the needed place and use it.
namespace Your\Namespace\YourConfig;
class YourConfig {
private $energy_config;
public function __construct() {
$this->energy_config = array(
'update_frequency' => 10,
'energy_added' => 10,
'energy_maximum' => 200,
);
}
}
Later if you need the energy_config values, just add in the needed class use statement:
use Your\Namespace\YourConfig;
...
public function foo() {
$config = new YourConfig();
// use your config values
}
This is just my idea, hope it helps until someone gives a truly great solution :)

Related

Dependency injection in PHP and DIC

I have a bone to pick with dependency injection. It seems like such a simple concept has led to a lot of confusion amongst many, and the 'right' way to it seems to spiral out of control in terms of complexity. In keeping things really simple I just want to know:
Is it good practice if I use standard functions to register my dependencies, like this:
function someObject() {
return new someObject();
}
And can I pass parameters?
function someObject($param1) {
return new someObject($param1);
}
Finally how about using a simple custom container class:
class Dependencies {
function someObject($param1) {
return new someObject($param1);
}
}
And using it:
class SomeClass {
public function __construct(Dependencies $dependencies) {
$this->dependencies = $dependencies
}
public function methodNeedsDependency() {
$param1 = 'one';
$task = $this->dependencies->someObject($param1);
}
}
I guess what I'm asking is, is this a decent attempt at a very simple implementation of a DIC? Or are there some underlying major issues bubbling under the surface?
Thanks!
If SomeClass is a factory, designed to create some instance with some exact config given, then yes, we could say this is very simple implementation of DIC.
Issues here? Well, let's say You want to create someObject with other constructor params.
Solutions:
Create another SomeClass factory that will pass other params. It's redundant solution that also mixes two application layers ( config with object creation layer ).
Parametrize methodNeedsDependency with params that will be passed to someObject. It's something that allows You to postpone in code config creation.
and last but the most proper way: DIC should be flexible, should allow You to create any service instances with any dependencies, and all of configuration should be stored in configuration layer. Separation of concerns - try to make DIC unaware of what object type is creating. Just let him make objects that You described somewhere else.
There are a lot of other issues also, like for example what if You would like to have only one instance of given class for whole DIC ( it's easy to "if" it, but that's another workaround to proper solution ) or what with circular references.
This discussion can go on and on with other issues about architecture in general, like "services should be stateless" what makes it unnecesary to have more then one, but answer to your question are those 3 points. Of course remember it's not ultimate-mega-hiper answer for DIC. It's just my way of seeing it ;-)

ZF2 - Make loggers globally accessable, ideally lazy loaded or by event

I have various Loggers defined in my module_config. Each controller or service that needs a specific logger needs to get the logger via its factory injected.
Ideally I don't want to worry about this injection and can make the loggers gloablly accessable (without having the service locator in my service/controller). They should be lazy loaded/instanciated, only when I need them.
Is there a way to do so? I was thinking of initializers, treats or using the event manager.
I know, It sounds like having the service locator back.
Any ideas?
I guess the cleanest way is to use a controller plugin to call upon your loggers from a controller.
Implementation is pretty simple:
Create a new PHP class and have it extend Zend\Mvc\Controller\Plugin\AbstractPlugin.
It's constructor should accept the logger(s) as a dependency, and it's __invoke method can be used to "magically" retrieve a logger.
Next, create a factory for it, implementing Zend\ServiceManager\FactoryInterface and use it to inject the dependencies (i.e., the loggers)
Register the factory in your servicemanager configuration:
'controller_plugins' => array(
'factories' => array(
'Logger' => 'MyApp\Controller\Plugin\Logger',
)
),
You are then able to call it in a controller like so:
$this->logger();
(See http://lab.empirio.no/custom-controller-plugin-in-zf2.html) for more information on creating custom controller plugins.
Lazy-loading loggers can be handled by registering them as lazy services under the lazy_services key in your service_manager configuration.
As for your services, you can implement your own version of a ServiceLocator (also known as a plugin manager).
It would work a lot like the ViewHelperManager, ControllerPluginManager, ValidatorPluginManager, etc.
I won't explain how to do that as it's a lot of code and many how-to's exist on the web on how to create one.
EDIT:
As #tasmaniski noted below you could use injection based on marker interfaces using initializers (ZF2 uses this internally through the 'Aware' interfaces).
Do note that initializers are relatively slow especially when used incorrectly, i.e. as a closure in your Module.php or module.config.php file.
Also, initializers get called upon for each service fetched by the ServiceManager's get() mtehod and can thus add significant overhead.
It is however a much easier and faster solution than writing your own implementation of a plugin manager.
I have exactly the same question in my application/architecture.
So the solution was:
In the scope of Controllers, as #FuST said you have to create controller plugin. Than the logger will be available across all controllers.
In the scope of services, I you can do a "Interface Injection".
So in your Service you have to add 3 thing:
// #1 Create interface with setLogger($logger); method
class YourService implement LoggerInterface {
private $logger; // #2 Define private $logger;
// #3 Add method that you will use to inject the $logger later
public function setLogger($logger){
$this->logger = $logger;
}
}
Than create initializer and inject $logger in every class that implement the LoggerInterface.

How do I use Doctrine from within my application, considering modern OOP practices?

Doctrine examples usually make use of Doctrine's $entityManager. Hence, whenever I need to do anything in my app with Doctrine, I need to get the entity manager into my code. But how? .... I can inject it into my class, but still I need to create manager somewhere first. I can also make it use PHP's trait which I put into my class to make it Doctrine-enabled.
What I have done in my OOP code is something like this -- I defined a class DoctrineConnector in its own namespace of DoctrineConnector, and inside the class I have a static function getEntityManager. Inside, I read Doctrine configuration, paths, parameters, and create an $entityManager, and then I return it to the caller.
Whenever I need to read or persist something in my Doctrine-unaware code, I do this:
//Pricing.php
use DoctrineConnector\DoctrineConnector;
class Pricing
{
public function getPricing()
{
$entityManager = DoctrineConnector::getEntityManager();
//further Doctrine code to read DB
}
}
DoctrineConnector is now a dependency of Pricing. Obvious answer may be "inject $entityManager into Pricing". But Pricing is called from another class, and that other class is called from another class, etc so I will have to make essentially every class I call be aware of the ORM variable. I want to avoid that.
Is what I currently have just fine or is there a better way?
How ZF2 module system does it
Out of curiosity I looked into how ZF2 manages Doctrine module and from DoctrineORMModule, it uses ServiceLocator pattern to call the inside of controller. So one of the leading framework module systems is not that far off from my implementation.
Singleton Pattern
Adapted from http://www.phptherightway.com/pages/Design-Patterns.html
namespace ABC;
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
class DoctrineConnector
{
private static $instance;
public static function getEntityManager()
{
if (null === static::$instance)
{
// Doctrine Config (from Docs)
include 'config/doctrine-config.php';
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
static::$instance = EntityManager::create($dbParams, $config);
}
return static::$instance;
}
protected function __construct()
{}
private function __clone()
{}
private function __wakeup()
{}
}
//to call
$em = DoctrineConnector::getEntityManager();
//if used later in the code - calls same instance
$em = DoctrineConnector::getEntityManager();
There are a couple of popular ways to get a dependency inside the class.
make the class aware of project environment and let the class locate the dependency (what you did with DoctrineConnector)
use dependency injection (what you are trying to achieve)
In your case, your class now depends on DoctrineConnector and is going to work as long as it can find DoctrineConnector. If you port your class to another project where DoctrineConnector isn't defined, your code isn't going to work.
Normally dependency injection is a decision you make when you start your new project. If you already have a large hierarchy of classes, the injection code will bubble up your hierarchy and there is going to be a lot of work refactoring your code. You might want to think wether you really need that and wether it's going to be worth your time.
If you are writing code for one particular app and have no plans to migrate it to other projects, then you probably don't care about dependency injection. Actually, without injection your code is going to be shorter and easier (faster) to write and understand. On the other hand if you have pieces of code that you want to be independent of the app, you might use dependency injection only for those code segments.
If you haven't injected ORM yet, chances are that the rest of the code is tightly coupled with your app as well, and injecting entityManager isn't going to make your code much more portable.

Laravel4: Use different model depending on config

I extended my Laravel4 Project with another Project Model. Now I have two Models (like Model1 and Model2). My goal is to set a custom config var, telling the installation which Project model to use.
Is there a way to set this in the config and then use Project1 as Project in my Controllers?
If I call $p = new Project; I want it to be an instance of either Project1 or Project2, depending on the configuration.
I hope you understand my question, thank you
You can kind of achieve this by using the IoC container.
First, instead of using new Project, use App::make('Project') - that won't change anything yet, but will allow you to now 'redefine' what 'Project' points to.
So the next step is, in your startup script (or a service provider if you're using service providers) code similar to the following:
if (Config::get('model', 'Project') != 'Project') {
App::alias('Project', Config::get('model', 'Project'));
}
This line will bind whatever classname you set in 'model' in the main config to be what the code retrieves when you use App::make('Project'). The reason for the if is just so we don't end up aliasing Project to Project. Feel free to test it without, but it may create an infinite loop/recursion in the IoC.
The code may not be perfect as it's untested, but at the very least it should give you a path to explore.
Points to note
You may find App::bind('project', Config::get('model')); works better, especially if you start using the IoC container's automatic injection
As mentioned above, the if surrounding the magic line may not be required. The IoC container may well be clever enough to not keep following an alias to itself.
A note on static access
I mentioned before to change all instances of new Project to App::make('Project'), but I didn't mention how to change your code when using something like $projects = Project::all();. Well you can still do a similar thing, but you just have to get an instance: App::make('Project')->all();.
When you start going down this route, you find that you're having to App::make('Project') more and more. When this is the case, using Laravel's automatic dependency injection will help a lot. To do this, you typehint the class in your controller's constructor:
class ProjectController extends \Controller
{
protected $project;
public function __construct(Project $project)
{
$this->project = $project;
}
public function index()
{
$projects = $this->projects->whereVisible(1)->orderBy('date', 'desc')->get();
return View::make('projects.index', compact('projects'));
}
}
This automatic injection happens in a few classes. I don't know the full list, but certainly controllers, event listeners, view creators and view composers. If you have a class of your own you wish to take advantage of the automatic injection, you can App::make() it and it too will get its dependencies injected automatically.

How to read config value in Symfony2 in Model class

I've defined a hash salt in my config.yml and would like to get this in my User class, any ideas about how to do this? I've seen loads of examples about how to use this in the controller class, but not in the model?
Thanks
I've had the same question. It'd be nice if there was something similar to sfConfig::get() from symfony 1. Anyway, I think this may actually be a case of "there's a better way to do this". What if you just use setter injection when you instantiate your User class (ie use a setHashSalt() method)? If you're instantiating from a controller you can use $this->container->parameters['hash_salt'].
AFAIK, there's no way to access config.yml parameters without using the container object. I'm very curious to see if anyone has an easier way.
See my answer here:
How do I read configuration settings from Symfony2 config.yml?
with
FIRST APPROACH: Separated config block, getting it as a parameter
SECOND APPROACH: Separated config block, injecting the config into a service
Answering to you, if you want to inject that in the Model, the best approach is to have a Manager that acts as a factory of the model, then the manager can inject itself into the model, so the model can get access it and therefore, get access to the configuration.
Say, your model has a Car and a House if they are related you could have a CityManager with a getAllCars() or getCarById() or similar, as well as a getAllHouses() or a getHouseById() or so.
Then in the CityManager either pass the config to the Model class:
class CityManager()
{
private $myConfigValue;
public getCarById( $id )
{
return new Car( $id, $this->myConfigValue );
}
}
either pass yourself, and let the Model get the config only if it needs it:
class CityManager()
{
private $myConfigValue;
public getCarById( $id )
{
return new Car( $id, $this );
}
public getConfig()
{
return $this->myConfigValue;
}
}
Fill the value as in the article linked.

Categories