I know I can do a lot of stuff...but I'd like to avoid overkill.
I have a class with general util methods. I want to be able to call one of those methods from inside a formType.
What I did is the following:
Inside the formType I added
use AppBundle\Util\GeneralUtil;
class HRMgmtFormType extends AbstractType
{
public function __construct(GeneralUtil $util)
{
$this->util = $util;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$timeCommitment = $this->util->generatePercentVectorByStep(1);
...all the rest of the stuff
And when I call it from the controller I have:
$form = $this->createForm(new HRMgmtFormType(new GeneralUtil()), array(...all the stuff I need here...);
It works.
Is this ok/elegant?
I basically avoided declaring my general util class as a service, and most important declaring my form as a service (and injecting the generalUtil service inside).
Thank you!
SN
As far as you have the class "GeneralUtil" such simple as you have it now: without any dependencies and any configuration parameters, your solution is fair. But there are some more aspects that you should think about:
Will be "GeneralUtil" reusable at other places?
Will you need to make unit tests? if so, do you plan to mock the util if you plan to get "pure" unit test and make dependency injection(DI) with mock?
Do you plan extend the util's functionality? will it get some dependencies or configuration in future?
If yes, then it is better to get the benefit from Symfony SOA (Service Oriented Architecture) approach and refactor your code to SOA and DI, that will allow you follow another important patter as DRY (Don't repeat yourself) http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Update to your commet:
So you can see from http://symfony.com/doc/current/book/forms.html#defining-your-forms-as-services
Defining your form type as a service is a good practice and makes it
really easy to use in your application.
but as I already explained about the utils class, similar logic is about forms. So if you plan to reuse your form, then you can do it as service, but if you will have several different forms that will use your utils class, then it's better leave the form as class, but make a service for the utils. In addition you can do it if you see possibility to overwrite your form some other i.e 3rd party forms.
So benefits list from form as service:
in case of multiply usage, it initialized only once
easy to overwrite by other form
globally configured from parameters ans DI other services
Related
For those of you that are familiar with the building of the Symfony container, do you know what is the differences (if any) between
Tagged service Collector using a Compiler pass
Tagged service Collector using the supported shortcut
Service Locator especially, one that collects services by tags
Specifically, I am wondering about whether these methods differ on making these collected services available sooner or later in the container build process. Also I am wondering about the ‘laziness’ of any of them.
It can certainly be confusing when trying to understand the differences. Keep in mind that the latter two approaches are fairly new. The documentation has not quite caught up. You might actually consider making a new project and doing some experimenting.
Approach 1 is basically an "old school" style. You have:
class MyCollector {
private $handlers = [];
public function addHandler(MyHandler $hamdler) {
$handlers[] = $handler;
# compiler pass
$myCollectorDefinition->addMethodCall('addHandler', [new Reference($handlerServiceId)]);
So basically the container will instantiate MyCollector then explicitly call addHandler for each handler service. In doing so, the handler services will be instantiated unless you do some proxy stuff. So no lazy creation.
The second approach provides a somewhat similar capability but uses an iterable object instead of a plain php array:
class MyCollection {
public function __construct(iterable $handlers)
# services.yaml
App\MyCollection:
arguments:
- !tagged_iterator my.handler
One nice thing about this approach is that the iterable actually ends up connecting to the container via closures and will only instantiate individual handlers when they are actually accessed. So lazy handler creation. Also, there are some variations on how you can specify the key.
I might point out that typically you auto-tag your individual handlers with:
# services.yaml
services:
_instanceof:
App\MyHandlerInterface:
tags: ['my.handler']
So no compiler pass needed.
The third approach is basically the same as the second except that handler services can be accessed individually by an index. This is useful when you need one out of all the possible services. And of course the service selected is only created when you ask for it.
class MyCollection {
public function __construct(ServiceLocator $locator) {
$this->locator = $locator;
}
public function doSomething($handlerKey) {
/** #var MyHandlerInterface $handler */
$handler = $serviceLocator->get($handlerKey);
# services.yaml
App\MyCollection:
arguments: [!tagged_locator { tag: 'app.handler', index_by: 'key' }]
I should point out that in all these cases, the code does not actually know the class of your handler service. Hence the var comment to keep the IDE happy.
There is another approach which I like in which you make your own ServiceLocator and then specify the type of object being located. No need for a var comment. Something like:
class MyHandlerLocator extends ServiceLocator
{
public function get($id) : MyHandlerInterface
{
return parent::get($id);
}
}
The only way I have been able to get this approach to work is a compiler pass. I won't post the code here as it is somewhat outside the scope of the question. But in exchange for a few lines of pass code you get a nice clean custom locator which can also pick up handlers from other bundles.
I'm a newbie on symfony, And I don't understand the advantage of use service instead of write the cose in Controller
For Example, I Have a service that Create Log, with a code like this:
$path = $root.'/../web';
$fs->touch($path.'/log.txt');
$this->file = $path.'/log.txt';
file_put_contents($this->file, $msg, FILE_APPEND | LOCK_EX);
I can put this login in service with DIC ($fs is FileSystem service), or I can Put this Login on my Controller.
Of course If i Need to log often I have to write the same code. The main advantage is decoupling?
Thanks a lot
Suppose you have a Bar class which uses BasicLogger.
You have a few ways to get access to this logger, lets start with the most simple option:
<?php
class Bar
{
public function bar()
{
$logger = new BasicLogger();
$logger->log("foo");
}
}
This is bad practice because we are mixing construction logic with application logic. It still works, but it has the following drawbacks:
It mixes responsibilities.
Bar becomes hard to test and cannot be tested without side effects.
We cannot dynamically change loggers (code is less reusable).
To solve these drawbacks, we can instead require our Logger class through the constructor.
Our code now looks like this:
class Bar
{
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function bar()
{
$this->logger->log("foo");
}
}
Great, our class is no longer responsible for creating the logger, we can test our code without side effects (and make assertions against how the logger was used) and we can now use any logger we like.
So now we use our new class all over the application.
$logger = new Logger();
$bar = new Bar($logger);
Look familiar?
Again we are mixing construction logic with application logic, which we already know is bad.
Not only that, but something even worse is happening here, Code duplication.
Thats right. and every time we want to use our Bar class, the duplication gets worse.
The solution? Use the Service container
Registering your logger as a service would mean that all of your code that needs logging functionality is no longer dependent on your specific logger, responsibilities will not be mixed, code duplication will be reduced and your design will become more flexible.
The main goal and advantage of services is that keep reusable code and use a DRY approach.
Of course, there is a lot of other advantages that you discover progressively as you use them.
Services are accessible from whatever context of your application that can accesses the service container, not only controllers.
If without the service the few lines of code you give would be duplicated in several methods/contexts, you should keep your service.
Otherwise, delete it and do your logic in the specific method.
I think the better approach to use them is at your own feeling.
Don't try to create services in prevention, use them to solve a real need.
When you have a block of code that is duplicated, you should naturally avoid it by creating a service (or other AbstractController that your controllers can extend and inherit the code block) .
The goal is: Always keep a light code and avoid duplicates as possible.
For that, you can use the powerful services of Symfony, or just use the inheritance of classes and other POO principles.
Still exploring the new ASP.NET MVC5, now with build in DI!
No Problem so far, I can just inject my Handlers (I don't like the Term Service, since this defines to me a Platform-Neutral Interface):
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.Configure<Model.Meta.AppSettings>(Configuration.GetSection("AppSettings"));
services.AddSingleton(typeof(Logic.UserEndPointConfigurationHandler));
services.AddSingleton(typeof(Logic.NetworkHandler));
services.AddMvc();
}
Works fine, also the strongly typed Configuration-Object "AppSettings" works perfectly fine.
Also the Injection in the Controllers works as well.
But now my collaps: I seperated my DataAccess from the Handlers, and obviously I'd like to inject them as well:
public class UserEndPointConfigurationHandler
{
private readonly DataAccess.UserEndPointAccess _access;
public UserEndPointConfigurationHandler(DataAccess.UserEndPointAccess access)
{
_access = access;
}
But bam, UserEndPointAccess can't be resolved. So it seems like even I directly request to DI an Class with a Parameterless-Constructor, I need to register that. For this case, sure I should Interface and register them, but what does that mean for internal helper classes I also inject?
According to the Docs: http://docs.asp.net/en/latest/fundamentals/dependency-injection.html#recommendations and also the examples I found, all people in the world only seem to communicate between Controllers and some Repositories. No Business-Layer and no Classes on different Abstraction-Levels in Assemblies.
Is the Microsoft DI approach something totally differnt than the good ol' Unity one, where I can really decouple as fine granular as I'd like to?
Thanks in advance.
Matthias
Edit #Nightowl: I add my answer here, since it's a bit longer.
First of all, Unity does automatically create Instances, if I request a conecrete Type. This allows me to inject Types I register and Types, like Helper classes etc. I don't need to. This combination allows me to use DI everywhere.
Also in your Example I'd need to know the DataAcces in the WebGui, which is quite thight coupled. Well, I know there are solutions for this via Reflection, but I hoped Microsoft did something in this Topic, but probably that'd mean to big of a change.
Also allows Unity to store Instances or Instructions how to create them, another huge feature, which is missing at the moment.
Probably I'm just to spoiled, what refined DI-Libraries do, probably they also do to much, but at the moment the Microsoft-Implementation is just a huge downgrade according to my Information.
MVC Core follows the the composition root pattern, which is where object graphs are created based off of a set of instructions to instantiate them. I think you are misinterpreting what the IServiceCollection is for. It does not store instances, it stores instructions on how to create instances. The instances aren't actually created until a constructor somewhere in the object graph requests one as a constructor parameter.
So, in short the reason why your service (which you call UserEndPointAccess) is not being instantiated when you request it is because you have not configured the IServiceCollection with instructions on how to create it.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.Configure<Model.Meta.AppSettings>(Configuration.GetSection("AppSettings"));
services.AddSingleton(typeof(Logic.UserEndPointConfigurationHandler));
services.AddSingleton(typeof(Logic.NetworkHandler));
// Need a way to instantiate UserEndPointAccess via DI.
services.AddSingleton(typeof(DataAccess.UserEndPointAccess));
services.AddMvc();
}
So it seems like even I directly request to DI an Class with a Parameterless-Constructor, I need to register that.
If you are doing DI correctly, each service class will only have a single constructor. If you have more than one it is known as the bastard injection anti-pattern, which essentially means you are tightly coupling your class definition to other classes by adding references to them as foreign defaults.
And yes, you need to register every type you require (that is not part of MVC's default registration). It is like that in Unity as well.
I've created custom decorator for EntityManager and now when I'm doing doctrine->getManager(), then I can get my custom manager class, but inside repository class I still have native EntityManager how can I fix this. Or maybe there is another way to set something inside repository classes from container?
Decorator calls getRepository on $wrapped(EntityManager) and then $wrapped pass $this inside RepositoryFactory $this == $wrapped == EntityManager
My solution is:
public function getRepository($className)
{
$repository = parent::getRepository($className);
if ($repository instanceof MyAbstractRepository) {
$repository->setDependency();
}
return $repository;
}
There are a couple of approaches:
Copy the static EntityManager::createRepository code to your entity manager class and adjust it accordingly. This is fragile since any change to the EntityManager code might break your code. You have to keep track of doctrine updates. However, it can be made to work.
A second approach is to define your repositories as services. You could then inject your entity manager in the repository. Bit of a hack but it avoids cloning the createRepository code.
The third approach is the recommended approach. Don't decorate the entity manager. Think carefully about what you are trying to do. In most cases, Doctrine events or a custom base repository class can handle your needs. And it saves you from fooling around with the internals.
One option would be to override the entity manager service classes or parameters via a compiler pass.
I'm building an application in Symfony 2.2.
I use a custom repository class for one of my entities. I include it like this:
#ORM\Entity(repositoryClass="MyApp\MainBundle\Entity\CategoryRepository")
I want to use methods on my category entites from two other repository classes.
#ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
#ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
The entity, however, only expects one repositoryClass. How can I use all three of these repositories for the same entity?
I believe, you can only have one repository per Entity.
So you have referenced
ORM\Entity(repositoryClass="MyApp\MainBundle\Entity\CategoryRepository")
If you must use another repositories within the repository,
You can get the EntityManager for those Repositories (if not same) eg: $em2 = whatEverIsTheEntity()
Now you can have proxy methods in CategoryRepository to the methods in the repository you want to use. eg: $em2->getMethodFromEm2().
Note: But I wouldn't prefer above solution unless there is really a need. I would rather create a service layer which has Doctrine Entity Manager as Dependency Injected. This service layer will connect to all the repositories and collect final result whatever you want to achieve (to be used in the controller). This way, the code will be much cleaner and testable as well.
Hope this helps.
Repositories are class (so entity) reserved. If you inherit from super classes (even if those classes doesn't inherit directly, but repos can), than you can make your repository (CategoryRepository) extended by other two (one extend the other, since PHP doesn't allow more than one class extension).
Pretty much something like that
class NestedTreeRepository extends SortableRepository
{
}
class CategoryRepository extends NestedTreeRepository
{
}
don't know if this fits your needs, but this a way (and I'll not surprised if is the only one) for realize what you're trying to do here