Api4SelectQuery - add more metadata to apiFieldSpec
authorColeman Watts <coleman@civicrm.org>
Mon, 30 Mar 2020 16:22:43 +0000 (12:22 -0400)
committerColeman Watts <coleman@civicrm.org>
Wed, 1 Apr 2020 21:10:19 +0000 (17:10 -0400)
Civi/Api4/Query/Api4SelectQuery.php

index 4e7082a4dc4b8e56ee25eac40d5eb195c51987bf..38c46ad887917c028710e5fbf26b75093a26fcc5 100644 (file)
@@ -41,12 +41,6 @@ class Api4SelectQuery extends SelectQuery {
    */
   protected $apiVersion = 4;
 
-  /**
-   * @var array
-   *   Maps select fields to [<table_alias>, <column_alias>]
-   */
-  protected $fkSelectAliases = [];
-
   /**
    * @var \Civi\Api4\Service\Schema\Joinable\Joinable[]
    *   The joinable tables that have been joined so far
@@ -82,9 +76,9 @@ class Api4SelectQuery extends SelectQuery {
     }
     $baoName = CoreUtil::getBAOFromApiName($this->entity);
     $this->entityFieldNames = array_column($baoName::fields(), 'name');
-    $this->apiFieldSpec = $apiGet->entityFields();
-    foreach ($this->apiFieldSpec as $key => $field) {
-      $this->apiFieldSpec[$key]['sql_name'] = '`' . self::MAIN_TABLE_ALIAS . '`.`' . $field['column_name'] . '`';
+    foreach ($apiGet->entityFields() as $path => $field) {
+      $field['sql_name'] = '`' . self::MAIN_TABLE_ALIAS . '`.`' . $field['column_name'] . '`';
+      $this->addSpecField($path, $field);
     }
 
     $this->constructQueryObject($baoName);
@@ -145,6 +139,7 @@ class Api4SelectQuery extends SelectQuery {
   }
 
   protected function buildSelectClause() {
+    // An empty select is the same as *
     if (empty($this->select)) {
       $this->select = $this->entityFieldNames;
     }
@@ -172,19 +167,19 @@ class Api4SelectQuery extends SelectQuery {
     }
     foreach ($this->select as $fieldName) {
       $field = $this->getField($fieldName);
-      if (!$this->isOneToOneField($fieldName)) {
-        continue;
-      }
-      elseif ($field) {
-        $this->query->select($field['sql_name'] . " AS `$fieldName`");
-      }
       // Remove unknown fields without raising an error
-      else {
+      if (!$field) {
         $this->select = array_diff($this->select, [$fieldName]);
         if (is_array($this->debugOutput)) {
           $this->debugOutput['undefined_fields'][] = $fieldName;
         }
       }
+      elseif ($field['is_many']) {
+        continue;
+      }
+      elseif ($field) {
+        $this->query->select($field['sql_name'] . " AS `$fieldName`");
+      }
     }
   }
 
@@ -315,8 +310,7 @@ class Api4SelectQuery extends SelectQuery {
       $this->joinFK($fieldName);
     }
     $field = $this->apiFieldSpec[$fieldName] ?? NULL;
-    // Check if field exists and we have permission to view it
-    if ($field && (!$this->checkPermissions || empty($field['permission']) || \CRM_Core_Permission::check($field['permission']))) {
+    if ($field) {
       return $field;
     }
     elseif ($strict) {
@@ -329,13 +323,12 @@ class Api4SelectQuery extends SelectQuery {
    * Joins a path and adds all fields in the joined eneity to apiFieldSpec
    *
    * @param $key
-   * @return bool
    * @throws \API_Exception
    * @throws \Exception
    */
   protected function joinFK($key) {
     if (isset($this->apiFieldSpec[$key])) {
-      return TRUE;
+      return;
     }
 
     $pathArray = explode('.', $key);
@@ -347,15 +340,24 @@ class Api4SelectQuery extends SelectQuery {
     $pathString = implode('.', $pathArray);
 
     if (!$joiner->canJoin($this, $pathString)) {
-      return FALSE;
+      return;
     }
 
     $joinPath = $joiner->join($this, $pathString);
+
+    $isMany = FALSE;
+    foreach ($joinPath as $joinable) {
+      if ($joinable->getJoinType() === Joinable::JOIN_TYPE_ONE_TO_MANY) {
+        $isMany = TRUE;
+      }
+    }
+
     /** @var \Civi\Api4\Service\Schema\Joinable\Joinable $lastLink */
     $lastLink = array_pop($joinPath);
 
     // Custom field names are already prefixed
-    if ($lastLink instanceof CustomGroupJoinable) {
+    $isCustom = $lastLink instanceof CustomGroupJoinable;
+    if ($isCustom) {
       array_pop($pathArray);
     }
     $prefix = $pathArray ? implode('.', $pathArray) . '.' : '';
@@ -364,10 +366,11 @@ class Api4SelectQuery extends SelectQuery {
     foreach ($lastLink->getEntityFields() as $fieldObject) {
       $fieldArray = ['entity' => $joinEntity] + $fieldObject->toArray();
       $fieldArray['sql_name'] = '`' . $lastLink->getAlias() . '`.`' . $fieldArray['column_name'] . '`';
-      $this->apiFieldSpec[$prefix . $fieldArray['name']] = $fieldArray;
+      $fieldArray['is_custom'] = $isCustom;
+      $fieldArray['is_join'] = TRUE;
+      $fieldArray['is_many'] = $isMany;
+      $this->addSpecField($prefix . $fieldArray['name'], $fieldArray);
     }
-
-    return TRUE;
   }
 
   /**
@@ -574,4 +577,20 @@ class Api4SelectQuery extends SelectQuery {
     return $path;
   }
 
+  /**
+   * @param $path
+   * @param $field
+   */
+  private function addSpecField($path, $field) {
+    // Only add field to spec if we have permission
+    if ($this->checkPermissions && !empty($field['permission']) && !\CRM_Core_Permission::check($field['permission'])) {
+      $this->apiFieldSpec[$path] = FALSE;
+      return;
+    }
+    $defaults = [];
+    $defaults['is_custom'] = $defaults['is_join'] = $defaults['is_many'] = FALSE;
+    $field += $defaults;
+    $this->apiFieldSpec[$path] = $field;
+  }
+
 }