3

I am developing an application for managing court interpreter services (using Doctrine and Zend Framework 2). There are a lot of people involved who have various specialized roles, hence a Person superclass, and subclasses. The class hierarchy is not complicated in a vertical sense -- one generation of inheritance is enough -- but the horizontal aspect is giving me trouble.

I don't think mapped superclasses fit my case. I also considered single-table inheritance but that would quickly get ugly because the subclasses have their own entity relationships, too much stuff to cram gracefully into a single table.

That leaves us with class table inheritance, which is a really nice fit in most respects, but... I will have plenty of cases where the subclass User (for authentication) and the subclass Interpreter will (or should) point to the same row in the parent data table, because they represent one and the same person in reality. But because of the discriminator column you have to choose one or the other, or else create two different rows holding the same data -- and the normalization police should get you for that.

I think maybe either the User or the Interpreter entity has to simply have a one-to-one relationship with the Person entity, and deal with that semi-manually. Another option I suppose would be to collapse User into Person -- but that's ugly because a lot of people will not be authenticating and will not have or need a password. I have looked at Doctrine inheritance, several entities extending the same super class and How to change and entity type in Doctrine2 CTI Inheritance (inter alia) and neither one solves this.

So, I wonder if anyone has any suggestions. Thanks.

Community
  • 1
  • 1
David
  • 815
  • 8
  • 18
  • Maybe personal preference, but I would have User as a standalone class. It may use the same underlying table as the others, but be independent just for authentication. It can then be linked to the appropriate Person objects with get methods. – Matt S May 18 '16 at 18:01
  • @Matt S: when you say it "may use the same underlying table as the others" you're suggesting that there could be more than one entity class mapped onto the same table? clever! but my problem is that the email field, which rightfully is an attribute of a User, is (as of now) a column/attribute of my Person entity, because every human has an email. maybe I'm not following... I could use getters to get to Person from User, clearly, one way or another, but I don't want authentication to get needlessly complicated -- the Doctrine module does a great job of that -- and I gather you agree. – David May 18 '16 at 20:38

2 Answers2

3

It sounds like you're managing a bunch of data about Persons, which identifies individual humans in the world. Since only some subset of the people in the system are Users, I'd argue that concerns about authentication, audit logging, notifications, etc are separate from the concerns of the Person class hierarchy.

I'd advise removing User from the Person class hierarchy. Perhaps rename it Account, to make it feel less person-y. An Account can have an owner property, which is a relation to Person. If you want to use the Person's email address as an identifier for authentication, that's fine. If you later wanted to add a username instead, that would be a property of Account, since it's only meaningful in that context.

timdev
  • 61,857
  • 6
  • 82
  • 92
  • I like it. This may well be the way forward. I was hoping not violate the basic normalization principle that a fact should exist only in one place at one time -- e.g., "there is a person named John Somebody" -- while at the same time not performing hideous contortions to avoid doing that. Or, not too hideous :-) I'll go back and play some more but thank you, this might do the trick. – David May 19 '16 at 13:15
1

Not sure about the underlying code (not a ZF developer), but I think you are confusing the behavior and the data.

To make the authentication working you do not need the inheritance really. Just found you code ontop of the interface dependencies.

To make the users able to auth (while generic person could not) - use the inheritance or, as @timdev suggested, the relation. In symfony world I'd better write this something like

class User extends Person implement UserInterface {
  //... implementation
}

class Person {
  //...
}

After that you can just be sure, that you have a UserInterface while any service stuff and authentication particularly.

In case if you have to dynamically switch the underlying class of User entity (i.e. you have Author extends Person and want allow some authors to sign in) I think the composition is the only suitable solution. Split the logic, split the entites, split the data. Be happy.

ScayTrase
  • 1,810
  • 23
  • 36
  • thanks. I take your point. I'm now convinced the way to go is composition rather than inheritance for my User. – David May 22 '16 at 14:38