*
* Generated from xml/schema/CRM/Contact/Relationship.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:787e0139d4a6b8b587b8d0d607e25ff0)
+ * (GenCodeChecksum:a10cc7576dc2353519a6c572435fb10a)
*/
/**
*/
public static $_log = TRUE;
+ /**
+ * Paths for accessing this entity in the UI.
+ *
+ * @var string[]
+ */
+ protected static $_paths = [
+ 'view' => 'civicrm/contact/view/rel?action=view&reset=1&cid=[contact_id_a]&id=[id]',
+ 'delete' => 'civicrm/contact/view/rel?action=delete&reset=1&cid=[contact_id_a]&id=[id]',
+ ];
+
/**
* Relationship ID
*
*
* Generated from xml/schema/CRM/Contact/RelationshipCache.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:3376402e2a249b7004b40df6aeac5df9)
+ * (GenCodeChecksum:dd52d37d1350a679b727c906ea37661b)
*/
/**
*/
public static $_log = FALSE;
+ /**
+ * Paths for accessing this entity in the UI.
+ *
+ * @var string[]
+ */
+ protected static $_paths = [
+ 'view' => 'civicrm/contact/view/rel?action=view&reset=1&cid=[near_contact_id]&id=[relationship_id]',
+ 'update' => 'civicrm/contact/view/rel?action=update&reset=1&cid=[near_contact_id]&id=[relationship_id]&rtype=[orientation]',
+ 'delete' => 'civicrm/contact/view/rel?action=delete&reset=1&cid=[near_contact_id]&id=[relationship_id]',
+ ];
+
/**
* Relationship Cache ID
*
* The record_type_id field determines the contact's role in the activity (source, target, or assignee).
* @ui_join_filters record_type_id
*
+ * @searchable bridge
* @see \Civi\Api4\Activity
* @package Civi\Api4
*/
*
* This connects an activity to one or more cases.
*
+ * @searchable bridge
* @see \Civi\Api4\Case
* @package Civi\Api4
*/
*
* This connects a client to a case.
*
+ * @searchable bridge
* @see \Civi\Api4\Case
* @package Civi\Api4
*/
*
* This places a dashboard item on a user's home screen.
*
+ * @searchable bridge
* @see \Civi\Api4\Dashboard
* @searchable none
* @package Civi\Api4
'options' => [
'primary' => ts('Primary'),
'secondary' => ts('Secondary'),
+ 'bridge' => ts('Bridge'),
'none' => ts('None'),
],
],
*
* @ui_join_filters account_relationship
*
+ * @searchable bridge
* @package Civi\Api4
*/
class EntityFinancialAccount extends Generic\DAOEntity {
*
* @see https://docs.civicrm.org/dev/en/latest/financial/financialentities/
*
+ * @searchable bridge
* @package Civi\Api4
*/
class EntityFinancialTrxn extends Generic\DAOEntity {
* EntityTag - links tags to contacts, activities, etc.
*
* @see \Civi\Api4\Tag
- *
+ * @searchable bridge
* @package Civi\Api4
*/
class EntityTag extends Generic\DAOEntity {
*
* @ui_join_filters status
*
+ * @searchable bridge
* @see \Civi\Api4\Group
* @package Civi\Api4
*/
/**
* RelationshipCache - readonly table to facilitate joining and finding contacts by relationship.
*
+ * @searchable secondary
* @see \Civi\Api4\Relationship
* @ui_join_filters near_relation
* @package Civi\Api4
*/
private $_afform;
+ /**
+ * @var array
+ */
+ private $_extraEntityFields = [];
+
/**
* @param \Civi\Api4\Generic\Result $result
* @throws UnauthorizedException
/**
* Determines if a column is eligible to use an aggregate function
- * @param $fieldName
- * @param $prefix
+ * @param string $fieldName
+ * @param string $prefix
* @return bool
*/
- private function canAggregate($fieldName, $prefix) {
+ private function canAggregate($fieldName, $prefix = '') {
$apiParams = $this->savedSearch['api_params'];
// If the query does not use grouping, never
* @param array $apiParams
*/
private function augmentSelectClause(&$apiParams): void {
+ foreach ($this->getExtraEntityFields($this->savedSearch['api_entity']) as $extraFieldName) {
+ if (!in_array($extraFieldName, $apiParams['select']) && !$this->canAggregate($extraFieldName)) {
+ $apiParams['select'][] = $extraFieldName;
+ }
+ }
$joinAliases = [];
- // Select the ids of explicitly joined entities (helps with displaying links)
+ // Select the ids, etc. of explicitly joined entities (helps with displaying links)
foreach ($apiParams['join'] ?? [] as $join) {
- $joinAliases[] = $joinAlias = explode(' AS ', $join[0])[1];
- $idFieldName = $joinAlias . '.id';
- if (!in_array($idFieldName, $apiParams['select']) && !$this->canAggregate('id', $joinAlias . '.')) {
- $apiParams['select'][] = $idFieldName;
+ [$joinEntity, $joinAlias] = explode(' AS ', $join[0]);
+ $joinAliases[] = $joinAlias;
+ foreach ($this->getExtraEntityFields($joinEntity) as $extraField) {
+ $extraFieldName = $joinAlias . '.' . $extraField;
+ if (!in_array($extraFieldName, $apiParams['select']) && !$this->canAggregate($extraField, $joinAlias . '.')) {
+ $apiParams['select'][] = $extraFieldName;
+ }
}
}
// Select the ids of implicitly joined entities (helps with displaying links)
}
}
+ /**
+ * Get list of extra fields needed for displaying links for a given entity
+ *
+ * @param string $entityName
+ * @return array
+ */
+ private function getExtraEntityFields(string $entityName): array {
+ if (!isset($this->_extraEntityFields[$entityName])) {
+ $info = CoreUtil::getApiClass($entityName)::getInfo();
+ $this->_extraEntityFields[$entityName] = [$info['id_field']];
+ foreach ($info['paths'] ?? [] as $path) {
+ $matches = [];
+ preg_match_all('#\[(\w+)]#', $path, $matches);
+ $this->_extraEntityFields[$entityName] = array_unique(array_merge($this->_extraEntityFields[$entityName], $matches[1] ?? []));
+ }
+ }
+ return $this->_extraEntityFields[$entityName];
+ }
+
}
// Add in FK fields for implicit joins
// For example, add a `campaign_id.title` field to the Contribution entity
foreach ($schema as &$entity) {
- if (in_array('DAOEntity', $entity['type'], TRUE) && !in_array('EntityBridge', $entity['type'], TRUE)) {
+ if ($entity['searchable'] !== 'bridge') {
foreach (array_reverse($entity['fields'], TRUE) as $index => $field) {
if (!empty($field['fk_entity']) && !$field['options'] && empty($field['serialize']) && !empty($schema[$field['fk_entity']]['label_field'])) {
$isCustom = strpos($field['name'], '.');
});
}
- var primaryEntities = _.filter(CRM.crmSearchAdmin.schema, function(entity) {
- return entity.searchable === 'primary' && !_.includes(entity.type, 'EntityBridge');
- });
- var secondaryEntities = _.filter(CRM.crmSearchAdmin.schema, function(entity) {
- return entity.searchable === 'secondary' && !_.includes(entity.type, 'EntityBridge');
- });
+ var primaryEntities = _.filter(CRM.crmSearchAdmin.schema, {searchable: 'primary'}),
+ secondaryEntities = _.filter(CRM.crmSearchAdmin.schema, {searchable: 'secondary'});
$scope.mainEntitySelect = formatForSelect2(primaryEntities, 'name', 'title_plural', ['description', 'icon']);
$scope.mainEntitySelect.push({
text: ts('More...'),
// Add extra searchable fields from bridge entity
if (join && join.bridge) {
addFields(_.filter(searchMeta.getEntity(join.bridge).fields, function(field) {
- return (field.name !== 'id' && field.name !== 'entity_id' && field.name !== 'entity_table' && !field.fk_entity);
+ return (field.name !== 'id' && field.name !== 'entity_id' && field.name !== 'entity_table' && !field.fk_entity && !_.includes(field.name, '.'));
}));
}
$this->assertNotEmpty($info['title_plural']);
$this->assertNotEmpty($info['type']);
$this->assertNotEmpty($info['description']);
- $this->assertContains($info['searchable'], ['primary', 'secondary', 'none']);
+ $this->assertContains($info['searchable'], ['primary', 'secondary', 'bridge', 'none']);
// Bridge must be between exactly 2 entities
if (in_array('EntityBridge', $info['type'], TRUE)) {
$this->assertCount(2, $info['bridge']);
<add>1.1</add>
<log>true</log>
<icon>fa-handshake-o</icon>
+ <paths>
+ <view>civicrm/contact/view/rel?action=view&reset=1&cid=[contact_id_a]&id=[id]</view>
+ <delete>civicrm/contact/view/rel?action=delete&reset=1&cid=[contact_id_a]&id=[id]</delete>
+ </paths>
+
<field>
<name>id</name>
<type>int unsigned</type>
<log>false</log>
<icon>fa-handshake-o</icon>
<title>Related Contact</title>
+ <paths>
+ <view>civicrm/contact/view/rel?action=view&reset=1&cid=[near_contact_id]&id=[relationship_id]</view>
+ <update>civicrm/contact/view/rel?action=update&reset=1&cid=[near_contact_id]&id=[relationship_id]&rtype=[orientation]</update>
+ <delete>civicrm/contact/view/rel?action=delete&reset=1&cid=[near_contact_id]&id=[relationship_id]</delete>
+ </paths>
+
<field>
<name>id</name>
<type>int unsigned</type>