DAO function to list all foreign key references to a table
authorAdam Roses Wight <awight@wikimedia.org>
Wed, 24 Apr 2013 23:16:40 +0000 (16:16 -0700)
committerAdam Roses Wight <awight@wikimedia.org>
Thu, 25 Apr 2013 19:05:57 +0000 (12:05 -0700)
This patch also begins to formally describe the entity_id/entity_table
references, introducing a new first-class object CRM_Core_EntityReference.
We still need some way to describe the restrictions on the possible values of
entity_table for each DAO, so that findReferences() doesn't have to scan
all tables using a generic pointer.

Note that I've broken compatibility with PEAR links(), this would be
easy to reimplement if we need, but it was not being unused in core.

33 files changed:
CRM/Core/DAO.php
CRM/Core/EntityReference.php [new file with mode: 0644]
CRM/Financial/BAO/FinancialType.php
api/api.php
tests/phpunit/CRM/Core/DAOTest.php [new file with mode: 0644]
xml/GenCode.php
xml/schema/ACL/ACL.xml
xml/schema/ACL/EntityRole.xml
xml/schema/Activity/ActivityAssignment.xml
xml/schema/Batch/EntityBatch.xml
xml/schema/Campaign/CampaignGroup.xml
xml/schema/Case/CaseActivity.xml
xml/schema/Contribute/Premium.xml
xml/schema/Core/ActionLog.xml
xml/schema/Core/Discount.xml
xml/schema/Core/EntityFile.xml
xml/schema/Core/EntityTag.xml
xml/schema/Core/Log.xml
xml/schema/Core/Note.xml
xml/schema/Core/UFJoin.xml
xml/schema/Financial/EntityFinancialAccount.xml
xml/schema/Financial/EntityFinancialTrxn.xml
xml/schema/Financial/FinancialItem.xml
xml/schema/Friend/Friend.xml
xml/schema/Mailing/Group.xml
xml/schema/PCP/PCPBlock.xml
xml/schema/Pledge/PledgeBlock.xml
xml/schema/Price/LineItem.xml
xml/schema/Price/SetEntity.xml
xml/schema/Project/Project.xml
xml/schema/Project/Task.xml
xml/schema/Project/TaskStatus.xml
xml/templates/dao.tpl

index 6ba99787805b770aaedce9e250900e9e33391633..36e550521e8e250fc928d6e6e2baf08cf37f114b 100644 (file)
@@ -190,20 +190,7 @@ class CRM_Core_DAO extends DB_DataObject {
    * @access protected
    */
   function initialize() {
-    $links = $this->links();
-    if (empty($links)) {
-      return;
-    }
-
     $this->_connect();
-
-    if (!isset($GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database])) {
-      $GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database] = array();
-    }
-
-    if (!array_key_exists($this->__table, $GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database])) {
-      $GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database][$this->__table] = $links;
-    }
   }
 
   /**
@@ -240,12 +227,13 @@ class CRM_Core_DAO extends DB_DataObject {
   /**
    * returns list of FK relationships
    *
+   * @static
    * @access public
    *
-   * @return array
+   * @return array of CRM_Core_EntityReference
    */
