SearchKit - Default join conditions
authorColeman Watts <coleman@civicrm.org>
Thu, 21 Jan 2021 14:49:18 +0000 (09:49 -0500)
committerColeman Watts <coleman@civicrm.org>
Thu, 21 Jan 2021 14:49:18 +0000 (09:49 -0500)
Adds @ui_join_filters annotations to APIv4 entities which automatically shows that field when adding a join

https://lab.civicrm.org/dev/core/-/issues/2313

Civi/Api4/ActivityContact.php
Civi/Api4/Address.php
Civi/Api4/Entity.php
Civi/Api4/RelationshipCache.php
Civi/Api4/Utils/ReflectionUtils.php
ext/search/Civi/Search/Admin.php
ext/search/ang/crmSearchAdmin.module.js
ext/search/ang/crmSearchAdmin/crmSearchAdmin.component.js

index 1af0de36ffa3b597b713b26bd3a3f8702f00d93d..39ec1b3b95df5db0679952e79f99230283011669 100644 (file)
 namespace Civi\Api4;
 
 /**
- * ActivityContact Entity.
+ * ActivityContact BridgeEntity.
  *
- * This entity adds a record which relate a contact to activity.
+ * This connects a contact to an activity.
  *
- * Creating a new ActivityContact requires at minimum a contact_id and activity_id.
+ * The record_type_id field determines the contact's role in the activity (source, target, or assignee).
+ * @ui_join_filters record_type_id
  *
  * @see \Civi\Api4\Activity
  * @package Civi\Api4
index 038e544ea71efb6d955b1306dc08891b1b7d0f19..242afbe0bdcbb46ef1c2d9b0640fecaac8fa71ca 100644 (file)
@@ -28,6 +28,8 @@ namespace Civi\Api4;
  * Creating a new address requires at minimum a contact's ID and location type ID
  *  and other attributes (although optional) like street address, city, country etc.
  *
+ * @ui_join_filters location_type_id
+ *
  * @package Civi\Api4
  */
 class Address extends Generic\DAOEntity {
index ac61fcf3892be89ddd6f20dcdcb7ed6de392423a..7bb80be3d1d71a29c70822509a2a938f04cd81a1 100644 (file)
@@ -103,6 +103,11 @@ class Entity extends Generic\AbstractEntity {
           'data_type' => 'Array',
           'description' => 'Connecting fields for EntityBridge types',
         ],
+        [
+          'name' => 'ui_join_filters',
+          'data_type' => 'Array',
+          'description' => 'When joining entities in the UI, which fields should be presented by default in the ON clause',
+        ],
       ];
     }))->setCheckPermissions($checkPermissions);
   }
index 4c6281c3a87792ef763b45c9d25cc3e277b4bbf7..8009477cc9c620d93f636600142cb9051306347e 100644 (file)
@@ -24,6 +24,7 @@ namespace Civi\Api4;
  *
  * @see \Civi\Api4\Relationship
  * @bridge near_contact_id far_contact_id
