7

I'm seeking clarification on whether to put code in a controller, an entity or to make a service.

I have 'cardset' and 'card' objects (where many of the latter are embedded in the former, MongoDB), represented by normal PHP classes/objects. These contain attributes e.g. 'id', 'postal_address'.

I have a method that generates a PDF of a card. Currently I have that inside the 'Card' object so from a Controller I can call:

$card->makePDF()

That seems clean and OO to me, but I suspect I'm wrong.

If I put all the logic in the controller that gets long and unwieldy, and I'm not sure the controller is the place for methods that act on my objects. Is that what services are for?

To try and summarise: should an object know all the regular things it could do 'to itself' and have them inside as member functions, or should methods elsewhere be passed the object to act upon. If so, where should those methods be kept?

I'm pretty sure it's not a 'Repository' because that just seems to help retrieve/store entities.

Thanks!

hakre
  • 193,403
  • 52
  • 435
  • 836
Adam Knowles
  • 492
  • 5
  • 14

3 Answers3

7
  • Symfony2 is NOT an MVC structure (as said by Fabien himself) precisely because it gives all the V (twig) and C (controllers) but does NOT give the M part. The M part is "free" to be built as you want.

  • There is a major confussion, people "think" that Doctrine IS the model. But that's not true. What we do is TWO directories in the Bundle, one called "Document" for the Doctrine-ODM classes and one called "Model" where the "business logic" reside.

Personally I see that $card->makePDF() makes sense...

But $card should be a "model card", which inherits or has an underlying object "data card" which is the doctrine class.

You can play with inheritance, or with interfaces, with creators or whatever you want to relate "model-card" to "data-card" but the key is that "doctrine is not BUSINESS model" it is simply a persistance layer and your model are "plain classes" that you can build to wrap your data inside and make the controllers to consume the model, not the data.

Xavi Montero
  • 9,239
  • 7
  • 57
  • 79
  • 1
    This is by far one of the most interesting point I've hear about Symfony2 and Doctrine. By the way, how do you use some inheritance over Doctrine classes ? Doctrine is still returning it's entity class ? If you have some time can you give a look to http://stackoverflow.com/q/15153738 – AsTeR Mar 01 '13 at 08:57
  • Commented there. Allow me 24 or 48 to answer. – Xavi Montero Mar 02 '13 at 02:24
7

Short answer:

PDF generation should be a service, not a method on an object.

Longer answer:

In general, and in Symfony2 especially, models should just be used to store data. Controllers are used to manipulate relationships between models, and Views are used to express the data in human- or computer-readable form. Services are for things that don't really fit into any of the above -- things that don't have to do with your web application's state.

A good example is sending emails. Emails contain data (model). Users have sent Emails (controller). Emails look a certain way (view). However, the act of actually sending emails is independent of the web application's state (all the service knows is it was asked to send this email to these people). Thus, it makes sense that there is an independent service that just handles sending emails.

Similarly, the act of generating PDF files is independent of the state of the web application. A PDF generator doesn't need to know about what's going on in your app, it just knows it was asked to make a PDF.

Rodney Folz
  • 6,709
  • 2
  • 29
  • 38
  • I can understand 'send an email' being a service because it's a utility. I just want to check my situation isn't different. My 'makePDF()' method isn't just to make a PDF (it uses TCPDF behind, which is more like that), it iterates over cards in a cardset making each into a specific format of PDF. So its closely tied to a particular object (card/cardset) and will only ever be given one of those to work with. Assume nevertheless the right place for it is a service, with no strict relationship to the object? – Adam Knowles Feb 26 '12 at 12:29
  • 6
    I think you can't say "In general, and in Symfony2 especially, models should just be used to store data.". In general, models can store **business logic** and data. When they store only data, it's called anemic model and considered as anti-pattern. Look at 'Report' entity. The requirements say: "can be stored as a file". That leads to `Report::save` method. Another point is that you should decouple thigns, and pdf generator should be implemented as A separated class (service) and injected into your model. PS: `makePdf` is a bad name. – meze Mar 02 '12 at 13:37
  • I agree to much with @meze comment to not down vote. The idea of putting big things in controller is the best way to duplicate code and ruin unit testing efficiency. – AsTeR Mar 01 '13 at 09:04
2

If you will follow SOLID principles you will get to the SRP which states that your class should have one single responsibility.

I think it is obvious that generating one pdf is something diferent than modeling data and mapping your database( your entity does that)

catalinux
  • 1,462
  • 14
  • 26