These were actually run as a separate query and "joined" to the main results array in PHP.
Removing them to make way for supporting explicit multi-joins which will use SQL.
*/
const SCHEMA_MAP_BUILD = 'api.schema_map.build';
- /**
- * Alter query results of APIv4 select query
- */
- const POST_SELECT_QUERY = 'api.select_query.post';
-
}
+++ /dev/null
-<?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 |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Event\Events;
-use Civi\Api4\Event\SchemaMapBuildEvent;
-use Civi\Api4\Service\Schema\Joinable\Joinable;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-class ContactSchemaMapSubscriber implements EventSubscriberInterface {
-
- /**
- * @return array
- */
- public static function getSubscribedEvents() {
- return [
- Events::SCHEMA_MAP_BUILD => 'onSchemaBuild',
- ];
- }
-
- /**
- * @param \Civi\Api4\Event\SchemaMapBuildEvent $event
- */
- public function onSchemaBuild(SchemaMapBuildEvent $event) {
- $schema = $event->getSchemaMap();
- $table = $schema->getTableByName('civicrm_contact');
- $this->addCreatedActivitiesLink($table);
- $this->fixPreferredLanguageAlias($table);
- }
-
- /**
- * @param \Civi\Api4\Service\Schema\Table $table
- */
- private function addCreatedActivitiesLink($table) {
- $alias = 'created_activities';
- $joinable = new Joinable('civicrm_activity_contact', 'contact_id', $alias);
- $joinable->addCondition($alias . '.record_type_id = 1');
- $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
- $table->addTableLink('id', $joinable);
- }
-
- /**
- * @param \Civi\Api4\Service\Schema\Table $table
- */
- private function fixPreferredLanguageAlias($table) {
- foreach ($table->getExternalLinks() as $link) {
- if ($link->getAlias() === 'languages') {
- $link->setAlias('preferred_language');
- return;
- }
- }
- }
-
-}
+++ /dev/null
-<?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 |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Event\Events;
-use Civi\Api4\Event\PostSelectQueryEvent;
-use Civi\Api4\Query\Api4SelectQuery;
-use Civi\Api4\Utils\ArrayInsertionUtil;
-use Civi\Api4\Utils\FormattingUtil;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Changes the results of a select query, doing 1-n joins and unserializing data
- */
-class PostSelectQuerySubscriber implements EventSubscriberInterface {
-
- /**
- * @inheritDoc
- */
- public static function getSubscribedEvents() {
- return [
- Events::POST_SELECT_QUERY => 'onPostQuery',
- ];
- }
-
- /**
- * @param \Civi\Api4\Event\PostSelectQueryEvent $event
- */
- public function onPostQuery(PostSelectQueryEvent $event) {
- $results = $event->getResults();
- $event->setResults($this->postRun($results, $event->getQuery()));
- }
-
- /**
- * @param array $results
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- *
- * @return array
- */
- protected function postRun(array $results, Api4SelectQuery $query) {
- if (empty($results)) {
- return $results;
- }
-
- FormattingUtil::formatOutputValues($results, $query->getApiFieldSpec(), $query->getEntity());
-
- // Group the selects to avoid queries for each field
- $groupedSelects = $this->getNtoManyJoinSelects($query);
- foreach ($groupedSelects as $finalAlias => $selects) {
- $joinPath = $query->getPathJoinTypes($selects[0]);
- $selects = $this->formatSelects($finalAlias, $selects, $query);
- $joinResults = $this->getJoinResults($query, $finalAlias, $selects);
- $this->formatJoinResults($joinResults, $query, $finalAlias);
-
- // Insert join results into original result
- foreach ($results as &$primaryResult) {
- $baseId = $primaryResult['id'];
- $filtered = array_filter($joinResults, function ($res) use ($baseId) {
- return ($res['_base_id'] == $baseId);
- });
- $filtered = array_values($filtered);
- ArrayInsertionUtil::insert($primaryResult, $joinPath, $filtered);
- }
- }
-
- return array_values($results);
- }
-
- /**
- * @param array $joinResults
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- * @param string $alias
- */
- private function formatJoinResults(&$joinResults, $query, $alias) {
- $join = $query->getJoinedTable($alias);
- $fields = [];
- foreach ($join->getEntityFields() as $field) {
- $name = explode('.', $field->getName());
- $fields[array_pop($name)] = $field->toArray();
- }
- if ($fields) {
- FormattingUtil::formatOutputValues($joinResults, $fields, $join->getEntity());
- }
- }
-
- /**
- * Find only those joins that need to be handled by a separate query and weren't done in the main query.
- *
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- *
- * @return array
- */
- private function getNtoManyJoinSelects(Api4SelectQuery $query) {
- $joinedDotSelects = array_filter(
- $query->getSelect(),
- function ($select) use ($query) {
- return strpos($select, '.') && array_filter($query->getPathJoinTypes($select));
- }
- );
-
- $selects = [];
- // group related selects by alias so they can be executed in one query
- foreach ($joinedDotSelects as $select) {
- $parts = explode('.', $select);
- $finalAlias = $parts[count($parts) - 2];
- $selects[$finalAlias][] = $select;
- }
-
- // sort by depth, e.g. email selects should be done before email.location
- uasort($selects, function ($a, $b) {
- $aFirst = $a[0];
- $bFirst = $b[0];
- return substr_count($aFirst, '.') > substr_count($bFirst, '.');
- });
-
- return $selects;
- }
-
- /**
- * @param array $selects
- * @param $serializationType
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- *
- * @return array
- */
- private function getResultsForSerializedField(
- array $selects,
- $serializationType,
- Api4SelectQuery $query
- ) {
- // Get the alias (Selects are grouped and all target the same table)
- $sampleField = current($selects);
- $alias = strstr($sampleField, '.', TRUE);
-
- // Fetch the results with the serialized field
- $selects['serialized'] = $query::MAIN_TABLE_ALIAS . '.' . $alias;
- $serializedResults = $this->runWithNewSelects($selects, $query);
- $newResults = [];
-
- // Create a new results array, with a separate entry for each option value
- foreach ($serializedResults as $result) {
- $optionValues = \CRM_Core_DAO::unSerializeField(
- $result['serialized'],
- $serializationType
- );
- unset($result['serialized']);
- foreach ($optionValues as $value) {
- $newResults[] = array_merge($result, ['value' => $value]);
- }
- }
-
- $optionValueValues = array_unique(array_column($newResults, 'value'));
- $optionValues = $this->getOptionValuesFromValues(
- $selects,
- $query,
- $optionValueValues
- );
- $valueField = $alias . '.value';
-
- // Index by value
- foreach ($optionValues as $key => $subResult) {
- $optionValues[$subResult['value']] = $subResult;
- unset($subResult[$key]);
-
- // Exclude 'value' if not in original selects
- if (!in_array($valueField, $selects)) {
- unset($optionValues[$subResult['value']]['value']);
- }
- }
-
- // Replace serialized with the sub-select results
- foreach ($newResults as &$result) {
- $result = array_merge($result, $optionValues[$result['value']]);
- unset($result['value']);
- }
-
- return $newResults;
- }
-
- /**
- * Prepares selects for the subquery to fetch join results
- *
- * @param string $alias
- * @param array $selects
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- *
- * @return array
- */
- private function formatSelects($alias, $selects, Api4SelectQuery $query) {
- $mainAlias = $query::MAIN_TABLE_ALIAS;
- $selectFields = [];
-
- foreach ($selects as $select) {
- $selectAlias = str_replace('`', '', $query->getField($select)['sql_name']);
- $fieldAlias = substr($select, strrpos($select, '.') + 1);
- $selectFields[$fieldAlias] = $selectAlias;
- }
-
- $firstSelect = $selects[0];
- $pathParts = explode('.', $firstSelect);
- $numParts = count($pathParts);
- $parentAlias = $numParts > 2 ? $pathParts[$numParts - 3] : $mainAlias;
-
- $selectFields['id'] = sprintf('%s.id', $alias);
- $selectFields['_parent_id'] = $parentAlias . '.id';
- $selectFields['_base_id'] = $mainAlias . '.id';
-
- return $selectFields;
- }
-
- /**
- * @param array $selects
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- *
- * @return array
- */
- private function runWithNewSelects(array $selects, Api4SelectQuery $query) {
- $aliasedSelects = array_map(function ($field, $alias) {
- return sprintf('%s as "%s"', $field, $alias);
- }, $selects, array_keys($selects));
-
- $newSelect = sprintf('SELECT DISTINCT %s', implode(", ", $aliasedSelects));
- $sql = $query->getQuery()->toSQL();
- // Replace the "SELECT" clause
- $sql = $newSelect . substr($sql, strpos($sql, "\nFROM"));
-
- if (is_array($query->debugOutput)) {
- $query->debugOutput['sql'][] = $sql;
- }
-
- $relatedResults = [];
- $resultDAO = \CRM_Core_DAO::executeQuery($sql);
- while ($resultDAO->fetch()) {
- $relatedResult = [];
- foreach ($selects as $alias => $column) {
- $returnName = $alias;
- $alias = str_replace('.', '_', $alias);
- if (property_exists($resultDAO, $alias)) {
- $relatedResult[$returnName] = $resultDAO->$alias;
- }
- };
- $relatedResults[] = $relatedResult;
- }
-
- return $relatedResults;
- }
-
- /**
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- * @param $alias
- * @param $selects
- * @return array
- */
- protected function getJoinResults(Api4SelectQuery $query, $alias, $selects) {
- $apiFieldSpec = $query->getApiFieldSpec();
- if (!empty($apiFieldSpec[$alias]['serialize'])) {
- $type = $apiFieldSpec[$alias]['serialize'];
- $joinResults = $this->getResultsForSerializedField($selects, $type, $query);
- }
- else {
- $joinResults = $this->runWithNewSelects($selects, $query);
- }
-
- // Remove results with no matching entries
- $joinResults = array_filter($joinResults, function ($result) {
- return !empty($result['id']);
- });
-
- return $joinResults;
- }
-
- /**
- * Get all the option_value values required in the query
- *
- * @param array $selects
- * @param \Civi\Api4\Query\Api4SelectQuery $query
- * @param array $values
- *
- * @return array
- */
- private function getOptionValuesFromValues(
- array $selects,
- Api4SelectQuery $query,
- array $values
- ) {
- $sampleField = current($selects);
- $alias = strstr($sampleField, '.', TRUE);
-
- // Get the option value table that was joined
- $relatedTable = NULL;
- foreach ($query->getJoinedTables() as $joinedTable) {
- if ($joinedTable->getAlias() === $alias) {
- $relatedTable = $joinedTable;
- }
- }
-
- // We only want subselects related to the joined table
- $subSelects = array_filter($selects, function ($select) use ($alias) {
- return strpos($select, $alias) === 0;
- });
-
- // Fetch all related option_value entries
- $valueField = $alias . '.value';
- $subSelects[] = $valueField;
- $tableName = $relatedTable->getTargetTable();
- $conditions = $relatedTable->getExtraJoinConditions();
- $conditions[] = $valueField . ' IN ("' . implode('", "', $values) . '")';
- $subQuery = new \CRM_Utils_SQL_Select($tableName . ' ' . $alias);
- $subQuery->where($conditions);
- $subQuery->select($subSelects);
- $subResults = $subQuery->execute()->fetchAll();
-
- return $subResults;
- }
-
-}
namespace Civi\Api4\Query;
use Civi\API\SelectQuery;
-use Civi\Api4\Event\Events;
-use Civi\Api4\Event\PostSelectQueryEvent;
use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
use Civi\Api4\Service\Schema\Joinable\Joinable;
use Civi\Api4\Utils\FormattingUtil;
$this->debugOutput['sql'][] = $sql;
}
$query = \CRM_Core_DAO::executeQuery($sql);
- $i = 0;
while ($query->fetch()) {
- $id = $query->id ?? $i++;
if (in_array('row_count', $this->select)) {
$results[]['row_count'] = (int) $query->c;
break;
}
- $results[$id] = [];
+ $result = [];
foreach ($this->selectAliases as $alias => $expr) {
$returnName = $alias;
$alias = str_replace('.', '_', $alias);
- $results[$id][$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
+ $result[$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
}
+ $results[] = $result;
}
- $event = new PostSelectQueryEvent($results, $this);
- \Civi::dispatcher()->dispatch(Events::POST_SELECT_QUERY, $event);
-
- return $event->getResults();
+ FormattingUtil::formatOutputValues($results, $this->getApiFieldSpec(), $this->getEntity());
+ return $results;
}
protected function buildSelectClause() {
});
foreach ($wildFields as $item) {
$pos = array_search($item, array_values($this->select));
- $this->joinFK($item);
+ $this->autoJoinFK($item);
$matches = SelectUtil::getMatchingFields($item, array_keys($this->apiFieldSpec));
array_splice($this->select, $pos, 1, $matches);
}
}
$valid = FALSE;
}
- elseif ($field['is_many']) {
- $valid = FALSE;
- }
}
if ($valid) {
$alias = $expr->getAlias();
$fieldName = $col ? substr($expr, 0, $col) : $expr;
// Perform join if field not yet available - this will add it to apiFieldSpec
if (!isset($this->apiFieldSpec[$fieldName]) && strpos($fieldName, '.')) {
- $this->joinFK($fieldName);
+ $this->autoJoinFK($fieldName);
}
$field = $this->apiFieldSpec[$fieldName] ?? NULL;
if ($strict && !$field) {
}
/**
- * Joins a path and adds all fields in the joined eneity to apiFieldSpec
+ * Joins a path and adds all fields in the joined entity to apiFieldSpec
*
* @param $key
* @throws \API_Exception
* @throws \Exception
*/
- protected function joinFK($key) {
+ protected function autoJoinFK($key) {
if (isset($this->apiFieldSpec[$key])) {
return;
}
array_pop($pathArray);
$pathString = implode('.', $pathArray);
- if (!$joiner->canJoin($this, $pathString)) {
+ if (!$joiner->canAutoJoin($this->getFrom(), $pathString)) {
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
$fieldArray['sql_name'] = '`' . $lastLink->getAlias() . '`.`' . $fieldArray['column_name'] . '`';
$fieldArray['is_custom'] = $isCustom;
$fieldArray['is_join'] = TRUE;
- $fieldArray['is_many'] = $isMany;
$this->addSpecField($prefix . $fieldArray['name'], $fieldArray);
}
}
}
}
- /**
- * Checks if a field either belongs to the main entity or is joinable 1-to-1.
- *
- * Used to determine if a field can be added to the SELECT of the main query,
- * or if it must be fetched post-query.
- *
- * @param string $fieldPath
- * @return bool
- */
- public function isOneToOneField(string $fieldPath) {
- return strpos($fieldPath, '.') === FALSE || !array_filter($this->getPathJoinTypes($fieldPath));
- }
-
- /**
- * Separates a string like 'emails.location_type.label' into an array, where
- * each value in the array tells whether it is 1-1 or 1-n join type
- *
- * @param string $pathString
- * Dot separated path to the field
- *
- * @return array
- * Index is table alias and value is boolean whether is 1-to-many join
- */
- public function getPathJoinTypes($pathString) {
- $pathParts = explode('.', $pathString);
- // remove field
- array_pop($pathParts);
- $path = [];
- $query = $this;
- $isMultipleChecker = function($alias) use ($query) {
- foreach ($query->getJoinedTables() as $table) {
- if ($table->getAlias() === $alias) {
- return $table->getJoinType() === Joinable::JOIN_TYPE_ONE_TO_MANY;
- }
- }
- return FALSE;
- };
-
- foreach ($pathParts as $part) {
- $path[$part] = $isMultipleChecker($part);
- }
-
- return $path;
- }
-
/**
* @param $path
* @param $field
return;
}
$defaults = [];
- $defaults['is_custom'] = $defaults['is_join'] = $defaults['is_many'] = FALSE;
+ $defaults['is_custom'] = $defaults['is_join'] = FALSE;
$field += $defaults;
$this->apiFieldSpec[$path] = $field;
}
}
/**
- * @param \Civi\Api4\Query\Api4SelectQuery $query
+ * Determines if path string points to a simple n-1 join that can be automatically added
+ *
+ * @param string $baseTable
* @param $joinPath
*
* @return bool
*/
- public function canJoin(Api4SelectQuery $query, $joinPath) {
- return !empty($this->getPath($query->getFrom(), $joinPath));
+ public function canAutoJoin($baseTable, $joinPath) {
+ try {
+ $path = $this->getPath($baseTable, $joinPath);
+ foreach ($path as $joinable) {
+ if ($joinable->getJoinType() === $joinable::JOIN_TYPE_ONE_TO_MANY) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ catch (\Exception $e) {
+ return FALSE;
+ }
}
/**
* @param string $baseTable
* @param string $joinPath
*
- * @return array
+ * @return \Civi\Api4\Service\Schema\Joinable\Joinable[]
* @throws \Exception
*/
protected function getPath($baseTable, $joinPath) {
$this->assertCount(1, $results);
}
- public function testActivityContactJoin() {
- $results = Activity::get()
- ->setCheckPermissions(FALSE)
- ->addSelect('assignees.id')
- ->addSelect('assignees.first_name')
- ->addSelect('assignees.display_name')
- ->addWhere('assignees.first_name', '=', 'Phoney')
- ->execute();
-
- $firstResult = $results->first();
-
- $this->assertCount(1, $results);
- $this->assertTrue(is_array($firstResult['assignees']));
-
- $firstAssignee = array_shift($firstResult['assignees']);
- $this->assertEquals($firstAssignee['first_name'], 'Phoney');
- }
-
- public function testContactPhonesJoin() {
- $testContact = $this->getReference('test_contact_1');
- $testPhone = $this->getReference('test_phone_1');
-
- $results = Contact::get()
- ->setCheckPermissions(FALSE)
- ->addSelect('phones.phone')
- ->addWhere('id', '=', $testContact['id'])
- ->addWhere('phones.location_type_id:name', '=', 'Home')
- ->execute()
- ->first();
-
- $this->assertArrayHasKey('phones', $results);
- $this->assertCount(1, $results['phones']);
- $firstPhone = array_shift($results['phones']);
- $this->assertEquals($testPhone['phone'], $firstPhone['phone']);
- }
-
}
+++ /dev/null
-<?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 |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
- *
- */
-
-
-namespace api\v4\Query;
-
-use Civi\Api4\Query\Api4SelectQuery;
-use api\v4\UnitTestCase;
-
-/**
- * @group headless
- */
-class Api4SelectQueryComplexJoinTest extends UnitTestCase {
-
- public function setUpHeadless() {
- $relatedTables = [
- 'civicrm_address',
- 'civicrm_email',
- 'civicrm_phone',
- 'civicrm_openid',
- 'civicrm_im',
- 'civicrm_website',
- 'civicrm_activity',
- 'civicrm_activity_contact',
- ];
- $this->cleanup(['tablesToTruncate' => $relatedTables]);
- $this->loadDataSet('SingleContact');
- return parent::setUpHeadless();
- }
-
- public function testWithComplexRelatedEntitySelect() {
- $api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
- $query = new Api4SelectQuery($api); $query->select[] = 'id';
- $query->select[] = 'display_name';
- $query->select[] = 'phones.*_id';
- $query->select[] = 'emails.email';
- $query->select[] = 'emails.location_type_id:name';
- $query->select[] = 'created_activities.contact_id';
- $query->select[] = 'created_activities.activity.subject';
- $query->select[] = 'created_activities.activity.activity_type_id:name';
- $query->where[] = ['first_name', '=', 'Single'];
- $query->where[] = ['id', '=', $this->getReference('test_contact_1')['id']];
- $results = $query->run();
-
- $testActivities = [
- $this->getReference('test_activity_1'),
- $this->getReference('test_activity_2'),
- ];
- $activitySubjects = array_column($testActivities, 'subject');
-
- $this->assertCount(1, $results);
- $firstResult = array_shift($results);
- $this->assertArrayHasKey('created_activities', $firstResult);
- $firstCreatedActivity = array_shift($firstResult['created_activities']);
- $this->assertArrayHasKey('activity', $firstCreatedActivity);
- $firstActivity = $firstCreatedActivity['activity'];
- $this->assertContains($firstActivity['subject'], $activitySubjects);
- $this->assertArrayHasKey('activity_type_id:name', $firstActivity);
-
- $this->assertArrayHasKey('location_type_id:name', $firstResult['emails'][0]);
- $this->assertArrayHasKey('location_type_id', $firstResult['phones'][0]);
- $this->assertArrayHasKey('id', $firstResult['phones'][0]);
- $this->assertArrayNotHasKey('phone', $firstResult['phones'][0]);
- }
-
- public function testWithSelectOfOrphanDeepValues() {
- $api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
- $query = new Api4SelectQuery($api); $query->select[] = 'id';
- $query->select[] = 'first_name';
- // emails not selected
- $query->select[] = 'emails.location_type.name';
- $results = $query->run();
- $firstResult = array_shift($results);
-
- $this->assertEmpty($firstResult['emails']);
- }
-
- public function testOrderDoesNotMatter() {
- $api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
- $query = new Api4SelectQuery($api);
- $query->select[] = 'id';
- $query->select[] = 'first_name';
- // before emails selection
- $query->select[] = 'emails.location_type_id:name';
- $query->select[] = 'emails.email';
- $query->where[] = ['emails.email', 'IS NOT NULL'];
- $results = $query->run();
- $firstResult = array_shift($results);
-
- $this->assertNotEmpty($firstResult['emails'][0]['location_type_id:name']);
- }
-
-}
return parent::setUpHeadless();
}
- public function testWithSingleWhereJoin() {
- $phoneNum = $this->getReference('test_phone_1')['phone'];
-
- $api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
- $query = new Api4SelectQuery($api);
- $query->where[] = ['phones.phone', '=', $phoneNum];
- $results = $query->run();
-
- $this->assertCount(1, $results);
- }
-
- public function testOneToManyJoin() {
- $phoneNum = $this->getReference('test_phone_1')['phone'];
-
- $api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
- $query = new Api4SelectQuery($api);
- $query->select[] = 'id';
- $query->select[] = 'first_name';
- $query->select[] = 'phones.phone';
- $query->where[] = ['phones.phone', '=', $phoneNum];
- $results = $query->run();
-
- $this->assertCount(1, $results);
- $firstResult = array_shift($results);
- $this->assertArrayHasKey('phones', $firstResult);
- $firstPhone = array_shift($firstResult['phones']);
- $this->assertEquals($phoneNum, $firstPhone['phone']);
- }
-
public function testManyToOneJoin() {
$phoneNum = $this->getReference('test_phone_1')['phone'];
$contact = $this->getReference('test_contact_1');
$this->assertEquals($contact['display_name'], $firstResult['contact.display_name']);
}
- public function testOneToManyMultipleJoin() {
- $api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
- $query = new Api4SelectQuery($api);
- $query->select[] = 'id';
- $query->select[] = 'first_name';
- $query->select[] = 'phones.phone';
- $query->where[] = ['first_name', '=', 'Phoney'];
- $results = $query->run();
- $result = array_pop($results);
-
- $this->assertEquals('Phoney', $result['first_name']);
- $this->assertCount(2, $result['phones']);
- }
-
public function testInvaidSort() {
$api = \Civi\API\Request::create('Contact', 'get', ['version' => 4, 'checkPermissions' => FALSE]);
$query = new Api4SelectQuery($api);
*
* @package CRM
* @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id$
*
*/
->addSelect('preferred_language:label')
->addSelect('last_name')
->execute()
- ->indexBy('last_name')
- ->getArrayCopy();
+ ->indexBy('last_name');
$this->assertEquals($contacts['One']['preferred_language:label'], 'Armenian');
$this->assertEquals($contacts['Two']['preferred_language:label'], 'Basque');
namespace api\v4\Query;
-use Civi\Api4\Contact;
use Civi\Api4\Email;
use api\v4\UnitTestCase;
return parent::setUpHeadless();
}
- public function testOneToManySelect() {
- $results = Contact::get()
- ->addSelect('emails.email')
- ->execute()
- ->indexBy('id')
- ->getArrayCopy();
-
- $firstContactId = $this->getReference('test_contact_1')['id'];
- $secondContactId = $this->getReference('test_contact_2')['id'];
-
- $firstContact = $results[$firstContactId];
- $secondContact = $results[$secondContactId];
- $firstContactEmails = array_column($firstContact['emails'], 'email');
- $secondContactEmails = array_column($secondContact['emails'], 'email');
-
- $expectedFirstEmails = [
- 'test_contact_one_home@fakedomain.com',
- 'test_contact_one_work@fakedomain.com',
- ];
- $expectedSecondEmails = [
- 'test_contact_two_home@fakedomain.com',
- 'test_contact_two_work@fakedomain.com',
- ];
-
- $this->assertEquals($expectedFirstEmails, $firstContactEmails);
- $this->assertEquals($expectedSecondEmails, $secondContactEmails);
- }
-
public function testManyToOneSelect() {
$results = Email::get()
->addSelect('contact.display_name')
->execute()
- ->indexBy('id')
- ->getArrayCopy();
+ ->indexBy('id');
$firstEmail = $this->getReference('test_email_1');
$secondEmail = $this->getReference('test_email_2');