Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle different Entity-Properties for different Entity-Categories

I am facing a problem, that may be placed on programmers.stackexchange.com, too, but since it's pretty tied to Doctrine and ZF2, i chose to put it up on here. Let me introduce you to my scenario:

  • I have an Application where Users post Entities (BaseEntity).
  • The BaseEntity has the property $cagetory
  • Depending on $category the Entity however has to have additional Properties

Ab simple Example:

class BaseEntity {
    protected $id;
    protected $title;
    protected $description;
}

class MovieEntity {
    protected $id;
    protected $title;
    protected $description;
    protected $airingDateStart; // new property
    protected $airingDateEnd;   // new property
}

Now i could easily do a two-step formular where a user first chooses his Category and depending on that the EntityClass will be chosen - but i can't have that. But that's no good because what if the User put up a Movie in the BaseEntity-Category and then later wants to change the Entity into MovieEntity? So that idea isn't really a safe option.

Additional Requirements (to make stuff more complicated)

  • The Categories as well as Entities have to be controlled by Doctrine
  • Each Category is provided to the application via a single Module
  • Modules need to be placed into the application without much configuration needed (at max one DB-Query to populate the CategoryTable)

What i did so far

At first i have chosen to run with Doctrines functionality Single Table Inheritance. This allowed me to easily do things like MovieEntity extends BaseEntity and things work like a charm for adding new Entities to the Database. But the main Problem remains: what if the user changes the Category, it would change the EntityClass and that's pretty much a No-Go.

I mean yes, i could potentionally do things my current way and on the change of category manually modify the DiscriminatorColumn but that's so freaking dirty.

Another alternate approach would be that on the event of changing the category, a NEW Entity will be created and the old one would be destroyed, but that feels kind of dirty, too.

All in all i think I'm headed into the wrong direction. There may be a development-pattern that i don't know about that makes all my hard work look like a waste and things turn out super easy in the end, but it appears I'm overlooking stuff.

To maybe get a closer Idea of what I'm up for, you may have a look at my application on GitHub:

  • DuitMarketplace - this is the main application with a base-category, all the controllers, etc... The ItemController#editAction() may provide some more clues as to how i intend to automade some things.
  • DuitMarketplaceItemVehicle - a category that get's dropped into the mainapp

Thanks in advance for all the feedback i may get. I fully realize that this question is probably borderline between an existance on SO vs programmers.stackexchange, but i chose to have it here after all.

like image 450
Sam Avatar asked Oct 04 '22 22:10

Sam


1 Answers

If I'm reading your situation right, it sounds like you'd be better served by avoiding inheritance on Thing, and instead thing about category-specific attributes as properties of the relation between Things and Categories.

What about an architecture like this:

<?php

class Category {
    protected $id;
    protected $title;
    protected $things; //@ManyToOne(targetEntity="ThingCategory")
}

class Thing {
    protected $id;
    protected $title;
    protected $description;
    protected $category; //@ManyToOne(targetEntity="ThingCategory")
}    

/**
 * Use [Single|Class]-Table Inheritence to model subject-category attributes.
 * 
 * ThingCategory is just a base class.  Modules provide concrete subclasses
 * that encapsulate category-specific attributes.
 */
class ThingCategory {
    protected $id; //surrogate key, not strictly necessary
    protected $thing; //@ManyToOne(targetEntity="Thing")
    protected $category //@ManyToOne(targetEntity="Category")
}

class ThingMovieCategory extends ThingCategory{
    protected $airingStartDate;
    protected $airingEndDate;
}

class ThingCarCategory extends ThingCategory {
    protected $horespower;
    protected $numberOfDoors;
    protected $color;
}

So, now things can move between categories by replacing the ThingCategory entity associated with it. The identity of the Thing never changes, just it's relationship to the category. The attributes necessary for inclusion in that category are properties of the ThingCategory relation-entity, not of the Thing itself.

EDIT: The problem you may run into is that there's no documented way of modifying the discriminator map when subclassing entities. The unfortunate side effect is that your base module will have to know about every possible module. But that's probably not a huge concern. If it is, I believe it's possible to avoid this by having each module manipulate the ClassMetaData for the base entity, but I never bothered actually making that work.

like image 132
timdev Avatar answered Oct 07 '22 18:10

timdev