Quick search:

12. Polymorphic persistency

This example requires phpPeanuts version 1.1 beta 1 or higher and is not included in 1.0. It is  situated in the example12 folder. To run it on your own server you need you need to create its table and data by executing the SQL in the file 'examples.sql/example12.sql'.  To try it out on the phpPeanuts website click here.

It extends a copy of example 9 with a polymorphism for a class Project and its superclass Activity.The example also demonstrates the use of HorizontalTablePart, see at the bottom of this page.

The polymorphism exists of the fieldproperties 'name' and 'description' inherited by Project, and the derived property 'message', retrieved from the method getMessage that is implemented on Activity but overridden on Project.

Normally, if instances of the class Activity are retrieved from the database, the framework will only look in the table 'ex_activities' as that is defined by the getTableName method. Then it will instantiate and initialize one Activity object for each record from the 'ex_activities' table. Similar for Projects: they are retrieved from the 'ex_projects' table and will only be retrieved from that table when projects are asked for. That Project is a subclass of Activity does not matter for normal persistency.

With polymorphic persistency super- and subclasses do play a role. When an Activity is saved, it is still stored in the 'ex_activities' table. But if a Project is saved, the properties it inherits from Activity (id, name and description) will also be stored in the 'ex_activities' table. The properties that are unique to Project (start and end) do not fit into the  'ex_activities' table, so they are stored in the 'ex_projects' table. When a Project is retrieved, both tables are JOINed by id. To make that possible the id is stored in both the 'ex_activities' table and the 'ex_projects' table. 

Now if Activities are retrieved, we will get data from both Projects and Activities. This is OK, the idea is that Projects are a kind of of Activities, so if you ask for Activities, you also want Projects (When you don't, maybe you should not use polymorhic persistency). In order to know for a specific record whether to instantiate a Project or an Activity the framework needs an extra column in the 'ex_activities' table to hold the name of the class.

This is arranged for in the Activity class:

  • The initPropertyDescriptors method defines a persistent field property 'clsId'.
  • The Acitivity constructor sets the field of the clsId property to the class name it gets from $this->getClass()
  • The initPropertyDescriptor method tells the classDescriptor the name of the property that holds the class name to be 'clsId'. This is done before the parent::initPropertyDescriptors calls so that the classDescriptor knows that polymorphic persistancy is to be activated and will initialize the inherited properties accordingly.

This is all the framework needs to make persistency of Activities and their subclasses polymorphic. You can see the result when you search for Activities: the derived property 'message' clearly shows that the framework actually instantiated the Projects from the class Project. And when editing Hours, you can select either a project or an as the activity the time was spent on.

This is a rather simple example of polymorhic persistency. Polymorhism can be a simple solution to make an application more flexible. But if used too easily, it will make applications unecessarily complex. And it will allways be harder for the database, especially if you use multiple layers of classes in a single polymorphism. When possible inner joins will be used, but when a project is retrieved were an Activity is asked for, the extra data will be retrieved from the ex_projects table with an extra query for each individual Project. This may be too slow for retrieving large collections. For more information see how to make a persistent class polymorphic.  

This example also demonstrates the use of HorizontalTablePart. It works like a normal TablePart, except for the colums and rows to be shown exchanged. To see it, click on the 'Hours' button when editing a Project. To get that table we added a new class ProjectPropertyHoursPage and gave it a method getInitItemTable. So far it is similar to example 6, where the same method was overridden to set event handlers on the TablePart of the EmployeeIndexPage. This time we made the method return a 'HorizontalTablePart' instead of a normal 'TablePart, initialized with the normal type, columnpaths and items.