I'm working on an ecommerce Symfony application, but I've been really struggling to find some information about how to create entities that retrieve some data from Doctrine, and some from an external source.
This is a website that's going to be selling the same items that are available inside of a brick-and-mortar store that already has a pre-existing inventory system.
My current situation is that this application needs to be able to retrieve price and stock information from an external source (the REST API of our in-store inventory system), while the majority of the data is handled by Doctrine.
For example, I have a Product class that looks like this:
class Product
{
protected $id;
protected $title;
protected $description;
protected $quantity;
protected $price
}
I would like the first three fields to be handled by Doctrine like any other entity (which I already have set up, and there's tons of documentation on), but the last two to be retrieved based on an arbitrary data source (the REST API of our backend data, and this needs to be able to change as the store grows and their backend changes)
Ultimately, I want these product entities to be callable from any standard doctrine query and have all the fields fully available like any other entity. (including price and quantity)
I'm wondering if anybody can suggest how this problem would be solved in the Symfony ecosystem.
At this point I'm looking at a Doctrine postLoad event subscriber that manipulates the entities before they're returned, but I'm wondering if that's the way to go, and how to structure the code to do it. (I'm guessing I first need a separate bundle that exposes a "price" service, so I can do $Service->getPrice($ProductID), then where would I place that getPrice call? I feel like it isn't something that belongs inside of the entity class itself (since that classes purpose is only to define what the entity is, not how to make one), but that's what Doctrine docs seem to suggest)
I've asked a similar question here, but did not get very clear responses so I'm hoping that simplifying the problem might help.
I would create a service to access your REST API data and then inject it into Doctrine EventSubscriber. Inside that subscriber I would handle postLoad event to load data from REST API.
This service can be a part of an existing bundle.
Related
I'm playing around with Zend 3 and I have a question.
I'm building a small dashboard for creating and viewing time sheets. I have "sheet" entities and "client" entities.
The client pages are relatively easy, however I don't know how to program to do what I want with the sheet pages.
What I want to do is: I have a sheet entity with an id, a date, a time and a database column where the id of the client is saved. In the front end I now would like to display the client's name instead of the id.
But that means I have to use the getName() function of the client module.
Usually I would create a new Client and then just ask for the name.
However after having built my application with the Zend tutorial for blog posts, with all the factories and interfaces I don't know what instance to create and where I'd get the constructor variables from.
I'm sure there's several answers for this, but this's how i do it;
1: create a strategy for hydrator which takes client repository via __construct method. on "hydrate" method, get client data from repository and put it into sheet model
2: create a callable class with name "injectClient". on __invoke method take sheet as parameter. when you got it, ask to client repository for client of that sheet. you have to join two tables here. it's good for lazy loading. on first method, you have to load client data when you load sheet. but this one, you can just call it when you exactly need.
I'm using first answer for "must have to be there" data. Like language on multi-language applications. And second one for lazy-load.
Also you can use an object mapper like doctrine. It's always using lazy-load if you don't mention opposite.
Hi !
i'd like to transform data before sending it to the view. The view is json response using FOSRestBundle.
For example, I have a Product entity with field Category, that is a reference to a Category Entity. On the output, i'd like to not to display json with the whole category, but rather only its ID field.
Of course, you can further process the entity in the Controller to return only specific field from the category, but..
I wanted to ask, if there's any "standard" solution how to do that, maybe using annotation?
Thank you
I reckon the best practice or standard way to achieve what you want (filtering/processing entity data before return as JSON to view), is to make use of serializer. You can consider using the Symfony Serializer Bundle: http://symfony.com/doc/current/components/serializer.html or the JMS Serializer: http://jmsyst.com/libs/serializer.
I myself recommend the JMS one, it has really good documentation, the definition is completely controlled through annotation, and a well-thought events system and a few feature I can list as follows:
-- Expose / Exclude specific entity attributes.
-- Create Virtual Property (attributes) for the entity once it converted to JSON. (So for example if you have Entity Product with attribute Price and you want to have a calculated field in the JSON object call taxPrice you can achieve it).
-- Access Control who can access which attributes/entities by categorising attributes to different groups.
-- Pre Serialization and Post Serialization events so you can tweak the data even further.
I can say the Serializer works so well with the Forest Bundle that once you get used to it, you can hardly write your project the different way :D.
I have a class (PersistenceClass), that takes an array of data (posts) and parses that data and puts it into a DB (via doctrine). The field content needs to be parsed by a second class (SyntaxClass) before it is set into the doctrine entity.
Now the problem is, that the SyntaxClass has to set references in the content to other posts (just a link with and ID). So it needs access to the DB, and also needs to search in the persisted but not yet flushed entities from the PersistenceClass.
I can inject a doctrine EM into SyntaxClass and find my references in DB, although I dont like it very much. But the bigger problem is, how I can access the only persisted, but not flushed entities from the PersistenceClass ? I could make an Array of that objects and put it as an parameter to the parser method like:
SyntaxClass->parseSyntax($content, $persistedObjects);
But that does not look very clean. Aside from that, I dont know if it is somehow possible to search in the data of the persisted objects?
Your question is full of sub-question, so, first I'll try to make some things clear.
First, the naming convention you used is a bit abiguos and this not helps, me and also other people that may work on your code in future (maybe you'll grow and need to hire more developers! :P ). So, let's start with some nomenclature.
What you are calling PersistenceClass may be something like this:
class PersistenceClass
{
public function parse(array $posts)
{
foreach ($posts as $post) {
// 1. Parse $post
// 2. Parse content with SyntaxClass
// 3. Persist $post in the database
}
}
}
The same applies also for SyntaxClass: it receives the $content and parses it in some ways, then sets the references and then persists.
This is just to set some boundaries.
Now, go to your questions.
I can inject a doctrine EM into SyntaxClass and find my references in
DB, although I dont like it very much.
This is exactly what you have to do! The OOP development works this way.
But, and here come the problems with naming conventions, the way you inject the entity manager depends on the structure of your classes.
A good design should use services.
So, what currently are PersistenceClass and SyntaxClass in reality should be called PersistenceService and SyntaxService (also if I prefere call them PersistenceManager and SyntaxManager, because in my code I always distinguish between managers and handlers - but this is a convention of mine, so I'll not write more about it here).
Now, another wrong thing that I'm imaging you are doing (only reading your question, I'M IMAGING!): you are instantiating SyntaxService (you currently named SyntaxClass) from inside PersistenceService (your currently named PersistenceClass). This is wrong.
If you need a fresh instance of SyntaxService for each post, then you should use a factory class (say SyntaxFactory), so calling SyntaxFactory::create() you'll get a fresh instance of SyntaxService. Is the factory itself that injects the entity manager in the newly created SyntaxClass.
If you don't need a fresh instance each, time, instead, you'll declare SyntaxClass simply as a service and will pass it to PersistenceService by injection. Below this last simpler example:
# app/config/service.yml
services:
app.service.persistence:
class: ...\PersistenceService
# Pass the SyntaxInstance directly or a factory if you need one
aguments: ["#doctrine.orm.default_entity_manager", "#app.service.syntax"]
app.service.syntax:
class: ...\SyntaxService
aguments: ["#doctrine.orm.default_entity_manager"]
But the bigger problem is, how I can access the only persisted, but
not flushed entities from the PersistenceClass ?
Now the second question: how to search for {persisted + flushed} and {persisted + not flushed} entities?
The problem is that you cannot use the ID as the search parameter as the persisted but not flushed entities doesn't have one before the flushing.
The solution may be to create another service: SearchReferencesService. In it you'll inject the entity manager too (as shown before).
So this class has a method search() that does the search.
To search for the entities persisted but not flushed, the UnitOfWork gives you some interesting methods: getScheduledEntityInsertions(), getScheduledEntityUpdates(), getScheduledEntityDeletions(), getScheduledCollectionDeletions() and getScheduledCollectionUpdates().
The array of which you are speaking about is already there: you need to only cycle it and compare object by object, basing the search on fields other than the ID one (as it doesn't exist yet).
Unfortunately, as you didn't provided more details about the nature of your search, it is not possible for me to be more precise about how to do this search, but only tell you you have to search using the unit of work and connecting to the database if null results are returned by the first search. Also the order in which you'll do this search (before in the database and then in the unit of work or viceversa) is up to you.
Hope this will help.
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.
We are having trouble in deciding where to put the ->flush() call in a Symfony2 application. Let's see if you can "inspire" us, please.
Our application is very big. It currently has about 30 bundles. We have 2 separate developer teams: one does frontend (controllers + twigs) and another does core (database + services + model, etc).
Frontend is one project (has its own bundles, which do not have any doctrine models nor logic nor services, but have twigs, public images and css and controllers), and lives in one repository.
Core is another project (has its own bundles, which offer services, model objects, etc, has doctrine objects in their inside and have no controllers nor twigs), and lives in another repo.
The goal of this approach is that our product is delivered with DIFFERENT FRONTENDS (Core+Frontend1 for the web, Core+Frontend2 for the mobiles, Core+Frontend3 for the support-team with a special web to admin the normal users). So all "logic" is "in the core" and either one or other frontend project is consuming the same services, so an improvement in the Core, improves all the deploys without having to re-test every piece of frontend.
So... we are trying that the controllers NEVER access the doctrine objects, but acces a "modelling layer", so if ever the persistance layer changes, the controllers and twigs (ie: all the frontend) remains without a single change so we only have to re-test the core but not the frontend.
We are trying to make a MODEL in such a way that all access to DB in "encapsulated" so the controllers do NOT access the doctrine but to "services" that in turn use doctrine. Suppose we treat the objects "cars" and "people", then a controller can access a "cars_manager" service or a "people_manager" service from which to do ALL necessary operations (create objects, retrieve them, etc).
Where would you put the flush call?
Example (in pseudo-code, to make it simpler to read):
controller AjaxJsonAddDriverToCar( $CarId, $DriverId )
{
try
{
$Cars = getService( "core.cars_manager" );
$Car = $Cars->getCarById( $CarId );
$Car->addDriver( $DriverId );
$Result = JSON_OK;
}
catch
{
$Result = JSON_FAIL;
}
return $Result;
}
Provided that the controller does NOT know how the core is implemented... it should NOT get the doctrine and perform a ->flush() on it.
Inspiration is welcome.
Thanks.
To avoid calling flush from the controller, I suggest encapsulating all the code that updates the database for a particular controller action into a service method which calls flush() at the end, in which case flush() won't be called if the service method throws an exception.
In the example you have given this can be accomplished by replacing:
$Cars = getService( "core.cars_manager" );
$Car = $Cars->getCarById( $CarId );
$Car->addDriver( $DriverId );
$Result = JSON_OK;
with:
$Cars = getService( "core.cars_manager" );
$Cars->addDriverToCar($CarId, $DriverId);
$Result = JSON_OK;
and CarsManager::addDriverToCar would be something like:
$Car = $this->getCarById( $CarId );
$Car->addDriver( $DriverId );
$this->getEntityManager()->flush();
However, this is a fairly simplistic example as it only updates a single Entity and the beauty of flush is that it saves changes to all the entities you have added/removed/updated, constituting the completion of a unit of work.
The approach you described mentions managers which are entity specific. Whilst there is no reason that the manager for a complex entity can't have methods that create/update/remove multiple entities of various types it is worth considering the responsibilities of your manager classes. It may be helpful to have a manager for each entity type that handles simple Find and CRUD type operations for that entity and then an additional layer of managers between the entity managers and the controllers that handle the processing for a particular feature or set of features.
My first thought was some kind of active record, where you would tell the car to save itself. As Car is only boilerplate code, it could be ok that it knows about the database implementation and accesses some services.
My second thought was that the cars manager should know about the saving, so it would be something very similar to the entity manager and you woudl tell him flush and he flushes. You would basically abstract the entity manager and make him a bit easier to use (as there is no repository which one uses directly).
My third thought was wtf. I understand that you want to seperate the frontend from the backend. I don't understand why the frontend cannot operate on models but needs to operate on boilerplate code. The funny thing is: If the models change, so do your layers in between. If you don't want to change the layer, you could also not change the model (it's the same either way). E.g. you want to remove a field from the database: Remvoe the annotation and ignore it. No harm done. If you rename it, you can always have the old getter and setter in place, operating on the new name. And so on.
Of course I don't see the whole picture, but you may want to think this through again ;)
And here is another thought: Maybe you want to just tell the abstraction layer if the whole thing was a success or failure and he does everything what needs to be done (flushing the database, writing logs, sending emails and so on). If you can narrow your use cases down to success and failure and the service knows what to do, then this might be the easiest solution.