Fix apiv4 bug on custom fields with spaces in name
authorEileen McNaughton <emcnaughton@wikimedia.org>
Sat, 1 Jul 2023 04:21:01 +0000 (21:21 -0700)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 5 Jul 2023 05:48:56 +0000 (17:48 +1200)
Civi/Api4/Query/Api4Query.php
Civi/Api4/Query/Api4SelectQuery.php
tests/phpunit/api/v4/Custom/ContactCustomJoinTest.php

index 65fc736945b45ae4b6e2348e5d11d27157242d92..15f7050cf3eabfacacee9f524f28264a0253ef6f 100644 (file)
@@ -93,7 +93,7 @@ abstract class Api4Query {
       $result = [];
       foreach ($this->selectAliases as $alias => $expr) {
         $returnName = $alias;
-        $alias = str_replace('.', '_', $alias);
+        $alias = str_replace(['.', ' '], '_', $alias);
         $result[$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
       }
       $results[] = $result;
index 9002d6b44c73d262013dce55c48fba20a6a76785..26d09e1897af42bdcd2c1772a4687d261431b9ec 100644 (file)
@@ -159,7 +159,7 @@ class Api4SelectQuery extends Api4Query {
         ], ['custom_group' => 'custom_group']);
         $customSelect = [];
         foreach ($customGroups as $groupName) {
-          $customSelect[] = "$groupName.*";
+          $customSelect[] = str_replace([' '], '_', $groupName) . '.*';
         }
         array_splice($select, $customStar, 1, $customSelect);
       }
index 99f20fbf342cb896a49e12a5ace8c7feee273052..850fd8473fb517d39c00d5c2097aa43b129655bd 100644 (file)
@@ -32,22 +32,56 @@ class ContactCustomJoinTest extends CustomTestBase {
   /**
    * Add test to ensure that in the very unusual and not really supported situation where there is a space in the
    * custom group machine name. This is not supported but has been seen in the wild and as such we have this test to lock in the fix for dev/mail#103
+   *
+   * @throws \CRM_Core_Exception
    */
-  public function testContactCustomJoin() {
+  public function testContactCustomJoin(): void {
     $customGroup = CustomGroup::create()->setValues([
       'name' => 'D - Identification_20',
       'table_name' => 'civicrm_value_demographics',
       'title' => 'D - Identification',
       'extends' => 'Individual',
     ])->execute();
+    CustomGroup::create()->setValues([
+      'name' => 'other',
+      'title' => 'other',
+      'extends' => 'Individual',
+    ])->execute();
     \CRM_Core_DAO::executeQuery("UPDATE civicrm_custom_group SET name = 'D - Identification_20' WHERE id = %1", [1 => [$customGroup[0]['id'], 'Integer']]);
     $customField = CustomField::create()->setValues([
       'label' => 'Test field',
+      'name' => 'test field',
       'custom_group_id' => $customGroup[0]['id'],
       'html_type' => 'Text',
       'data_type' => 'String',
     ])->execute();
-    Contact::get()->addSelect('*')->addSelect('custom.*')->execute();
+    \CRM_Core_DAO::executeQuery("UPDATE civicrm_custom_field SET name = 'D - Identification_20' WHERE id = %1", [1 => [$customField[0]['id'], 'Integer']]);
+    CustomField::create()->setValues([
+      'label' => 'other',
+      'name' => 'other',
+      'custom_group_id:name' => 'other',
+      'html_type' => 'Text',
+      'data_type' => 'String',
+    ])->execute();
+    $contactID = Contact::create()->setValues([
+      'contact_type' => 'Individual',
+      'first_name' => 'Ben',
+      'other.other' => 'other',
+      'D - Identification_20.D - Identification_20' => 10,
+    ])->execute()->first()['id'];
+    $this->assertEquals(10, Contact::get()->addSelect('*')
+      ->addSelect('D - Identification_20.D - Identification_20')
+      ->addWhere('id', '=', $contactID)
+      ->execute()->first()['D - Identification_20.D - Identification_20']);
+
+    // Test that calling a get with custom.* does not fatal.
+    // Ideally we would also check it returns our field - but so far I haven't
+    // figured out how to make it do that - so this maintains the prior level of cover.
+    Contact::get()
+      ->addSelect('*')
+      ->addSelect('custom.*')
+      ->addWhere('id', '=', $contactID)
+      ->execute()->first();
   }
 
   /**