4

Problem of the concrete kind

I've an object A (an entity) that manipulates some companies (entity), companies can be linked together (if they have some common shareholder for example). I want A to be able to know wether or not a company C1 is linked with a company C2.

Feelings

Within my state of knowledge and habits, I think that there should be a method in the company Entity to tell wether or not it's connected with another one (point 1). Of courses I can do it by getting all the company connexions, and looking wether or not my company C2 is in there (point 2). But this is dirty, it means getting all dependencies for what reason ? Getting a boolean that could be retrieved easily from the database, I could think about making a function in the repository linked with the companies (point 3). But no, because nothing is available in the entities (either A or C1 and C2), light weight objects remember.

Argumentations

  • Point 1 is based on the Single Responsibility Principle. Feel free to criticize that point. Since entity are supposed to embed the model logic, I think the isConnectedWith function should be right in the definition of the CompanyEntity. Please remember here that A is also an entity (so repository here too)
  • Point 2 is a solution, it's good in one sense : it's the right way to do, if, that's a big if, what Doctrine is only simulating was true; if simply calling $object->connexions accessed to some objects collection that was already there, somewhere in the memory. But the truth is awful : it's not there, it lived the common bottleneck of our webapp : database.
  • Point 3 I'm getting sick of that Data Mapper & Dependency Injection combo telling that your entities (and a bit repositories) should live only by themselves because they are the model logic. Maybe I'm terribly wrong on my understanding, but since everybody is telling it's a bad thing, I've completely dropped the idea of injecting helper or configuration in the entities.

Questions

How would you solved the problem maintaining integrity and performances and avoiding dirty workaround ? What points of my argumentations are bad ?

AsTeR
  • 7,247
  • 14
  • 60
  • 99
  • This is a nice question. Let's see if I'm able to give you a good answer – DonCallisto Mar 01 '13 at 09:00
  • Seen your comment in here: http://stackoverflow.com/questions/9440519/symfony2-mvc-where-does-my-code-belong/10167126#comment21337103_10167126 (thanks). Allow me a couple of days, 3am in Spain. Too late to start writing now. Tomorrow I'll try to explain how we do things internally in http://www.guparty.com separating "model" from "data-storage". We have more than 90 bundles!!! (one takes care of users, another of users' profiles, other takes care of communication between people, another bundle of people's connections, etc). So either we do things *very* clear or we die. 90+!!! Tomorrow more. – Xavi Montero Mar 02 '13 at 02:21
  • By the way... can you tell which "real world" class is A? I understand, by the way, that there are (at least) `companies`, `shareholders` and `other-objects` (the last ones are A). Can you give a real-name for them, please? Writing the examples will be easier. In other words: *who* is willing to know if two `companies` are related via common `shareholders`? – Xavi Montero Mar 02 '13 at 02:40
  • Well, I've started to work in the sample project. I'll show how to build a MODEL from the ground-up taking a FRESH symfony2 install. I will assume that "A" are "police inspectors" that are the in the anti-corruption and must follow if companies C1 and C2 are related or not. Additionally for simplicity I'll assume only "people" can be shareholder of a company. That's not real: companies can also have shares of other companies, but I'll assume "companies" and "shareholders" are different objects. So my objects will be `Company`, `Shareholder` and `PoliceInspector`. Works? Give me 48h to build. – Xavi Montero Mar 02 '13 at 12:06
  • Let's call them `PoliceInspector`and consider that we want to add them a method to `pickNewCompanyToInspect` which would require to know if a company has been linked with another non trustful company. – AsTeR Mar 02 '13 at 13:38
  • At this moment, I have programmed a fully-functional example with Model/Data separation, with 2 major functionalities: `Economy` which models companies and their shareholders and `Inspection` which models inpectors and inspected companies which are not else than normal companies in the context of being inspected. In fact, I've programmed 2 complete sets of models: One "mock-model" that models a "mock-world" to test and that never accesses the database, and one "real-model" that models a "real-world" of companies extracted via Doctrine from the database. I'll post everything tomorrow. – Xavi Montero Mar 04 '13 at 01:01
  • @XaviMontero Did you forget me ? ;) – AsTeR Mar 07 '13 at 09:35
  • I did not forget you, just overloaded with normal job, which consumes me 16hrs/day + weekends... Example was already finished, and I was just writing the text of the post. Sorry for the delay, I'll post in brief. Really I feel very much the delay but we've incorporated a new team of people at http://www.guparty.com and these last 2 weeks have really been brutal. But not forgotten you! – Xavi Montero Mar 18 '13 at 20:29
  • @XaviMontero I'm waiting ;) – AsTeR Mar 18 '13 at 20:36

