Add handling of usage to DAO generator
authorEileen McNaughton <emcnaughton@wikimedia.org>
Mon, 20 Mar 2023 00:07:10 +0000 (13:07 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 21 Mar 2023 00:23:24 +0000 (13:23 +1300)
CRM/Core/CodeGen/Specification.php
CRM/Core/DAO/Address.php
tests/phpunit/CRM/Contact/SelectorTest.php
xml/schema/Core/Address.xml
xml/templates/dao.tpl

index 9f3aaea73769fb602c8bd2b6cfdcde240613bd3c..c26671270e1c32e81d692948f3133585beef2766 100644 (file)
@@ -23,7 +23,7 @@ class CRM_Core_CodeGen_Specification {
     $this->buildVersion = $buildVersion;
 
     if ($verbose) {
-      echo "Parsing schema description " . $schemaPath . "\n";
+      echo 'Parsing schema description ' . $schemaPath . "\n";
     }
     $dbXML = CRM_Core_CodeGen_Util_Xml::parse($schemaPath);
 
@@ -45,13 +45,13 @@ class CRM_Core_CodeGen_Specification {
 
     // add archive tables here
     foreach ($this->tables as $name => $table) {
-      if ($table['archive'] == 'true') {
+      if ($table['archive'] === 'true') {
         $name = 'archive_' . $table['name'];
         $table['name'] = $name;
         $table['archive'] = 'false';
         if (isset($table['foreignKey'])) {
           foreach ($table['foreignKey'] as $fkName => $fkValue) {
-            if ($this->tables[$fkValue['table']]['archive'] == 'true') {
+            if ($this->tables[$fkValue['table']]['archive'] === 'true') {
               $table['foreignKey'][$fkName]['table'] = 'archive_' . $table['foreignKey'][$fkName]['table'];
               $table['foreignKey'][$fkName]['uniqName']
                 = str_replace('FK_', 'FK_archive_', $table['foreignKey'][$fkName]['uniqName']);
@@ -355,7 +355,7 @@ class CRM_Core_CodeGen_Specification {
 
       default:
         $field['sqlType'] = $type;
-        if ($type == 'int unsigned' || $type == 'tinyint') {
+        if ($type === 'int unsigned' || $type === 'tinyint') {
           $field['crmType'] = 'CRM_Utils_Type::T_INT';
         }
         else {
@@ -372,13 +372,28 @@ class CRM_Core_CodeGen_Specification {
     $field['comment'] = $this->value('comment', $fieldXML);
     $field['deprecated'] = $this->value('deprecated', $fieldXML, FALSE);
     $field['default'] = $this->value('default', $fieldXML);
-    $field['import'] = $this->value('import', $fieldXML);
-    if ($this->value('export', $fieldXML)) {
-      $field['export'] = $this->value('export', $fieldXML);
+    $import = $this->value('import', $fieldXML) ? strtoupper($this->value('import', $fieldXML)) : 'FALSE';
+    $export = $this->value('export', $fieldXML) ? strtoupper($this->value('export', $fieldXML)) : NULL;
+    if (!isset($fieldXML->usage)) {
+      $usage = [
+        'import' => $import,
+        'export' => $export ?? $import,
+      ];
     }
     else {
-      $field['export'] = $this->value('import', $fieldXML);
-    }
+      $usage = [];
+      foreach ($fieldXML->usage->children() as $usedFor => $isUsed) {
+        $usage[$usedFor] = strtoupper((string) $isUsed);
+      }
+      $import = $usage['import'] ?? $import;
+    }
+    // Ensure all keys are populated. Import is the historical de-facto default.
+    $field['usage'] = array_merge(array_fill_keys(['import', 'export', 'duplicate_matching'], $import), $usage);
+    // Usage for tokens has not historically been in the metadata so we can default to FALSE.
+    // historically hard-coded lists have been used.
+    $field['usage']['token'] = $field['usage']['token'] ?? 'FALSE';
+    $field['import'] = $field['usage']['import'];
+    $field['export'] = $export ?? $import;
     $field['rule'] = $this->value('rule', $fieldXML);
     $field['title'] = $this->value('title', $fieldXML);
     if (!$field['title']) {
@@ -730,7 +745,7 @@ class CRM_Core_CodeGen_Specification {
    * @param $object
    * @param null $default
    *
-   * @return null|string
+   * @return null|string|\SimpleXMLElement
    */
   protected function value($key, &$object, $default = NULL) {
     if (isset($object->$key)) {
index c1d7e448b9a52f5eec1b1ef3fbf0d4835022a973..d6daae72270248c4c85feb113280e198bcecad3f 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/Address.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d528ca80d405aaf51c9868b4a491329d)
+ * (GenCodeChecksum:93cff77485ca748e88906bcb5d61bf22)
  */
 
 /**
@@ -350,6 +350,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'title' => ts('Address ID'),
           'description' => ts('Unique Address ID'),
           'required' => TRUE,
+          'usage' => [
+            'import' => FALSE,
+            'export' => TRUE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.id',
           'export' => TRUE,
           'table_name' => 'civicrm_address',
@@ -367,6 +373,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Contact ID'),
           'description' => ts('FK to Contact ID'),
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.contact_id',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -383,6 +395,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Address Location Type'),
           'description' => ts('Which Location does this address belong to.'),
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.location_type_id',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -404,6 +422,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'title' => ts('Is Primary'),
           'description' => ts('Is this the primary address.'),
           'required' => TRUE,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.is_primary',
           'default' => '0',
           'table_name' => 'civicrm_address',
@@ -421,6 +445,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'title' => ts('Is Billing Address'),
           'description' => ts('Is this the billing address.'),
           'required' => TRUE,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.is_billing',
           'default' => '0',
           'table_name' => 'civicrm_address',
@@ -441,6 +471,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
       delivery, etc.).'),
           'maxlength' => 96,
           'size' => CRM_Utils_Type::HUGE,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.street_address',
           'headerPattern' => '/(street|address)/i',
@@ -460,6 +496,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Street Number'),
           'description' => ts('Numeric portion of address number on the street, e.g. For 112A Main St, the street_number = 112.'),
+          'usage' => [
+            'import' => FALSE,
+            'export' => TRUE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_number',
           'export' => TRUE,
           'table_name' => 'civicrm_address',
@@ -478,6 +520,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Non-numeric portion of address number on the street, e.g. For 112A Main St, the street_number_suffix = A'),
           'maxlength' => 8,
           'size' => CRM_Utils_Type::EIGHT,
+          'usage' => [
+            'import' => FALSE,
+            'export' => TRUE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_number_suffix',
           'export' => TRUE,
           'table_name' => 'civicrm_address',
@@ -496,6 +544,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Directional prefix, e.g. SE Main St, SE is the prefix.'),
           'maxlength' => 8,
           'size' => CRM_Utils_Type::EIGHT,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_number_predirectional',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -513,6 +567,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Actual street name, excluding St, Dr, Rd, Ave, e.g. For 112 Main St, the street_name = Main.'),
           'maxlength' => 64,
           'size' => CRM_Utils_Type::BIG,
+          'usage' => [
+            'import' => FALSE,
+            'export' => TRUE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_name',
           'export' => TRUE,
           'table_name' => 'civicrm_address',
@@ -531,6 +591,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('St, Rd, Dr, etc.'),
           'maxlength' => 8,
           'size' => CRM_Utils_Type::EIGHT,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_type',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -548,6 +614,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Directional prefix, e.g. Main St S, S is the suffix.'),
           'maxlength' => 8,
           'size' => CRM_Utils_Type::EIGHT,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_number_postdirectional',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -565,6 +637,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Secondary unit designator, e.g. Apt 3 or Unit # 14, or Bldg 1200'),
           'maxlength' => 16,
           'size' => CRM_Utils_Type::TWELVE,
+          'usage' => [
+            'import' => FALSE,
+            'export' => TRUE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.street_unit',
           'export' => TRUE,
           'table_name' => 'civicrm_address',
@@ -583,6 +661,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Supplemental Address Information, Line 1'),
           'maxlength' => 96,
           'size' => CRM_Utils_Type::HUGE,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.supplemental_address_1',
           'headerPattern' => '/(supplemental(\s)?)?address(\s\d+)?/i',
@@ -604,6 +688,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Supplemental Address Information, Line 2'),
           'maxlength' => 96,
           'size' => CRM_Utils_Type::HUGE,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.supplemental_address_2',
           'headerPattern' => '/(supplemental(\s)?)?address(\s\d+)?/i',
@@ -625,6 +715,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Supplemental Address Information, Line 3'),
           'maxlength' => 96,
           'size' => CRM_Utils_Type::HUGE,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.supplemental_address_3',
           'headerPattern' => '/(supplemental(\s)?)?address(\s\d+)?/i',
@@ -646,6 +742,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('City, Town or Village Name.'),
           'maxlength' => 64,
           'size' => CRM_Utils_Type::BIG,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.city',
           'headerPattern' => '/city/i',
@@ -665,6 +767,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('County ID'),
           'description' => ts('Which County does this address belong to.'),
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.county_id',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -689,6 +797,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('State/Province ID'),
           'description' => ts('Which State_Province does this address belong to.'),
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.state_province_id',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -716,6 +830,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Store the suffix, like the +4 part in the USPS system.'),
           'maxlength' => 12,
           'size' => 3,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.postal_code_suffix',
           'headerPattern' => '/p(ostal)\sc(ode)\ss(uffix)/i',
@@ -737,6 +857,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Store both US (zip5) AND international postal codes. App is responsible for country/region appropriate validation.'),
           'maxlength' => 64,
           'size' => 6,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.postal_code',
           'headerPattern' => '/postal|zip/i',
@@ -758,6 +884,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('USPS Bulk mailing code.'),
           'maxlength' => 32,
           'size' => CRM_Utils_Type::MEDIUM,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.usps_adc',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -771,6 +903,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Country ID'),
           'description' => ts('Which Country does this address belong to.'),
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.country_id',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -796,6 +934,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_FLOAT,
           'title' => ts('Latitude'),
           'description' => ts('Latitude'),
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.geo_code_1',
           'headerPattern' => '/geo/i',
@@ -814,6 +958,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_FLOAT,
           'title' => ts('Longitude'),
           'description' => ts('Longitude'),
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.geo_code_2',
           'headerPattern' => '/geo/i',
@@ -833,6 +983,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'title' => ts('Is Manually Geocoded'),
           'description' => ts('Is this a manually entered geo code'),
           'required' => TRUE,
+          'usage' => [
+            'import' => FALSE,
+            'export' => TRUE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.manual_geo_code',
           'export' => TRUE,
           'default' => '0',
@@ -852,6 +1008,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'description' => ts('Timezone expressed as a UTC offset - e.g. United States CST would be written as "UTC-6".'),
           'maxlength' => 8,
           'size' => CRM_Utils_Type::EIGHT,
+          'usage' => [
+            'import' => FALSE,
+            'export' => FALSE,
+            'duplicate_matching' => FALSE,
+            'token' => FALSE,
+          ],
           'where' => 'civicrm_address.timezone',
           'table_name' => 'civicrm_address',
           'entity' => 'Address',
@@ -868,6 +1030,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'title' => ts('Address Name'),
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.name',
           'headerPattern' => '/^location|(l(ocation\s)?name)$/i',
@@ -887,6 +1055,12 @@ class CRM_Core_DAO_Address extends CRM_Core_DAO {
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Master Address ID'),
           'description' => ts('FK to Address ID'),
+          'usage' => [
+            'import' => TRUE,
+            'export' => TRUE,
+            'duplicate_matching' => TRUE,
+            'token' => FALSE,
+          ],
           'import' => TRUE,
           'where' => 'civicrm_address.master_id',
           'export' => TRUE,
index d436e5273c0e469e38e7f2c05066ec57c02746d0..f1b5423e8ee3fdb6335a1b809bc662c5eaf5bdd1 100644 (file)
@@ -27,13 +27,15 @@ class CRM_Contact_SelectorTest extends CiviUnitTestCase {
   }
 
   /**
-   * Test the query from the selector class is consistent with the dataset expectation.
+   * Test the query from the selector class is consistent with the dataset
+   * expectation.
    *
    * @param array $dataSet
-   *   The data set to be tested. Note that when adding new datasets often only form_values and expected where
-   *   clause will need changing.
+   *   The data set to be tested. Note that when adding new datasets often only
+   *   form_values and expected where clause will need changing.
    *
    * @dataProvider querySets
+   * @throws \Civi\Core\Exception\DBQueryException
    */
   public function testSelectorQuery(array $dataSet): void {
     $tag = $this->callAPISuccess('Tag', 'create', [
index 9937b31a5c5f33e73c7d810a420445e34dac9e8a..f9f06ed27ca0e46697f365d478a0c0e01e05a3c7 100644 (file)
     <uniqueName>address_id</uniqueName>
     <type>int unsigned</type>
     <export>true</export>
+    <usage>
+      <import>false</import>
+      <export>true</export>
+      <duplicate_matching>false</duplicate_matching>
+    </usage>
     <title>Address ID</title>
     <required>true</required>
     <comment>Unique Address ID</comment>
index 904aefbb8f6dc5d3da8bf0ac5ee53740fd10be53..d56d62fcecba1fab8de119db01e4f91a563ce322 100644 (file)
@@ -184,17 +184,19 @@ class {$table.className} extends CRM_Core_DAO {ldelim}
 {if isset($field.cols)}
                       'cols'      => {$field.cols},
 {/if} {* field.cols *}
-
-{if $field.import}
-                      'import'    => {$field.import|strtoupper},
+                      'usage'     => array(
+                                       {foreach from=$field.usage key=usage item=isUsed}'{$usage}' => {$isUsed},
+                                       {/foreach}),
+{if $field.import === 'TRUE'}
+                      'import'    => TRUE,
 
 {/if} {* field.import *}
   'where'     => '{$table.name}.{$field.name}',
   {if $field.headerPattern}'headerPattern' => '{$field.headerPattern}',{/if}
   {if $field.dataPattern}'dataPattern' => '{$field.dataPattern}',{/if}
-{if $field.export}
-                      'export'    => {$field.export|strtoupper},
-{/if} {* field.export *}
+{if $field.export === 'TRUE' || ($field.export === 'FALSE' && $field.import === 'TRUE')}
+                      'export'    => {$field.export},
+{/if} {* field.export - only show if meaningful, deprecated for usage *}
 {if $field.contactType}
                       'contactType' => {if $field.contactType == 'null'}NULL{else}'{$field.contactType}'{/if},
 {/if}