Use trait instead of class for Entity Bridges; add OptionList trait
authorColeman Watts <coleman@civicrm.org>
Sat, 21 Nov 2020 02:53:30 +0000 (21:53 -0500)
committerColeman Watts <coleman@civicrm.org>
Sat, 21 Nov 2020 02:53:30 +0000 (21:53 -0500)
Traits allow each entity to have > 1 type
This excludes both EntityBridge and OptionList types from the
main search dropdown.

23 files changed:
Civi/Api4/ActivityContact.php
Civi/Api4/Country.php
Civi/Api4/DashboardContact.php
Civi/Api4/Entity.php
Civi/Api4/EntityTag.php
Civi/Api4/Generic/AbstractEntity.php
Civi/Api4/Generic/DAOGetAction.php
Civi/Api4/Generic/Traits/EntityBridge.php [moved from Civi/Api4/Generic/BridgeEntity.php with 80% similarity]
Civi/Api4/Generic/Traits/OptionList.php [new file with mode: 0644]
Civi/Api4/GroupContact.php
Civi/Api4/LocationType.php
Civi/Api4/MembershipType.php
Civi/Api4/OptionValue.php
Civi/Api4/PaymentProcessorType.php
Civi/Api4/Query/Api4SelectQuery.php
Civi/Api4/RelationshipCache.php
Civi/Api4/RelationshipType.php
Civi/Api4/StateProvince.php
Civi/Api4/UFJoin.php
Civi/Api4/UFMatch.php
ang/api4Explorer/Explorer.js
ext/search/Civi/Search/Admin.php
ext/search/ang/crmSearchAdmin/crmSearchAdmin.component.js

index e0d39cfb2797ac7813f43e14f64c4097f4946946..1af0de36ffa3b597b713b26bd3a3f8702f00d93d 100644 (file)
@@ -29,6 +29,7 @@ namespace Civi\Api4;
  * @see \Civi\Api4\Activity
  * @package Civi\Api4
  */
-class ActivityContact extends Generic\BridgeEntity {
+class ActivityContact extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
 
 }
index c1845d7ba0340585e93eb3e3efd72e0c4c4fe966..ccc94427890b182a1878c137407bef0a6a8863aa 100644 (file)
@@ -24,5 +24,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class Country extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index 313327204109da16e053694d1fdbed1e3d23febe..4ca8b6ecf61b7945971dcdda6fa95b78da40c769 100644 (file)
@@ -26,6 +26,7 @@ namespace Civi\Api4;
  * @see \Civi\Api4\Dashboard
  * @package Civi\Api4
  */
-class DashboardContact extends Generic\BridgeEntity {
+class DashboardContact extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
 
 }