1 Answers1

4

Let's analyze your points

Point 3

Point 3 isn't a solution at all! Who told that inject pretty much everything into entity isn't a good solution, is absolutely right. This is nearly a "standard-de-facto" of best practices of OO and pattern programming.
Entity is there only for represent an "object" (not in IT acception, of course!) so, property and accessor methods are the only things you have to include.
Think about a situation where you inject something that change could change with time (methods signature, methods return type or logic): in those case, you have to change entity itself for keep the things works together, but why you have to do this into entity, that isn't changed? Think deeply about it because is a good point of start for OO programming (not symfony2 or entity representation only!)

Point 2

Yes, you're right: why fetch from DB all entity or, if you write a "good" query, only entity you're searching for, if you'll never use it?
About this point you have to analyze somethings like:

  • Have I to use this (or those) entities elsewhere, so fetch it could be good?
  • Did I know that Doctrine2 will keep in memory where he fetches the objects(*)? That means, when you query (or ask) for the same object previously fetched, he returns to you the same instance of that object? So, no DB connections, no fecth, no heavyweight operations.

Point 1

Yes, this is a good point. You have to implement (in repository, of course!) a method like ->isThere() or something better (this is first name that came in mind).
With this method you can write your custom SQL (called D QL, where D stands for Doctrine) where you return only a flag or an intenger (obtained with COUNT(*) or aggregative functions alike).
For fetch not an ArrayCollection of entity but a scalar result, as the name suggest you, use $query->getSingleScalarResult();
I suppose that I would incline for this last solution.

Hope to had explained all well


(*) This is called identity map

Community
  • 1
  • 1
DonCallisto
  • 29,419
  • 9
  • 72
  • 100
  • What I was used to do with ActiveRecord seemed cleaner, because to the question "where shall this function be declared ?", there was no mentioned needed about how it should be concretely implemented in order to answer. You didn't answer one point : how would you do it ? (picking point 1 ?) – AsTeR Mar 01 '13 at 10:35
  • @AsTeR: yes, I've answer. Read about point 1. (just edited for make it clear) – DonCallisto Mar 01 '13 at 10:37
  • by the way thanks for the detailed answer. Last point how do you called that CompanyRepository in Entity A ? You can't, so have to have absolutely no logic in the entities ? – AsTeR Mar 01 '13 at 10:40
  • @AsTeR: You don't have to call it INSIDE (if I understood fine what you're asking for) an entity. You have to rietrieve inside your controller (or Model, or Manager, or whatever you migrate your core logic) an entity manager and use it, with some parameters (i.e.: id of the entity you call "A") – DonCallisto Mar 01 '13 at 10:44
  • Okay, so again, I should blow the idea of seeing entities as model, they definitely have nothing to do with it. BTW I strongly discourage any reader to put any business logic in a controller. – AsTeR Mar 01 '13 at 10:47
  • 1
    @AsTeR: no, you didn't understood what I mean. Entities are entities. They aren't a model. Custom DQL have to be placed inside Repository. Entity, as I mention in "point 3" of my answer, has to be as clean as possibile. So, as I told you into previous comment (maybe I wasn't so clear, sorry) you have to call the snippet of code that you'll define inside your repository, where you place your business logic (Model,Manager, or whatever you want). Controller haven't NOTHING to do with B.L. I perfectly know. BUT you have to recall FROM controller, your Model.. This is what I mean. Is clear, now? :) – DonCallisto Mar 01 '13 at 10:54