-  function links() {
-    return NULL;
+  static function getReferenceColumns() {
+    return array();
   }
 
   /**
@@ -273,9 +261,6 @@ class CRM_Core_DAO extends DB_DataObject {
       }
     }
 
-    // set the links
-    $this->links();
-
     return $table;
   }
 
@@ -1687,30 +1672,70 @@ SELECT contact_id
   }
 
   /**
-   * Check the tables sent in, to see if there are any tables where there is a value for
-   * a column
+   * Find all records which refer to this entity.
+   *
+   * @return array of objects referencing this
+   */
+  function findReferences() {
+    $links = self::getReferencesToTable(static::getTableName());
+
+    $occurrences = array();
+    foreach ($links as $refSpec) {
+      $refColumn = $refSpec->getReferenceKey();
+      $targetColumn = $refSpec->getTargetKey();
+      $params = array(1 => array($this->$targetColumn, 'String'));
+      $sql = <<<EOS
+SELECT id
+FROM {$refSpec->getReferenceTable()}
+WHERE {$refColumn} = %1
+EOS;
+      if ($refSpec->isGeneric()) {
+        $params[2] = array(static::getTableName(), 'String');
+        $sql .= <<<EOS
+    AND {$refSpec->getTypeColumn()} = %2
+EOS;
+      }
+      $daoName = CRM_Core_AllCoreTables::getClassForTable($refSpec->getReferenceTable());
+      $result = self::executeQuery($sql, $params, TRUE, $daoName);
+      while ($result->fetch()) {
+        $obj = new $daoName();
+        $obj->id = $result->id;
+        $occurrences[] = $obj;
+      }
+    }
+
+    return $occurrences;
+  }
+
+  /**
+   * List all tables which have hard foreign keys to this table.
    *
-   * This is typically used when we want to delete a row, but want to avoid the FK errors
-   * that it might cause due to this being a required FK
+   * For now, this returns a description of every entity_id/entity_table
+   * reference.
+   * TODO: filter dynamic entity references on the $tableName, based on
+   * schema metadata in dynamicForeignKey which enumerates a restricted
+   * set of possible entity_table's.
    *
-   * @param array an array of values (tableName, columnName)
-   * @param array the parameter array with the value and type
-   * @param array (reference) the tables which had an entry for this value
+   * @param string $tableName table referred to
    *
-   * @return boolean true if no value exists in all the tables
-   * @static
+   * @return array structure of table and column, listing every table with a
+   * foreign key reference to $tableName, and the column where the key appears.
    */
-  public static function doesValueExistInTable(&$tables, $params, &$errors) {
-    $errors = array();
-    foreach ($tables as $table) {
-      $sql = "SELECT count(*) FROM {$table['table']} WHERE {$table['column']} = %1";
-      $count = self::singleValueQuery($sql, $params);
-      if ($count > 0) {
-        $errors[$table['table']] = $count;
+  static function getReferencesToTable($tableName) {
+    $refsFound = array();
+    foreach (CRM_Core_AllCoreTables::getClasses() as $daoClassName) {
+      $links = $daoClassName::getReferenceColumns();
+      $daoTableName = $daoClassName::getTableName();
+
+      foreach ($links as $refSpec) {
+        if ($refSpec->getTargetTable() === $tableName
+              or $refSpec->isGeneric()
+        ) {
+          $refsFound[] = $refSpec;
+        }
       }
     }
-
-    return (empty($errors)) ? FALSE : TRUE;
+    return $refsFound;
   }
 
   /**
diff --git a/CRM/Core/EntityReference.php b/CRM/Core/EntityReference.php
new file mode 100644 (file)
index 0000000..3b53128
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Description of a one-way link between two entities
+ *
+ * This could be a foreign key or a generic (entity_id, entity_table) pointer
+ */
+class CRM_Core_EntityReference {
+  protected $refTable;
+  protected $refKey;
+  protected $refTypeColumn;
+  protected $targetTable;
+  protected $targetKey;
+
+  function __construct($refTable, $refKey, $targetTable = NULL, $targetKey = 'id', $refTypeColumn = NULL) {
+    $this->refTable = $refTable;
+    $this->refKey = $refKey;
+    $this->targetTable = $targetTable;
+    $this->targetKey = $targetKey;
+    $this->refTypeColumn = $refTypeColumn;
+  }
+
+  function getReferenceTable() {
+    return $this->refTable;
+  }
+
+  function getReferenceKey() {
+    return $this->refKey;
+  }
+
+  function getTypeColumn() {
+    return $this->refTypeColumn;
+  }
+
+  function getTargetTable() {
+    return $this->targetTable;
+  }
+
+  function getTargetKey() {
+    return $this->targetKey;
+  }
+
+  /**
+   * @return true if the reference can point to more than one type
+   */
+  function isGeneric() {
+    return ($this->refTypeColumn !== NULL);
+  }
+}
index d2f6d36d22603ec62f013c9d5278bd16d56d9752..5ac611d3d583bfaab75fb50c0ba0e50b0a9d4f62 100644 (file)
@@ -117,39 +117,19 @@ class CRM_Financial_BAO_FinancialType extends CRM_Financial_DAO_FinancialType {
    * @static
    */
   static function del($financialTypeId) {
-    //checking if financial type is present
-    $check = false;
-
-    // ensure that we have no objects that have an FK to this financial type id that cannot be null
-    $tables =
-      array(
-        array(
-          'table'  => 'civicrm_contribution',
-          'column' => 'financial_type_id'
-        ),
-        array(
-          'table'  => 'civicrm_contribution_page',
-          'column' => 'financial_type_id'
-        ),
-        array(
-          'table'  => 'civicrm_contribution_recur',
-          'column' => 'financial_type_id'
-        ),
-        array(
-          'table'  => 'civicrm_membership_type',
-          'column' => 'financial_type_id'
-        ),
-        array(
-          'table'  => 'civicrm_pledge',
-          'column' => 'financial_type_id',
-        ),
-      );
-
-    $errors = array();
-    $params = array( 1 => array($financialTypeId, 'Integer'));
-    if (CRM_Core_DAO::doesValueExistInTable( $tables, $params, $errors)) {
-      $message  = ts('The following tables have an entry for this financial type') . ': ';
-      $message .= implode( ', ', array_keys($errors));
+    $financialType = new CRM_Financial_DAO_FinancialType( );
+    $financialType->id = $financialTypeId;
+    $financialType->find(true);
+    //TODO: if (!$financialType->find(true)) {
+
+    // ensure that we have no objects that have an FK to this financial type id TODO: that cannot be null
+    $occurrences = $financialType->findReferences();
+    if ($occurrences) {
+      $tables = array();
+      foreach ($occurrences as $occurence) {
+        $tables[] = get_class($occurence);
+      }
+      $message = ts('The following tables have an entry for this financial type: %1', array( '%1' => implode(', ', $tables) ));
 
       $errors = array();
       $errors['is_error'] = 1;
@@ -158,14 +138,12 @@ class CRM_Financial_BAO_FinancialType extends CRM_Financial_DAO_FinancialType {
     }
 
     //delete from financial Type table
-    $financialType = new CRM_Financial_DAO_FinancialType( );
-    $financialType->id = $financialTypeId;
     $financialType->delete();
 
     $entityFinancialType = new CRM_Financial_DAO_EntityFinancialAccount( );
     $entityFinancialType->entity_id = $financialTypeId;
     $entityFinancialType->entity_table = 'civicrm_financial_type';
-    $entityFinancialType ->delete();
+    $entityFinancialType->delete();
     return FALSE;
   }
   
index a494d28635c0a5e283cfe07bc8d95057d1d6fc53..dd79c69f59365775d7a8f93fe3635df409d54f29 100644 (file)
@@ -354,16 +354,10 @@ function civicrm_error($result) {
 }
 
 function _civicrm_api_get_camel_name($entity, $version = NULL) {
-  static $_map = NULL;
-
   if (empty($version)) {
     $version = civicrm_get_api_version();
   }
 
-  if (isset($_map[$version][strtolower($entity)])) {
-    return $_map[$version][strtolower($entity)];
-  }
-
   $fragments = explode('_', $entity);
   foreach ($fragments as & $fragment) {
     $fragment = ucfirst($fragment);
diff --git a/tests/phpunit/CRM/Core/DAOTest.php b/tests/phpunit/CRM/Core/DAOTest.php
new file mode 100644 (file)
index 0000000..8bad54c
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+require_once 'CiviTest/CiviUnitTestCase.php';
+class CRM_Core_DAOTest extends CiviUnitTestCase {
+  function get_info() {
+    return array(
+      'name'    => 'DAO',
+      'description' => 'Test core DAO functions',
+      'group'     => 'Core',
+    );
+  }
+
+  function testGetReferenceColumns() {
+    // choose CRM_Core_DAO_Email as an arbitrary example
+    $emailRefs = CRM_Core_DAO_Email::getReferenceColumns();
+    $refsByTarget = array();
+    foreach ($emailRefs as $refSpec) {
+      $refsByTarget[$refSpec->getTargetTable()] = $refSpec;
+    }
+    $this->assertTrue(array_key_exists('civicrm_contact', $refsByTarget));
+    $contactRef = $refsByTarget['civicrm_contact'];
+    $this->assertEquals('contact_id', $contactRef->getReferenceKey());
+    $this->assertEquals('id', $contactRef->getTargetKey());
+    $this->assertEquals(FALSE, $contactRef->isGeneric());
+  }
+
+  function testGetReferencesToTable() {
+    $refs = CRM_Core_DAO::getReferencesToTable(CRM_Financial_DAO_FinancialType::getTableName());
+    $refsBySource = array();
+    foreach ($refs as $refSpec) {
+      $refsBySource[$refSpec->getReferenceTable()] = $refSpec;
+    }
+    $this->assertTrue(array_key_exists('civicrm_entity_financial_account', $refsBySource));
+    $genericRef = $refsBySource['civicrm_entity_financial_account'];
+    $this->assertEquals('entity_id', $genericRef->getReferenceKey());
+    $this->assertEquals('entity_table', $genericRef->getTypeColumn());
+    $this->assertEquals('id', $genericRef->getTargetKey());
+    $this->assertEquals(TRUE, $genericRef->isGeneric());
+  }
+
+  function testFindReferences() {
+    $params = array(
+      'first_name' => 'Testy',
+      'last_name' => 'McScallion',
+      'contact_type' => 'Individual',
+    );
+
+    $contact = CRM_Contact_BAO_Contact::add($params);
+    $this->assertNotNull($contact->id);
+
+    $params = array(
+      'email' => 'spam@dev.null',
+      'contact_id' => $contact->id,
+      'is_primary' => 0,
+      'location_type_id' => 1,
+    );
+
+    $email = CRM_Core_BAO_Email::add($params);
+
+    $refs = $contact->findReferences();
+    $refsByTable = array();
+    foreach ($refs as $refObj) {
+      $refsByTable[$refObj->__table] = $refObj;
+    }
+
+    $this->assertTrue(array_key_exists('civicrm_email', $refsByTable));
+    $refDao = $refsByTable['civicrm_email'];
+    $refDao->find(TRUE);
+    $this->assertEquals($contact->id, $refDao->contact_id);
+  }
+}
index 70a2f4bbafbb14dc94bd6517db6a9de27dc44358..d8ed559af661f59ae9220cbae6d3dc34d1d39617 100644 (file)
@@ -585,6 +585,19 @@ Alternatively you can get a version of CiviCRM that matches your PHP version
       $table['foreignKey'] = &$foreign;
     }
 
+    if ($this->value('dynamicForeignKey', $tableXML)) {
+      $dynamicForeign = array();
+      foreach ($tableXML->dynamicForeignKey as $foreignXML) {
+        if ($this->value('drop', $foreignXML, 0) > 0 and $this->value('drop', $foreignXML, 0) <= $this->buildVersion) {
+          continue;
+        }
+        if ($this->value('add', $foreignXML, 0) <= $this->buildVersion) {
+          $this->getDynamicForeignKey($foreignXML, $dynamicForeign, $name);
+        }
+      }
+      $table['dynamicForeignKey'] = $dynamicForeign;
+    }
+
     $tables[$name] = &$table;
     return;
   }
@@ -828,6 +841,15 @@ Alternatively you can get a version of CiviCRM that matches your PHP version
     $foreignKeys[$name] = &$foreignKey;
   }
 
+  function getDynamicForeignKey(&$foreignXML, &$dynamicForeignKeys) {
+    $foreignKey = array(
+      'idColumn' => trim($foreignXML->idColumn),
+      'typeColumn' => trim($foreignXML->typeColumn),
+      'key' => trim($this->value('key', $foreignXML)),
+    );
+    $dynamicForeignKeys[] = $foreignKey;
+  }
+
   protected function value($key, &$object, $default = NULL) {
     if (isset($object->$key)) {
       return (string ) $object->$key;
index a692e988116e0a9c7a8a9f13676e407b8fe052e8..9f43be136023bc17662dd0ee150ae7b9f0894df3 100644 (file)
         <comment>ID of the object possessing this ACL</comment>
         <add>1.6</add>
     </field>
+    <dynamicForeignKey>
+        <idColumn>entity_id</idColumn>
+        <typeColumn>entity_table</typeColumn>
+        <add>1.6</add>
+    </dynamicForeignKey>
     <field>
         <name>operation</name>
         <type>enum</type>
index 87fb370aac5892cbc9adeeadb2398182d497a4c1..80eaf5f10b1fd29e4da8bcabea10f06a6bad74c0 100644 (file)
         <comment>ID of the group/contact object being joined</comment>
         <add>1.6</add>
     </field>
+    <dynamicForeignKey>
+        <idColumn>entity_id</idColumn>
+        <typeColumn>entity_table</typeColumn>
+        <add>1.6</add>
+    </dynamicForeignKey>
     <field>
        <name>is_active</name>
        <type>boolean</type>
index b5daaa1859427ead44ef16f99ff012f94e6eb0fe..a60ddab71e5591bcaeed9233756ded05b3e2f966 100644 (file)
     <drop>2.0</drop>    
   </field>
 
+  <dynamicForeignKey>
+    <idColumn>activity_entity_id</idColumn>
+    <typeColumn>activity_entity_table</typeColumn>
+    <add>1.8</add>
+    <drop>2.0</drop>    
+  </dynamicForeignKey>
+
   <field> 
     <name>target_entity_table</name>
     <type>varchar</type>
     <drop>2.0</drop>    
    </field>
 
+   <dynamicForeignKey>
+    <idColumn>target_entity_id</idColumn>
+    <typeColumn>target_entity_table</typeColumn>
+    <add>1.8</add>
+    <drop>2.0</drop>    
+   </dynamicForeignKey>
+
    <field> 
     <name>activity_id</name>
     <type>int unsigned</type>
index 4131a28b51341e4d265bbde96e4b443a190d7c25..1ab00dd36e740ae9c63136313d8e6917367944f3 100644 (file)
     <comment>FK to entity table specified in entity_table column.</comment>
     <add>3.3</add>
   </field>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>3.3</add>
+  </dynamicForeignKey>
   <index>
     <name>index_entity</name>
     <fieldName>entity_table</fieldName>
index 059adbfd8ad59a95641968a81ccd7bb312c2e27e..04de043b7b63a71cf89b85ddc1ae9f51a9c782cb 100644 (file)
     <comment>Entity id of referenced table.</comment>
     <add>3.3</add>
   </field>
+
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>3.3</add>
+  </dynamicForeignKey>
 </table>
index 68e96231a39e0e4c1aeec8b10ad066d597d06e96..562fa5f2d09e55d8d9e811684f58158734483edd 100644 (file)
    <drop>2.0</drop>   
   </field>
 
+  <dynamicForeignKey>
+   <idColumn>activity_entity_id</idColumn>
+   <typeColumn>activity_entity_table</typeColumn>
+   <add>1.8</add>
+   <drop>2.0</drop>
+  </dynamicForeignKey>
 
 </table>
index d6a1eb3efb5eca59993d4ff2d10d79f7a9ffe6e3..0d7e215514eb49b851fb1b42c04b87eb70c4b97c 100644 (file)
       <required>true</required>
       <add>1.4</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>entity_id</idColumn>
+      <typeColumn>entity_table</typeColumn>
+      <add>1.4</add>
+  </dynamicForeignKey>
   <field> 
       <name>premiums_active </name> 
       <type>boolean</type> 
index b890ac625035120345edcbb398aed2cd2ac0d049..09f05039b227feb03c0256470eedc06f1682be53 100644 (file)
        <comment>name of the entity table for the above id, e.g. civicrm_activity, civicrm_participant</comment>
        <add>3.4</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>entity_id</idColumn>
+       <typeColumn>entity_table</typeColumn>
+       <add>3.4</add>
+  </dynamicForeignKey>
   <field>
       <name>action_schedule_id</name>
       <type>int unsigned</type>
index e1a4885af4b43529536692c3fad558706df007cf..7596da2185fd40285d232cb78f99306ff7ccf84e 100644 (file)
     <comment>FK to entity table specified in entity_table column.</comment>
     <add>2.1</add>
   </field>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>2.1</add>
+  </dynamicForeignKey>
   <index>
     <name>index_entity</name>
     <fieldName>entity_table</fieldName>
     <add>4.3</add>
     <onDelete>CASCADE</onDelete>
   </foreignKey>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+  </dynamicForeignKey>
   <index>
     <name>index_entity_option_id</name>
     <fieldName>entity_table</fieldName>
index fdfb1ce3af691f066032b6ab4d2c1c4bd089a6e6..bfd82606011ce59cbf3ccdb3f3f556118f36bce1 100644 (file)
       <type>int unsigned</type>
       <required>true</required>
       <comment>FK to entity table specified in entity_table column.</comment>
-       <add>1.5</add>
+      <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>entity_id</idColumn>
+      <typeColumn>entity_table</typeColumn>
+      <add>1.5</add>
+  </dynamicForeignKey>
   <index>
        <name>index_entity</name>
        <fieldName>entity_table</fieldName>
index 495795ad9b5367473c41c87d508cff774697fdea..6875b2136ec436ae52f0040c53443f99fc18ab79 100644 (file)
       <title>Entity ID</title>
       <required>true</required>
       <comment>FK to entity table specified in entity_table column.</comment>
-       <add>3.2</add>
+      <add>3.2</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>entity_id</idColumn>
+      <typeColumn>entity_table</typeColumn>
+      <add>3.2</add>
+  </dynamicForeignKey>
   <index>
        <name>index_entity</name>
        <fieldName>entity_table</fieldName>
index fc3c6d93ef015e39fa222e4ddbedff4a7ce136ef..fccbe8ac37641d41e6a96defd1ef46ab3c1a05c9 100644 (file)
        <comment>Foreign key to the referenced item.</comment>
        <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>entity_id</idColumn>
+       <typeColumn>entity_table</typeColumn>
+       <add>1.5</add>
+  </dynamicForeignKey>
   <index>
        <name>index_entity</name>
        <add>1.5</add>
index 361fc1f55820d8d8dc04c1d52f6bdef075cf574a..9934fb4d05c816eb57d189682ced152052e41df5 100644 (file)
        <comment>Foreign key to the referenced item.</comment>
        <add>1.1</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>entity_id</idColumn>
+       <typeColumn>entity_table</typeColumn>
+       <add>1.1</add>
+  </dynamicForeignKey>
   <index>
        <name>index_entity</name>
        <fieldName>entity_table</fieldName>
index 6ed456b82d7f19f845acfab071b5e16c1548e5bf..64c539329fcfa80402561bd6d23983c8aec9a8f0 100644 (file)
        <comment>Foreign key to the referenced item.</comment> 
        <add>1.3</add> 
   </field>
+  <dynamicForeignKey>
+       <idColumn>entity_id</idColumn>
+       <typeColumn>entity_table</typeColumn>
+       <add>1.3</add> 
+  </dynamicForeignKey>
   <index> 
        <name>index_entity</name> 
        <add>1.3</add> 
index 388521e7c9b67d43716b4f70adb2a2626b638266..edd95ff7984ef917937f6c61d63816ba9d6e5a55 100755 (executable)
     <add>4.3</add>
     <comment>Links to an id in the entity_table, such as vid in civicrm_financial_type</comment>
   </field>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>4.3</add>
+  </dynamicForeignKey>
   <field>  
     <name>account_relationship</name>  
     <type>int unsigned</type>  
index 4df52724153c38c95eb0a8de18ceeb754977cf42..26935a5b8fd9a0679c2831926baf2a48121ecf03 100755 (executable)
     <required>true</required>  
     <add>3.2</add>  
   </field>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>3.2</add>  
+  </dynamicForeignKey>
   <field>  
     <name>financial_trxn_id</name>  
     <type>int unsigned</type>  
index 390fbc31aafed56ddbd4abac974be1a14f19e167..540387acc7dd4103105ddc9c43694fd94474519b 100644 (file)
     <comment>The specific source item that is responsible for the creation of this financial_item</comment>
     <add>4.3</add>
   </field>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>4.3</add>
+  </dynamicForeignKey>
   <index>
     <name>UI_id</name>
     <fieldName>id</fieldName>
index 7930e5433cc0891166b184c37f64024b50429623..f79a5fc6a23a7120578fde8c8f879cc9065e8c1d 100644 (file)
        <required>true</required>
        <comment>Foreign key to the referenced item.</comment>
        <add>2.0</add>
-  </field>  
-  <field>     
+  </field>
+  <dynamicForeignKey>
+       <idColumn>entity_id</idColumn>
+       <typeColumn>entity_table</typeColumn>
+       <add>2.0</add>
+  </dynamicForeignKey>
+  <field>
       <name>title</name>  
       <type>varchar</type>
       <length>255</length>
index 706012aa4132a318fc3f68aea3a82a33e37d9c3f..dafff0f9fd3de6c422515c966cf91bc14ecdb686 100644 (file)
        <required>true</required>
        <comment>Foreign key to the referenced item.</comment>
   </field>
+  <dynamicForeignKey>
+       <idColumn>entity_id</idColumn>
+       <typeColumn>entity_table</typeColumn>
+  </dynamicForeignKey>
   <field>
         <name>search_id</name>
         <type>int</type>
index 64dd578d3f4e8979487f10988b6e4c84a0c4be0c..fbd6c504ee37bc6683f70dfc465a7a75f068aa18 100644 (file)
       <comment>FK to civicrm_contribution_page.id OR civicrm_event.id</comment>
       <add>2.2</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>entity_id</idColumn>
+      <typeColumn>entity_table</typeColumn>
+      <add>2.2</add>
+  </dynamicForeignKey>
   <foreignKey> 
       <name>entity_id</name> 
       <table>civicrm_contribution_page</table> 
       <comment>The entity that this pcp targets</comment>
       <add>4.1</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>target_entity_id</idColumn>
+      <!-- FIXME: typename and not tablename? -->
+      <typeColumn>target_entity_type</typeColumn>
+      <add>4.1</add>
+  </dynamicForeignKey>
   <field>  
       <name>supporter_profile_id</name>  
       <type>int unsigned</type>                  
index f3367ddc0265be937d16f5c4aabb8771d5918eaa..a5e1cf936f4e3879b47f336a21ca09550e1625a9 100644 (file)
       <type>int unsigned</type>
       <required>true</required>
       <comment>FK to entity table specified in entity_table column.</comment>
-       <add>2.1</add>
+      <add>2.1</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>entity_id</idColumn>
+      <typeColumn>entity_table</typeColumn>
+      <add>2.1</add>
+  </dynamicForeignKey>
   <index>
        <name>index_entity</name>
        <fieldName>entity_table</fieldName>
index a0c91157345a3a366b9a74041a2402ae2a54219a..f37afc5246083d3e6391cff7a1ded21cc400aef3 100644 (file)
     <comment>entry in table</comment>
     <add>1.7</add>
   </field>
+  <dynamicForeignKey>
+    <idColumn>entity_id</idColumn>
+    <typeColumn>entity_table</typeColumn>
+    <add>1.7</add>
+  </dynamicForeignKey>
   <field>
     <name>price_field_id</name>
     <type>int unsigned</type>
index af7c39870ebcdc2d0da608f5bd0a82249e19ea89..c16bb088b00a6ec01e6a8f6156695ca68b85eadd 100644 (file)
       <comment>Item in table</comment>
       <add>1.8</add>
   </field>
+  <dynamicForeignKey>
+      <idColumn>entity_id</idColumn>
+      <typeColumn>entity_table</typeColumn>
+      <add>1.8</add>
+  </dynamicForeignKey>
   <field>
       <name>price_set_id</name>
       <type>int unsigned</type>
index 44e3af90c2a0240a9bfbee9fd4ccedcaa36fbc59..3868915b9063a8e6ed9bcf64139a2054d6b9cc7c 100644 (file)
        <comment>Foreign key to project owner (contact, group, etc.).</comment>
        <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>owner_entity_id</idColumn>
+       <typeColumn>owner_entity_table</typeColumn>
+       <add>1.5</add>
+  </dynamicForeignKey>
   <field> 
       <name>start_date</name> 
       <title>Start Date</title>
index 21e893402370c819d357561b620191f6a782dfb1..078276866466ae5a601ddb055fb8361fbf5d9f55 100644 (file)
        <comment>Foreign key to Task owner (contact, group, etc.).</comment>
        <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>owner_entity_id</idColumn>
+       <typeColumn>owner_entity_table</typeColumn>
+       <add>1.5</add>
+  </dynamicForeignKey>
   <field>
        <name>parent_entity_table</name>
        <type>varchar</type>
        <comment>Optional foreign key to Task Parent (project, another task, etc.).</comment>
        <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>parent_entity_id</idColumn>
+       <typeColumn>parent_entity_table</typeColumn>
+       <add>1.5</add>
+  </dynamicForeignKey>
   <field> 
       <name>due_date</name> 
       <title>Due Date</title>
index 1995ef1093be2d06a2d61e9db81a9b2811a45f96..846997f49915824b2348ebbb41a9b4cb0febd508 100644 (file)
        <comment>Foreign key to responsible entity (contact, group, etc.).</comment>
        <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>responsible_entity_id</idColumn>
+       <typeColumn>responsible_entity_table</typeColumn>
+       <add>1.5</add>
+  </dynamicForeignKey>
   <field>
        <name>target_entity_table</name>
        <type>varchar</type>
        <comment>Foreign key to target entity (contact, group, etc.).</comment>
        <add>1.5</add>
   </field>
+  <dynamicForeignKey>
+       <idColumn>target_entity_id</idColumn>
+       <typeColumn>target_entity_table</typeColumn>
+       <add>1.5</add>
+  </dynamicForeignKey>
   <field> 
       <name>status_detail</name> 
       <title>Status Details</title>
index e6ec545d8db6ed25c704f6f18a206e27d566ec0f..7f55dcf3863c37a9c1eaef66620854e84097969d 100644 (file)
@@ -122,22 +122,27 @@ class {$table.className} extends CRM_Core_DAO {ldelim}
         parent::__construct( );
     {rdelim}
 
-{if $table.foreignKey}
+{if $table.foreignKey || $table.dynamicForeignKey}
     /**
-     * return foreign links
+     * return foreign keys and entity references
      *
+     * @static
      * @access public
-     * @return array
+     * @return array of CRM_Core_EntityReference
      */
-    function links( ) {ldelim}
-  if ( ! ( self::$_links ) ) {ldelim}
-       self::$_links = array(
+    static function getReferenceColumns() {ldelim}
+      if (!self::$_links) {ldelim}
+        self::$_links = array(
 {foreach from=$table.foreignKey item=foreign}
-                                   '{$foreign.name}' => '{$foreign.table}:{$foreign.key}',
+          new CRM_Core_EntityReference(self::getTableName(), '{$foreign.name}', '{$foreign.table}', '{$foreign.key}'),
 {/foreach}
-                             );
-        {rdelim}
-        return self::$_links;
+
+{foreach from=$table.dynamicForeignKey item=foreign}
+          new CRM_Core_EntityReference(self::getTableName(), '{$foreign.idColumn}', NULL, '{$foreign.key|default:'id'}', '{$foreign.typeColumn}'),
+{/foreach}
+        );
+      {rdelim}
+      return self::$_links;
     {rdelim}
 {/if} {* table.foreignKey *}