I'm learning Symfony and I'm trying to figure out where to put custom actions over an Entity...
For example, if I have an entity Order, where to put $order->complete()? Or $order->sendToProduction(), $order->queueForDelivery()?
Those are just examples, I have complex entities and I must perform on them many actions.
In the Controller?
No, because the same action may be called from different controllers
In the Entity?
That would be the more appropriate way in a MVC model, but here I can't find an easy way to perform custom mysql query (doctrine/em is not available) from inside the Entity class, which I find strange since db operations should be perfomed at the Entity level, I believe...
In the EntityController?
It doesn't seem appropriate, and it's not easy to call repository methods from a listener, for example, and call them directly on the object...
What else? Do I have to create services? Utility classes?
If the work can be done inside a signle entity (and it's relations of course) then it should be placed there. I mean, if the operation is about changing entity's internal state.
Otherwise, if this job need to use other parts of application like database, or is performed on multiple not related entites, then I would suggest using services.
That's what where are for. Service is basically a class that can do anything. Using Service container, you can pass any dependencies to it so it's very flexible and easy to use.
For example $order->queueForDelivery(). That may mean a few different things:
changing internal state like change status to queued_for_delivery - then it should be in Order entity class
$order should be put in the Queue that is other entity class, then it should be in Queue class like $queue->addOrder($order)
this queue is an external service like RabbitMQ or anything else. Then you should use a service class.
Related
I'm currently developing a simple web application with Symfony 2 that process orders with products and lines of orders. Actually I have three Doctrine entities (Product, Line and Order). I will work with the data of these models and I need filter by date, add, delete, list, and make some administrative things with all the data.
As far I know this will be Service territory and is best practice to keep the controller away from doing this.
My question really is if I need to create ONE class with the methods that I need for all operations and then call them from the Controller, or create one Service class for each Doctrine Model (OrderManager, LineManager...) or orient more to task specific (RecountTotal, FilterDate...) But with the last method I think that each Service will have only one method inside.
Which is the best practice for this?
You are totally correct in stating this is NOT a job for controller.
It's totally fine to use only one manager to contain all your data access methods.
class BaseManager { // Name it as you like
...
}
If you find yourself having very similar methods for different entities, you may want to split into different managers (OrderManager, LineManager, ProductManager) and have them extend a common BaseManager to reduce code duplication.
Additional classes for filters and counting should not be necessary unless you have some advanced requirements.
Recently I began to study SF2 but there are some doubts which I'd like to solve:
Let's say that I have a lot (more than 100) of Models which extend one generic model(Entity). Each model has unique functions (rules for parsing content from model source), what is the best way to organize it?
In ZF1 I would create Model/ModelType/ModelName.php file for every model which need customization, in SF2 I'm not sure what kind of entity should I use: One service container which calls entities and call their custom functions (implemented through interface), or maybe one huge Service which has different functions for every single model?
Thanks in advance.
If parsing functions based only on current entity state (use only entity property) it's better to use entity method. Entity aslo can implement some interface, e.g. ParsableInterface.
If paring function need to use another entities it's better to use standalone services handle parsing
What is a way to log messages or errors from an Entity or Repository class in the symfony2 architecture? In symfony1 you could use the singleton to kill puppies by doing something like this to get the logger from anywhere:
sfContext::getInstance()->getLogger()
Symfony2's container model is stricter, which is great, but how should one go about logging from non-container-aware classes? For repos, I guess you can define them (all) as services, with a dependency on the logger, and go from there. But what about when you just have an instance of an Entity class?
Historically I'd want to put the log message inside class methods, but now? Should I pass the logger (as a parameter) into every class method that wants to write a log message? This seems like a bit of overkill but perhaps it's best practice?
Or am I looking at this wrong and Entities or Repos shouldn't be writing log messages but passing them back to the controller to handle?
You should probably avoid putting business logic (even logging) inside entity model.
As for the repositories, the way you described is the right one.
I come from a Java/Grails background and cannot seem to find a definite answer online as to where the service logic for a CakePHP application should be stored. By "Services", I am talking about classes that are usually instantiated through dependency injection to conduct business logic on domain objects. They should be able to query any domain object and make changes in response to a controller action.
Currently, CakePHP's "Component" class seems to be the closest match to this behavior. I can load a component into any controller and execute its methods as needed. However, I have read in several places that components should never access the database and that doing so will result in some steep performance hits.
I have also looked into CakePHP's "Behavior" class and it doesn't seem to fit the ticket at all. It seems well-equipped to organize domain objects into a data structure setting, but that's not the kind of logic that a service would execute. Also, to import any model definition into a Behavior, I would have to edit the model definition itself to allow access, which is very awkward.
So I ask this question: Where should service logic be stored? Certainly not the controller, as it should only contain the minimal logic to process a request and send a response.
Components are the service layer in CakePHP. They are constructed by a dependency injection container (Components Collection) and get passed the controller, request and response that is to be handled.
There are no restrictions in what Components can do other than maintaining separation between layers. It is fine to use database connections, or use models, directly from a component and modify the request.
Components are actually very light-weighted if you only make them act for specific cases. Inspecting the action name, is a common way of limiting the reach of a component. You can also inject setting so it can know when is OK to execute custom service logic.
So I ask this question: Where should service logic be stored?
Certainly not the controller, as it should only contain the minimal
logic to process a request and send a response.
Sounds like the ideal use case for a Dispatcher Filter. It gets called even before a controller is instantiated. If you need to query the database simply load a model via ClassRegistry::init('YourModelName') and pass the request params to the model method and return whatever you need in your request. No controller needed at all. We've implemented oauth + xhttp using Dispatcher Filters without calling ever a controller.
How using a model inside a component should effect the performance... I don't know who got that strange idea, sounds like not the best article you found. It is true that you should not put model layer related logic in them but you can call a model for example through the controller instance and the controllers models.
I'm trying to pass the entity manager to a service but havent find a correct way yet. I want to complete remove the em from the controller so thats why I'm finding another way.
I was thinking of this options:
1. I could save it in the registry and then try to access it from the service object. can I access the registry from there?
2. Inject the em to a static variable of a base class for the services in the bootstrap.
What is the correct way yo do it?
thanks
I think generally the best way to do it is to pass the entitymanager as an argument to the constructor.
This allows you to easily replace the entitymanager for example when doing unit tests, and unlike your approaches of 1 and 2, it does not depend on behavior in a base class or global data (the registry is a lot like a global variable)
What you could do to avoid touching the EM in your controllers is using a dependency injection container, such as the one in Symfony2 or the one in ZF2 (not sure if that component is very stable yet).
Another perhaps slightly simpler approach would be to have a sort of a "service locator" object, which you would use in the controller to get instances of your services. You could initialize the locator in your bootstrap with the services, or perhaps with a factory class which creates them.
In any case you will probably require at least some kind of an intermediate object in the controller. Personally I don't really see an issue with simply using the EM itself, unless you have some other reasons besides just not wanting to.
There's nothing wrong, IMO, with letting your controllers know about the EM. I typically use a Zend_Application_Resource to bootstrap Doctrine. That resource facilitates a bootstrap resource called "doctrine" which has an EM available. The abstract controller implements and em() method, which returns the EM.
When instantiating service classes, the constructor simply injects the EM via a call to $this->em() at constructor time.
This is nice, as many times, simple controller actions don't need any special service class, but can instead get away with doing $entity = $this->em()->getRepository('Some\Entity')->find(1); In those cases, I don't see any reason for additional redirection via a service class.