I'm trying to achieve a subscription model that can be applied to multiple entities using a single table/class with Doctrine 2. See explanation by example below.
Schema (yml):
User:
type: entity
table: users
id: int
name: string
Subscription:
type: entity
table: subscriptions
id: int
object_type: string
object_id: int
user_id: int
Feature:
type: entity
table: features
id: int
name: string
manyToMany:
subscribers:
targetEntity: User
joinTable:
name: subscriptions
joinColumns:
object_id:
referencedColumnName: id
Issue:
type: entity
table: issues
id: int
subject: string
manyToMany:
subscribers:
targetEntity: User
joinTable:
name: subscriptions
joinColumns:
object_id:
referencedColumnName: id
The table data would look something like this:
users:
| id | name |
| 1 | John |
| 2 | Joe |
features:
| id | name |
| 1 | Feature A |
| 2 | Feature B |
issues:
| id | subject |
| 1 | Issue 1 |
| 2 | Issue 2 |
subscriptions:
| id | object_type | object_id | user_id
| 1 | feature | 1 | 1 <- John is subscribed to Feature A
| 2 | issue | 1 | 1 <- John is subscribed to Issue 1
What I'd expected to have is an additional 'distinction' field that I can have in the model's manyToMany relation for example:
manyToMany:
subscribers:
targetEntity: User
joinTable:
name: subscriptions
joinColumns:
object_id:
referencedColumnName: id
object_type:
value: feature
I know that the latter soultion doesn't exist in doctrine, however I'd be curious how would you solve this situation?
The idea would be to dynamically extend this subscription "trait" to other entities as well (eg. Project, Team, etc)
Do I have to introduce separate tables for all the subscriptions like feature_subscribers
and issue_subscribers
.. and so on, or is there a more elegant way?
UPDATE:
I don't want to know from the Subscription side the target object's type. I'm only looking for to get the subscribers (collection of User
) from entities (Feature, Issue, etc).
You can achieve this subscription table layout by using single table inheritance with a discriminator map. ( see this blog post for an example )
Lets say your subscription-manager service's subscribe(User $user, SubscriptionObject $object)
method receives a user object and an object to subscribe to ( feature or issue).
Now the subscription-manager creates either a IssueSubscription
or FeatureSubscription
object and persists it. This way doctrine would save the object_type
correctly depending on your discriminator-map.
Finally you'd have to add a listener/subscriber that dynamically adjusts the relation mapping ( relation to Issue or Feature ) of your ***Subscription object in order to have the foreign key always saved to object_id
.
A quicker way might be just using IssueSubscription->Issue and FeatureSubscription->Feature relation mappings ending in a table layout like this:
subscriptions:
| id | object_type | feature_id | issue_id | user_id
| 1 | feature | 1 | NULL | 1
| 2 | issue | NULL | 1 | 1
... then adding a simple getObject()
method to your BaseSubscription
like return ( $this->feature != null ) ? $this->feature : $this->issue;
would ommit the need for the listener.