Creating a polymorphic 1:1 relation: Difference between revisions
No edit summary |
|||
| Line 17: | Line 17: | ||
) | ) | ||
-- | -- | ||
-- genatt* are the generic attributes of the entity | -- genatt* are the generic attributes of the entity (shared by all types) | ||
-- | -- | ||
CREATE TABLE `fruittype` ( | CREATE TABLE `fruittype` ( | ||
| Line 26: | Line 26: | ||
) | ) | ||
-- | -- | ||
-- table holds the tablename (e.g orange), | -- table holds the tablename (e.g orange), description is shown in the dropdown | ||
-- | -- | ||
CREATE TABLE `orange` ( | CREATE TABLE `orange` ( | ||
| Line 35: | Line 35: | ||
) | ) | ||
-- | -- | ||
-- orangeatt* are the specific attributes of | -- orangeatt* are the specific attributes of this type | ||
-- | -- | ||
</pre> | </pre> | ||
== Polymorphic Relation == | == Polymorphic Relation == | ||
Here is the Relation | Here is the Relation | ||
Revision as of 21:57, 12 July 2006
|
ATK Howto: Creating a polymorphic 1:1 relation
|
Preface
From a thread in the forum, regarding polymorphism and onetoone relations
My goal was to implement a one2one relation between a master table (e.g fruit) and several detail tables (orange, apple, pear, etc). This can be useful for showing a single node with all the fruits instead of showing a node for apples, another for pears, etc.
DB Schema
Here is the db schema for this example
CREATE TABLE `fruit` ( `id` int(11) NOT NULL, `fruittype_id` int(11) NOT NULL, `genatt1` varchar(50) default NULL, `genatt2` varchar(50) default NULL, PRIMARY KEY (`id`) ) -- -- genatt* are the generic attributes of the entity (shared by all types) -- CREATE TABLE `fruittype` ( `id` int(11) NOT NULL, `table` varchar(50) NOT NULL default '', `description` varchar(50) NOT NULL default '', PRIMARY KEY (`id`) ) -- -- table holds the tablename (e.g orange), description is shown in the dropdown -- CREATE TABLE `orange` ( `fruit_id` int(11) NOT NULL, `orangeatt1` varchar(50) default NULL, `orangeatt2` varchar(50) default NULL, PRIMARY KEY (`fruit_id`) ) -- -- orangeatt* are the specific attributes of this type --
Polymorphic Relation
Here is the Relation class.atkpolymorphiconetoonerelation.inc in the relations subdir of the module
<?php
userelation("atkonetoonerelation");
class atkPolymorphicOneToOneRelation extends atkOneToOneRelation
{
/**
* The name of the foreign key field in the master node to the type table.
* @access private
* @var String
*/
var $m_typefk="";
/**
* The name of the foreign key field in the master node to the type table.
* @access private
* @var String
*/
var $m_discriminatorfield="";
/**
* $modulename The module name
* @access private
* @var String
*/
var $m_modulename="";
/**
* Default Constructor
*
* The atkPolymorphicOneToOneRelation extends atkOneToOneRelation:
* <b>Example:</b>
* <code>
* $this->add(new atkPolymorphicOneToOneRelation("details","fruittype_id","table","poly.orange",
* "poly","fruit_id",AF_CASCADE_DELETE ));
* </code>
*
* @param String $name The unique name of the attribute.
* @param String $typefk The name of the foreign key field in the master node to the type table .
* @param String $discriminatorfield The name of the field in the type table wich stores the type tablename * (a node with the same name must be created).
* @param String $defaultdest The default destination node (in module.nodename
* notation)
* @param String $modulename The module name
* @param String $refKey Specifies the foreign key
* field from the destination node that points to
* the master record.
* @param int $flags Attribute flags that influence this attributes'
* behavior.
*/
function atkPolymorphicOneToOneRelation($name,$typefk,$discriminatorfield,$defaultdest,$modulename,$refKey, $flags=0)
{
$this->atkOneToOneRelation($name,"",$refKey, $flags|AF_HIDE_LIST);
$this->m_typefk=$typefk;
$this->m_discriminatorfield=$discriminatorfield;
$this->m_destination =$defaultdest;
$this->m_modulename =$modulename;
}
function loadType()
{
return POSTLOAD;
}
/**
* Retrieve detail records from the database.
*
* Called by the framework to load the detail records.
*
* @param atkDb $db The database used by the node.
* @param array $record The master record
* @param String $mode The mode for loading (admin, select, copy, etc)
*
* @return array Sets the destination from the record and
* return the atkonetoone load function
*/
function load(&$db, $record, $mode)
{
$this->m_destination = $this->m_modulename.".".$record[$this->m_typefk][$this->m_discriminatorfield];
$this->m_destInstance = $this->m_modulename.".".$record[$this->m_typefk][$this->m_discriminatorfield];
return parent::load($db, $record, $mode);
}
}
?>
Implementation
Using the relation in a Fruit node, please read the previous usage comments in the atkpolymorphiconetoonerelation constructor.
<?php
atkimport("atk.atkmetanode");
userelation("poly.atkpolymorphiconetoonerelation");
userelation("atkmanytoonerelation");
class fruit extends atkMetaNode
{
function fruit()
{
$this->atkMetaNode();
$this->add(new atkManyToOneRelation('fruittype_id','poly.fruittype',
AF_OBLIGATORY));
$this->add(new atkPolymorphicOneToOneRelation("details",
"fruittype_id","table","poly.orange","poly",
"fruit_id",AF_CASCADE_DELETE ));
}
function initial_values()
{
$init_vals = array();
if($this->m_postvars['fruittype_id'])
{
$emp = &getNode("poly.fruittype");
$emp->addFilter($this->m_postvars['fruittype_id']);
$result = $emp->selectDb();
$init_vals["fruittype_id"] = $result['0']['id'];
}
return $init_vals;
}
function action_admin(&$handler)
{
$this->setDynamicAttributes();
return $handler->action_admin();
}
function action_add(&$handler)
{
$this->setDynamicAttributes();
return $handler->action_add();
}
function action_update(&$handler)
{
$this->setDynamicAttributes();
return $handler->action_update();
}
function action_save(&$handler)
{
$this->setDynamicAttributes();
return $handler->action_save();
}
function action_edit(&$handler)
{
$this->setDynamicAttributes();
$typeattr = &$this->getAttribute('fruittype_id');
$typeattr->addFlag(AF_READONLY);
return $handler->action_edit();
}
function setDynamicAttributes()
{
$typeattr = &$this->getAttribute('fruittype_id');
$requesturi=$_SERVER['REQUEST_URI'];
//We must remove the last fruittype_id from the url
$lasturi=substr($requesturi,0,strpos($requesturi,"&fruittype_id="));
if ($lasturi=='')$lasturi=$requesturi;//first time is not set
$typeattr->addOnChangeHandler
("window.location='".$lasturi."&fruittype_id='+newvalue;");
$polyattr = &$this->getAttribute('details');
if ($this->m_postvars['fruittype_id'])
{
$emp = &getNode("poly.fruittype");
$emp->addFilter($this->m_postvars['fruittype_id']);
$result = $emp->selectDb();
//the table field reflects the node name
$polyattr->m_destination='poly.'.$result['0']['table'];
$polyattr->m_destInstance='poly'.$result['0']['table'];
$polyattr->createDestination();
}
}
}
?>
The fruittype node
<?php
atkimport("atk.atkmetanode");
class fruittype extends atkMetaNode
{
function fruittype()
{
$this->atkMetaNode();
$this->add(new atkAttribute("table",AF_FORCE_LOAD|AF_OBLIGATORY));
}
function descriptor_def()
{
return "[description]";
}
}
?>
Conclusion
This can be useful for example in a Person-Role scenario (user as master and employee, customer, manager as details) Please notice that this is the firs working version, for example the relation is readonly in edit mode (once a fruit is a pear it cannot be changed to an orange)