Jump to content

Creating a polymorphic 1:1 relation: Difference between revisions

From NusaATK
Gabilan (talk | contribs)
No edit summary
 
Gabilan (talk | contribs)
No edit summary
Line 1: Line 1:
{{howto|expert|[[Gabriel Achtig]] <achtig@gmail.com>}}
{{howto|expert|[[Gabriel Achtig]] <achtig@gmail.com>}}
== Preface ==
From a [http://www.achievo.org/forum/viewtopic.php?t=1349&start=0&postdays=0&postorder=asc&highlight= thread] in the forum, regarding polymorphism and onetoone relations
From a [http://www.achievo.org/forum/viewtopic.php?t=1349&start=0&postdays=0&postorder=asc&highlight= 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.
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
Here is the db schema for this example
<pre>
<pre>
Line 36: Line 39:


</pre>
</pre>
== Polymorphic Relation ==
Here is the Relation
Here is the Relation
class.atkpolymorphiconetoonerelation.inc
class.atkpolymorphiconetoonerelation.inc
Line 122: Line 126:
</pre>
</pre>


 
== Implementation ==
Using the relation in a Fruit node, please read the previous usage comments in the atkpolymorphiconetoonerelation constructor.
Using the relation in a Fruit node, please read the previous usage comments in the atkpolymorphiconetoonerelation constructor.


Line 232: Line 236:
?>
?>
</pre>
</pre>
== 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)
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)
Gabriel

Revision as of 21:49, 12 July 2006

ATK Howto: Creating a polymorphic 1:1 relation

Complexity: expert
Author: Gabriel Achtig <achtig@gmail.com>

List of other Howto's

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
-- 
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), descrition 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 the entity
-- 

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)