+ * @ui_join_filters near_relation
  * @package Civi\Api4
  */
 class RelationshipCache extends Generic\AbstractEntity {
index ce3f709a2c352a1ae25ccc8cd832ae22f89c8f8c..781a87b127260fcad5395c283725000665d287ef 100644 (file)
@@ -88,9 +88,9 @@ class ReflectionUtils {
         elseif ($key == 'return') {
           $info['return'] = explode('|', $words[0]);
         }
-        elseif ($key == 'options') {
+        elseif ($key == 'options' || $key == 'ui_join_filters') {
           $val = str_replace(', ', ',', implode(' ', $words));
-          $info['options'] = explode(',', $val);
+          $info[$key] = explode(',', $val);
         }
         elseif ($key == 'throws' || $key == 'see') {
           $info[$key][] = implode(' ', $words);
index 6363b53c647b88008aa77c4de0185de89bbeee1a..f359c93fc2146a0b50133a3135a0719efeada5f2 100644 (file)
@@ -61,7 +61,7 @@ class Admin {
   public static function getSchema() {
     $schema = [];
     $entities = \Civi\Api4\Entity::get()
-      ->addSelect('name', 'title', 'type', 'title_plural', 'description', 'icon', 'paths', 'dao', 'bridge')
+      ->addSelect('name', 'title', 'type', 'title_plural', 'description', 'icon', 'paths', 'dao', 'bridge', 'ui_join_filters')
       ->addWhere('searchable', '=', TRUE)
       ->addOrderBy('title_plural')
       ->setChain([
@@ -142,6 +142,7 @@ class Admin {
           ) {
             continue;
           }
+          // For dynamic references getTargetEntities will return multiple targets; for normal joins this loop will only run once
           foreach ($reference->getTargetEntities() as $targetTable => $targetEntityName) {
             if (!isset($allowedEntities[$targetEntityName]) || $targetEntityName === $entity['name']) {
               continue;
@@ -158,6 +159,7 @@ class Admin {
                 'description' => $dynamicCol ? '' : $keyField['label'],
                 'entity' => $targetEntityName,
                 'conditions' => self::getJoinConditions($keyField['name'], $alias . '.' . $reference->getTargetKey(), $targetTable, $dynamicCol),
+                'defaults' => self::getJoinDefaults($alias, $targetEntity),
                 'alias' => $alias,
                 'multi' => FALSE,
               ];
@@ -168,6 +170,7 @@ class Admin {
                 'description' => $dynamicCol ? '' : $keyField['label'],
                 'entity' => $entity['name'],
                 'conditions' => self::getJoinConditions($reference->getTargetKey(), $alias . '.' . $keyField['name'], $targetTable, $dynamicCol ? $alias . '.' . $dynamicCol : NULL),
+                'defaults' => self::getJoinDefaults($alias, $entity),
                 'alias' => $alias,
                 'multi' => TRUE,
               ];
@@ -192,6 +195,7 @@ class Admin {
                   [$bridge],
                   self::getJoinConditions('id', $alias . '.' . $baseKey, NULL, NULL)
                 ),
+                'defaults' => self::getJoinDefaults($alias, $targetEntity, $entity),
                 'bridge' => $bridge,
                 'alias' => $alias,
                 'multi' => TRUE,
@@ -206,6 +210,7 @@ class Admin {
                     [$bridge],
                     self::getJoinConditions($reference->getTargetKey(), $alias . '.' . $keyField['name'], $targetTable, $dynamicCol ? $alias . '.' . $dynamicCol : NULL)
                   ),
+                  'defaults' => self::getJoinDefaults($alias, $baseEntity, $entity),
                   'bridge' => $bridge,
                   'alias' => $alias,
                   'multi' => TRUE,
@@ -246,4 +251,29 @@ class Admin {
     return $conditions;
   }
 
+  /**
+   * @param $alias
+   * @param array ...$entities
+   * @return array
+   */
+  private static function getJoinDefaults($alias, ...$entities):array {
+    $conditions = [];
+    foreach ($entities as $entity) {
+      foreach ($entity['ui_join_filters'] ?? [] as $fieldName) {
+        $field = civicrm_api4($entity['name'], 'getFields', [
+          'select' => ['options'],
+          'where' => [['name', '=', $fieldName]],
+          'loadOptions' => ['name'],
+        ])->first();
+        $value = isset($field['options'][0]) ? json_encode($field['options'][0]['name']) : '';
+        $conditions[] = [
+          $alias . '.' . $fieldName . ($value ? ':name' : ''),
+          '=',
+          $value,
+        ];
+      }
+    }
+    return $conditions;
+  }
+
 }
index 7e737d109667ce4f1cefade078762a2680608d1e..660611ffa2e0a939917689cf661d7f1e23706998 100644 (file)
         // Add the numbered suffix to the join conditions
         // If this is a deep join, also add the base entity prefix
         var prefix = alias.replace(new RegExp('_?' + join.alias + '_?\\d?\\d?$'), '');
-        _.each(result.conditions, function(condition) {
+        function replaceRefs(condition) {
           if (_.isArray(condition)) {
             _.each(condition, function(ref, side) {
-              if (side !== 1 && _.includes(ref, '.')) {
-                condition[side] = ref.replace(join.alias + '.', alias + '.');
-              } else if (side !== 1 && prefix.length && !_.includes(ref, '"') && !_.includes(ref, "'")) {
-                condition[side] = prefix + '.' + ref;
+              if (side !== 1 && typeof ref === 'string') {
+                if (_.includes(ref, '.')) {
+                  condition[side] = ref.replace(join.alias + '.', alias + '.');
+                } else if (prefix.length && !_.includes(ref, '"') && !_.includes(ref, "'")) {
+                  condition[side] = prefix + '.' + ref;
+                }
               }
             });
           }
-        });
+        }
+        _.each(result.conditions, replaceRefs);
+        _.each(result.defaults, replaceRefs);
         return result;
       }
       function getFieldAndJoin(fieldName, entityName) {
index 36578e224e02d1fa71b4c736f8a4b5a92d9e10d2..ad1580caec5df21fa1f67e15ab0fbe407b1f79f6 100644 (file)
             _.each(_.cloneDeep(join.conditions), function(condition) {
               params.push(condition);
             });
+            _.each(_.cloneDeep(join.defaults), function(condition) {
+              params.push(condition);
+            });
             ctrl.savedSearch.api_params.join.push(params);
             loadFieldOptions();
           }