index 883e80dc5c5c47f5f5a2f5ebcff7538b355a5b9f..e326df383075d56de198430ad112e63bd65009f5 100644 (file)
@@ -58,8 +58,15 @@ class Entity extends Generic\AbstractEntity {
         ],
         [
           'name' => 'type',
+          'data_type' => 'Array',
           'description' => 'Base class for this entity',
-          'options' => ['DAOEntity' => 'DAOEntity', 'BasicEntity' => 'BasicEntity', 'BridgeEntity' => 'BridgeEntity', 'AbstractEntity' => 'AbstractEntity'],
+          'options' => [
+            'AbstractEntity' => 'AbstractEntity',
+            'DAOEntity' => 'DAOEntity',
+            'BasicEntity' => 'BasicEntity',
+            'EntityBridge' => 'EntityBridge',
+            'OptionList' => 'OptionList',
+          ],
         ],
         [
           'name' => 'description',
index aeefb3d747e26a8af89fbc8b1d443f7483fe3e8e..2febe72254e348cf20fe1a6d2c88101b1047454d 100644 (file)
@@ -25,6 +25,7 @@ namespace Civi\Api4;
  *
  * @package Civi\Api4
  */
-class EntityTag extends Generic\BridgeEntity {
+class EntityTag extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
 
 }
index 443a7f186ca01825e1725c3107fd793cce298720..a07dc847d17893762b6a28149a1cf58d9b01ad1f 100644 (file)
@@ -133,9 +133,12 @@ abstract class AbstractEntity {
       'name' => static::getEntityName(),
       'title' => static::getEntityTitle(),
       'title_plural' => static::getEntityTitle(TRUE),
-      'type' => self::stripNamespace(get_parent_class(static::class)),
+      'type' => [self::stripNamespace(get_parent_class(static::class))],
       'paths' => static::getEntityPaths(),
     ];
+    foreach (ReflectionUtils::getTraits(static::class) as $trait) {
+      $info['type'][] = self::stripNamespace($trait);
+    }
     $reflection = new \ReflectionClass(static::class);
     $info += ReflectionUtils::getCodeDocs($reflection, NULL, ['entity' => $info['name']]);
     unset($info['package'], $info['method']);
index 852a333ccff065f41154e11965c0b89103a8644e..055eb72370715efab6280adaa71535b4ac731e2a 100644 (file)
@@ -59,13 +59,13 @@ class DAOGetAction extends AbstractGetAction {
    *
    * - `Entity`: the name of the api entity to join onto.
    * - `Required`: `TRUE` for an `INNER JOIN`, `FALSE` for a `LEFT JOIN`.
-   * - `Bridge` (optional): Name of a BridgeEntity to incorporate into the join.
+   * - `Bridge` (optional): Name of a Bridge to incorporate into the join.
    * - `[field, op, value]...`: zero or more conditions for the ON clause, using the same nested format as WHERE and HAVING
    *     but with the difference that "value" is interpreted as an expression (e.g. can be the name of a field).
    *     Enclose literal values with quotes.
    *
    * @var array
-   * @see \Civi\Api4\Generic\BridgeEntity
+   * @see \Civi\Api4\Generic\Traits\EntityBridge
    */
   protected $join = [];
 
similarity index 80%
rename from Civi/Api4/Generic/BridgeEntity.php
rename to Civi/Api4/Generic/Traits/EntityBridge.php
index 32faea1aa588a668f48ececec085810487442fec..1123021dba96d755011cadbcc313445a4976d5ba 100644 (file)
@@ -9,13 +9,13 @@
  +--------------------------------------------------------------------+
  */
 
-namespace Civi\Api4\Generic;
+namespace Civi\Api4\Generic\Traits;
 
 /**
  * A bridge is a small table that provides an intermediary link between two other tables.
  *
- * The API can automatically incorporate a bridgeEntity into a join expression.
+ * The API can automatically incorporate a Bridge into a join expression.
  */
-class BridgeEntity extends DAOEntity {
+trait EntityBridge {
 
 }
diff --git a/Civi/Api4/Generic/Traits/OptionList.php b/Civi/Api4/Generic/Traits/OptionList.php
new file mode 100644 (file)
index 0000000..fb400cc
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Generic\Traits;
+
+/**
+ * An optionList is a small entity whose primary purpose is to supply a semi-static list of options to fields in other entities.
+ *
+ * The options appear in the field metadata for other entities that reference this one via pseudoconstant.
+ */
+trait OptionList {
+
+}
index d5935967a6399505eeba1ce7516b897081273fa5..808c1a92fd3c67b6fd9236425dadd9bf1f05be7d 100644 (file)
@@ -28,7 +28,8 @@ namespace Civi\Api4;
  *
  * @package Civi\Api4
  */
-class GroupContact extends Generic\BridgeEntity {
+class GroupContact extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
 
   /**
    * @param bool $checkPermissions
index cef52216207fadbf192241e151e0f3085b595192..35d96e91aaf09327fc8984875b4ffe33020c2c18 100644 (file)
@@ -18,5 +18,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class LocationType extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index 293c9602c1f6b1dfcb04eaefe52619b30312bad6..ed49d85f2a6965f5103da74984bcb38bc641ce4f 100644 (file)
@@ -18,5 +18,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class MembershipType extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index 09f741ebb66b9f1a1c4306df676e6bc72235b6ca..0b3e672e7846be3dd6fcd15f908e73f1bf82f6ca 100644 (file)
@@ -26,5 +26,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class OptionValue extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index 425d4ba122c12d544f60f583f52da97f786d2173..2c2b9aba6abfdf01095baceab8b5957b35f580c9 100644 (file)
@@ -25,5 +25,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class PaymentProcessorType extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index a1654769af00edc965a77e5263f174ddccf67a1c..2d1a73e265a6a6588c5bcd86b64f357d44785849 100644 (file)
@@ -594,7 +594,7 @@ class Api4SelectQuery {
   }
 
   /**
-   * Join onto a BridgeEntity table
+   * Join onto a Bridge table
    *
    * @param array $joinTree
    * @param string $joinEntity
@@ -604,13 +604,15 @@ class Api4SelectQuery {
    */
   protected function getBridgeJoin(&$joinTree, $joinEntity, $alias) {
     $bridgeEntity = array_shift($joinTree);
-    if (!is_a('\Civi\Api4\\' . $bridgeEntity, '\Civi\Api4\Generic\BridgeEntity', TRUE)) {
+    /* @var \Civi\Api4\Generic\DAOEntity $bridgeEntityClass */
+    $bridgeEntityClass = '\Civi\Api4\\' . $bridgeEntity;
+    if (!in_array('EntityBridge', $bridgeEntityClass::getInfo()['type'], TRUE)) {
       throw new \API_Exception("Illegal bridge entity specified: " . $bridgeEntity);
     }
     $bridgeAlias = $alias . '_via_' . strtolower($bridgeEntity);
     $bridgeTable = CoreUtil::getTableName($bridgeEntity);
     $joinTable = CoreUtil::getTableName($joinEntity);
-    $bridgeEntityGet = \Civi\API\Request::create($bridgeEntity, 'get', ['version' => 4, 'checkPermissions' => $this->getCheckPermissions()]);
+    $bridgeEntityGet = $bridgeEntityClass::get($this->getCheckPermissions());
     $fkToJoinField = $fkToBaseField = NULL;
     // Find the bridge field that links to the joinEntity (either an explicit FK or an entity_id/entity_table combo)
     foreach ($bridgeEntityGet->entityFields() as $name => $field) {
index 4030d331807b14538756949475fe3ce8695e6683..a206cd2c277641d96b0dd8241f163f6c9ed752dd 100644 (file)
@@ -27,6 +27,7 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class RelationshipCache extends Generic\AbstractEntity {
+  use Generic\Traits\EntityBridge;
 
   /**
    * @param bool $checkPermissions
index 5032e947fb61cea24b81ad33bef9e74ac8cce5dc..883a7c48b40f0c30f87b4caa18bdf4069b0adc1f 100644 (file)
@@ -27,5 +27,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class RelationshipType extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index 1b0eb9efe9291d305221a62747f559dc63de2794..97ec5e5259b1cbf8e1a629cce99ac00fdee4c1ee 100644 (file)
@@ -24,5 +24,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class StateProvince extends Generic\DAOEntity {
+  use Generic\Traits\OptionList;
 
 }
index 7e6369de58b7d400e3517c1bcda59ab806f58bd4..a5c8ce90088ba0f4f9efcad269c5969c0fc79da5 100644 (file)
@@ -26,5 +26,6 @@ namespace Civi\Api4;
  * @package Civi\Api4
  */
 class UFJoin extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
 
 }
index 6cc1c4ee5907e5d65b5abe181442e56eabe185dd..c7956c9d88c131971763f134b5d2f3ebdc91ed5f 100644 (file)
@@ -24,6 +24,7 @@ namespace Civi\Api4;
  *
  * @package Civi\Api4
  */
-class UFMatch extends Generic\BridgeEntity {
+class UFMatch extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
 
 }
index 4b8c0257d92155506fd2ca26723363dc15f6670a..82bce1904513f48cc5606e58a8553a8474a51e70 100644 (file)
@@ -57,7 +57,7 @@
     $scope.controls = {};
     $scope.langs = ['php', 'js', 'ang', 'cli'];
     $scope.joinTypes = [{k: false, v: 'FALSE (LEFT JOIN)'}, {k: true, v: 'TRUE (INNER JOIN)'}];
-    $scope.bridgeEntities = _.filter(schema, {type: 'BridgeEntity'});
+    $scope.bridgeEntities = _.filter(schema, function(entity) {return _.includes(entity.type, 'EntityBridge');});
     $scope.code = {
       php: [
         {name: 'oop', label: ts('OOP Style'), code: ''},
       setHelp($scope.entity, {
         description: entityInfo.description,
         comment: entityInfo.comment,
+        type: entityInfo.type,
         see: entityInfo.see
       });
     }
index bf392c57b07480fc5e0d9c9e5ba0761e9e336e86..ab90e7f02ecb862fe1e9c0dd89fa07f586924211 100644 (file)
@@ -58,7 +58,7 @@ class Admin {
   public static function getSchema() {
     $schema = [];
     $entities = \Civi\Api4\Entity::get()
-      ->addSelect('name', 'title', 'title_plural', 'description', 'icon', 'paths')
+      ->addSelect('name', 'title', 'type', 'title_plural', 'description', 'icon', 'paths')
       ->addWhere('name', '!=', 'Entity')
       ->addOrderBy('title_plural')
       ->setChain([
index a62dc05a2c10828e912f1e499bc13a116480fa58..1b29fe45c6d23affacaeabd1ef8a4f5de9d8815d 100644 (file)
       $scope.controls = {tab: 'compose'};
       $scope.joinTypes = [{k: false, v: ts('Optional')}, {k: true, v: ts('Required')}];
       $scope.groupOptions = CRM.crmSearchActions.groupOptions;
-      $scope.entities = formatForSelect2(CRM.vars.search.schema, 'name', 'title_plural', ['description', 'icon']);
+      // Try to create a sensible list of entities one might want to search for,
+      // excluding those whos primary purpose is to provide joins or option lists to other entities
+      var primaryEntities = _.filter(CRM.vars.search.schema, function(entity) {
+        return !_.includes(entity.type, 'EntityBridge') && !_.includes(entity.type, 'OptionList');
+      });
+      $scope.entities = formatForSelect2(primaryEntities, 'name', 'title_plural', ['description', 'icon']);
       this.perm = {
         editGroups: CRM.checkPerm('edit groups')
       };