if ($filterAttr && is_string($filterAttr) && $filterAttr[0] === '{') {
foreach (\CRM_Utils_JS::decode($filterAttr) as $filterKey => $filterVal) {
// Automatically apply filters from the markup if they have a value
- if ($filterVal !== NULL) {
+ // Only do this if there's one instance of the display on the form
+ if ($afform['searchDisplay']['count'] === 1 && $filterVal !== NULL) {
unset($this->filters[$filterKey]);
if ($this->hasValue($filterVal)) {
$this->applyFilter(explode(',', $filterKey), $filterVal);
}
}
// If it's a javascript variable it will have come back from decode() as NULL;
- // whitelist it to allow it to be passed to this api from javascript.
+ // Or if there's more than one instance of the display on the form, they might
+ // use different filters.
+ // Just whitelist it so the value passed in will be accepted.
else {
$filterKeys[] = $filterKey;
}
if (empty($afform['layout'])) {
return FALSE;
}
+ $afform['searchDisplay'] = NULL;
// Get all search display fieldsets (which will have an empty value for the af-fieldset attribute)
$fieldsets = \CRM_Utils_Array::findAll($afform['layout'], ['af-fieldset' => '']);
// As a fallback, search the entire afform in case the search display is not in a fieldset
$fieldsets['form'] = $afform['layout'];
- // Validate that the afform contains this search display
+ // Search for one or more instance of this search display
foreach ($fieldsets as $key => $fieldset) {
- $afform['searchDisplay'] = \CRM_Utils_Array::findAll(
- $fieldset,
- ['#tag' => $this->display['type:name'], 'search-name' => $this->savedSearch['name'], 'display-name' => $this->display['name']]
- )[0] ?? NULL;
+ if ($key === 'form' && $afform['searchDisplay']) {
+ // Already found in a fieldset, don't search the whole form
+ continue;
+ }
+ $displays = \CRM_Utils_Array::findAll(
+ $fieldset,
+ ['#tag' => $this->display['type:name'], 'search-name' => $this->savedSearch['name'], 'display-name' => $this->display['name']]
+ );
+ if (!$displays) {
+ continue;
+ }
+ // Already found, just increment the count
if ($afform['searchDisplay']) {
+ $afform['searchDisplay']['count'] += count($displays);
+ }
+ else {
+ $afform['searchDisplay'] = $displays[0];
+ $afform['searchDisplay']['count'] = count($displays);
// Set the fieldset for this display (if it is in one and we haven't fallen back to the whole form)
+ // TODO: This just uses the first fieldset, but there could be multiple. Potentially could use filters to match it.
$afform['searchDisplay']['fieldset'] = $key === 'form' ? [] : $fieldset;
- return $this->_afform = $afform;
}
}
+ $this->_afform = $afform;
}
return $this->_afform;
}
use Civi\Api4\Afform;
use Civi\Api4\Contact;
use Civi\Api4\Email;
+use Civi\Api4\OptionGroup;
+use Civi\Api4\OptionValue;
use Civi\Api4\Phone;
use Civi\Api4\SavedSearch;
use Civi\Api4\SearchDisplay;
/**
* Test running a searchDisplay within an afform.
*/
- public function testRunWithAfform() {
+ public function testRunWithAfform(): void {
$search = SavedSearch::create(FALSE)
->setValues([
'name' => 'TestContactEmailSearch',
$this->assertCount(1, $result);
}
- public function testRunMultipleSearchForm() {
+ public function testRunMultipleSearchForm(): void {
$email = uniqid('tester@');
Contact::create(FALSE)
)
->execute();
- $contactEmailSearch = SavedSearch::create(FALSE)
- ->setValues([
+ $contactEmailSearch = SavedSearch::save(FALSE)
+ ->addRecord([
'name' => 'TestContactEmailSearch',
'label' => 'TestContactEmailSearch',
'api_entity' => 'Contact',
'having' => [],
],
])
+ ->setMatch(['name'])
->execute()->first();
- $contactEmailDisplay = SearchDisplay::create(FALSE)
- ->setValues([
+ $contactEmailDisplay = SearchDisplay::save(FALSE)
+ ->addRecord([
'name' => 'TestContactEmailDisplay',
'label' => 'TestContactEmailDisplay',
'saved_search_id.name' => 'TestContactEmailSearch',
],
'acl_bypass' => FALSE,
])
+ ->setMatch(['name'])
->execute()->first();
foreach (['Email', 'Phone'] as $entity) {
- SavedSearch::create(FALSE)
- ->setValues([
+ SavedSearch::save(FALSE)
+ ->addRecord([
'name' => 'TestSearchFor' . $entity,
'label' => 'TestSearchFor' . $entity,
'api_entity' => $entity,
'having' => [],
],
])
+ ->setMatch(['name'])
->execute();
}
$this->assertCount(1, $result);
}
- public function testSearchReferencesToAfform() {
- $search = SavedSearch::create(FALSE)
- ->setValues([
+ public function testSearchReferencesToAfform(): void {
+ $search = SavedSearch::save(FALSE)
+ ->addRecord([
'name' => 'TestSearchToDelete',
'label' => 'TestSearchToDelete',
'api_entity' => 'Contact',
'select' => ['id'],
],
])
+ ->setMatch(['name'])
->execute()->first();
- $display = SearchDisplay::create(FALSE)
- ->setValues([
+ $display = SearchDisplay::save(FALSE)
+ ->addRecord([
'name' => 'TestDisplayToDelete',
'label' => 'TestDisplayToDelete',
'saved_search_id.name' => 'TestSearchToDelete',
],
'acl_bypass' => FALSE,
])
+ ->setMatch(['saved_search_id', 'name'])
->execute()->first();
// The search should have one reference (its display)
$this->assertCount(0, Afform::get(FALSE)->addWhere('name', 'CONTAINS', 'TestAfformToDelete')->execute());
}
+ public function testDisplaysSharingSameFieldset(): void {
+ OptionGroup::save(FALSE)
+ ->addRecord([
+ 'title' => 'search_test_options',
+ ])
+ ->setMatch(['title'])
+ ->execute();
+ OptionValue::save(FALSE)
+ ->setDefaults(['option_group_id.name' => 'search_test_options'])
+ ->addRecord([
+ 'label' => 'option_a',
+ 'value' => 'a',
+ ])
+ ->addRecord([
+ 'label' => 'option_b',
+ 'value' => 'b',
+ ])
+ ->addRecord([
+ 'label' => 'option_c',
+ 'value' => 'c',
+ 'is_active' => FALSE,
+ ])
+ ->setMatch(['name', 'option_group_id'])
+ ->execute();
+
+ $search = SavedSearch::save(FALSE)
+ ->addRecord([
+ 'name' => 'testDisplaysSharingSameFieldset',
+ 'label' => 'testDisplaysSharingSameFieldset',
+ 'api_entity' => 'OptionValue',
+ 'api_params' => [
+ 'version' => 4,
+ 'select' => ['value'],
+ ],
+ ])
+ ->setMatch(['name'])
+ ->execute()->first();
+
+ $display = SearchDisplay::save(FALSE)
+ ->addRecord([
+ 'name' => 'testDisplaysSharingSameFieldset',
+ 'label' => 'testDisplaysSharingSameFieldset',
+ 'saved_search_id.name' => 'testDisplaysSharingSameFieldset',
+ 'type' => 'table',
+ 'settings' => [
+ 'columns' => [
+ [
+ 'key' => 'value',
+ 'type' => 'field',
+ ],
+ ],
+ ],
+ 'acl_bypass' => FALSE,
+ ])
+ ->setMatch(['saved_search_id', 'name'])
+ ->execute()->first();
+
+ $baseParams = [
+ 'return' => 'page:1',
+ 'savedSearch' => $search['name'],
+ 'display' => $display['name'],
+ 'afform' => 'testDisplaysSharingSameFieldset',
+ 'filters' => ['option_group_id:name' => 'search_test_options'],
+ ];
+
+ // Afform has 2 copies of the same display, with different values for the filter is_active
+ // This should allow the is_active filters to be set in the params
+ $params = $baseParams;
+ $params['filters']['is_active'] = TRUE;
+ $result = civicrm_api4('SearchDisplay', 'run', $params);
+ $this->assertCount(2, $result);
+
+ $params = $baseParams;
+ $params['filters']['is_active'] = FALSE;
+ $result = civicrm_api4('SearchDisplay', 'run', $params);
+ $this->assertCount(1, $result);
+ $this->assertEquals('c', $result[0]['columns'][0]['val']);
+
+ // Because the 2 displays share a fieldset, the filter field should work on both
+ $params = $baseParams;
+ $params['filters']['is_active'] = TRUE;
+ $params['filters']['label'] = 'b';
+ $result = civicrm_api4('SearchDisplay', 'run', $params);
+ $this->assertCount(1, $result);
+ $this->assertEquals('b', $result[0]['columns'][0]['val']);
+
+ $params = $baseParams;
+ $params['filters']['is_active'] = FALSE;
+ $params['filters']['label'] = 'b';
+ $result = civicrm_api4('SearchDisplay', 'run', $params);
+ $this->assertCount(0, $result);
+ }
+
}