Skip to content

Commit

Permalink
Merge pull request #23018 from colemanw/afformDeepSearch
Browse files Browse the repository at this point in the history
SearchKit - Ensure filters work with multiple search displays on a form
  • Loading branch information
eileenmcnaughton authored Apr 6, 2022
2 parents 1e219b1 + 78ee4a9 commit 7ae3799
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 10 deletions.
21 changes: 21 additions & 0 deletions ext/afform/mock/ang/testMultipleSearchForm.aff.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div>
<div af-fieldset="">
<af-field name="source" />
<af-field name="Contact_Email_contact_id_01.location_type_id" defn="{input_attrs: {multiple: true}}" />
<crm-search-display-table search-name="TestContactEmailSearch" display-name="TestContactEmailDisplay"></crm-search-display-table>
</div>
<div af-fieldset="">
<!-- Filter is in wrong fieldset to work with `TestSearchForEmail`. See SearchAfformTest::testRunMultipleSearchForm -->
<af-field name="email" />
<crm-search-display-table search-name="TestContactEmailSearch" display-name=""></crm-search-display-table>
</div>
</div>
<div af-fieldset="">
<af-field name="contact_id.display_name" />
<af-field name="location_type_id" />
<crm-search-display-table search-name="TestSearchForEmail" display-name=""></crm-search-display-table>
</div>
<!-- This display is not enclosed in an af-fieldset so this filter will not work. See SearchAfformTest::testRunMultipleSearchForm -->
<af-field name="location_type_id" />
<crm-search-display-table filters="{'contact_id.display_name': 'testRunMultipleSearchForm'}" search-name="TestSearchForPhone" display-name=""></crm-search-display-table>

6 changes: 6 additions & 0 deletions ext/afform/mock/ang/testMultipleSearchForm.aff.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "search",
"title": "TestMultipleSearchForm",
"server_route": "",
"permission": "access CiviCRM"
}
31 changes: 21 additions & 10 deletions ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,7 @@ private function getAfformFilters() {
}
// Get afform field filters
$filterKeys = array_column(\CRM_Utils_Array::findAll(
$afform['layout'] ?? [],
$afform['searchDisplay']['fieldset'],
['#tag' => 'af-field']
), 'name');
// Get filters passed into search display directive from Afform markup
Expand Down Expand Up @@ -1028,24 +1028,35 @@ private function getAfformFilters() {
*
* Verifies the searchDisplay is embedded in the afform and the user has permission to view it.
*
* @return array|false|null
* @return array|false
*/
private function loadAfform() {
// Only attempt to load afform once.
if ($this->afform && !isset($this->_afform)) {
$this->_afform = FALSE;
// Permission checks are enabled in this api call to ensure the user has permission to view the form
$afform = \Civi\Api4\Afform::get()
$afform = \Civi\Api4\Afform::get($this->getCheckPermissions())
->addWhere('name', '=', $this->afform)
->setLayoutFormat('shallow')
->setLayoutFormat('deep')
->execute()->first();
if (empty($afform['layout'])) {
return FALSE;
}
// 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
$afform['searchDisplay'] = \CRM_Utils_Array::findAll(
$afform['layout'] ?? [],
['#tag' => "{$this->display['type:name']}", 'display-name' => $this->display['name']]
)[0] ?? NULL;
if ($afform['searchDisplay']) {
$this->_afform = $afform;
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 ($afform['searchDisplay']) {
// Set the fieldset for this display (if it is in one and we haven't fallen back to the whole form)
$afform['searchDisplay']['fieldset'] = $key === 'form' ? [] : $fieldset;
return $this->_afform = $afform;
}
}
}
return $this->_afform;
Expand Down
170 changes: 170 additions & 0 deletions ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchAfformTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Civi\Api4\Afform;
use Civi\Api4\Contact;
use Civi\Api4\Email;
use Civi\Api4\Phone;
use Civi\Api4\SavedSearch;
use Civi\Api4\SearchDisplay;
use Civi\Api4\Utils\CoreUtil;
Expand Down Expand Up @@ -163,6 +164,175 @@ public function testRunWithAfform() {
$this->assertCount(1, $result);
}

public function testRunMultipleSearchForm() {
$email = uniqid('tester@');

Contact::create(FALSE)
->addValue('first_name', 'tester')
->addValue('last_name', __FUNCTION__)
->addValue('source', 'afform_multi_test')
->addChain('emails', Email::save()
->addDefault('contact_id', '$id')
->addRecord(['email' => $email, 'location_type_id:name' => 'Home'])
->addRecord(['email' => $email, 'location_type_id:name' => 'Work'])
)
->addChain('phones', Phone::save()
->addDefault('contact_id', '$id')
->addRecord(['phone' => '123-4567', 'location_type_id:name' => 'Home'])
->addRecord(['phone' => '234-5678', 'location_type_id:name' => 'Work'])
)
->execute();

Contact::create(FALSE)
->addValue('first_name', 'tester2')
->addValue('last_name', __FUNCTION__)
->addValue('source', 'afform_multi_test')
->addChain('emails', Email::save()
->addDefault('contact_id', '$id')
->addRecord(['email' => 'other@test.com', 'location_type_id:name' => 'Other'])
)
->addChain('phones', Phone::save()
->addDefault('contact_id', '$id')
->addRecord(['phone' => '123-4567', 'location_type_id:name' => 'Home'])
->addRecord(['phone' => '234-5678', 'location_type_id:name' => 'Work'])
)
->execute();

// Decoy contact just to make sure we don't get false-positives
Contact::create(FALSE)
->addValue('first_name', 'tester3')
->addValue('last_name', 'nobody')
->addValue('source', 'decoy')
->addChain('emails', Email::save()
->addDefault('contact_id', '$id')
->addRecord(['email' => $email, 'location_type_id:name' => 'Home'])
)
->addChain('phones', Phone::save()
->addDefault('contact_id', '$id')
->addRecord(['phone' => '123-4567', 'location_type_id:name' => 'Home'])
->addRecord(['phone' => '234-5678', 'location_type_id:name' => 'Work'])
)
->execute();

$contactEmailSearch = SavedSearch::create(FALSE)
->setValues([
'name' => 'TestContactEmailSearch',
'label' => 'TestContactEmailSearch',
'api_entity' => 'Contact',
'api_params' => [
'version' => 4,
'select' => [
'id',
'display_name',
'GROUP_CONCAT(DISTINCT Contact_Email_contact_id_01.email) AS GROUP_CONCAT_Contact_Email_contact_id_01_email',
],
'orderBy' => [],
'where' => [
['contact_type:name', '=', 'Individual'],
],
'groupBy' => ['id'],
'join' => [
[
'Email AS Contact_Email_contact_id_01',
'LEFT',
['id', '=', 'Contact_Email_contact_id_01.contact_id'],
],
],
'having' => [],
],
])
->execute()->first();

$contactEmailDisplay = SearchDisplay::create(FALSE)
->setValues([
'name' => 'TestContactEmailDisplay',
'label' => 'TestContactEmailDisplay',
'saved_search_id.name' => 'TestContactEmailSearch',
'type' => 'table',
'settings' => [
'limit' => 50,
'pager' => TRUE,
'columns' => [
[
'key' => 'id',
'label' => 'Contact ID',
'dataType' => 'Integer',
'type' => 'field',
],
[
'key' => 'display_name',
'label' => 'Display Name',
'dataType' => 'String',
'type' => 'field',
],
[
'key' => 'GROUP_CONCAT_Contact_Email_contact_id_01_email',
'label' => 'Emails',
'dataType' => 'String',
'type' => 'field',
],
],
],
'acl_bypass' => FALSE,
])
->execute()->first();

foreach (['Email', 'Phone'] as $entity) {
SavedSearch::create(FALSE)
->setValues([
'name' => 'TestSearchFor' . $entity,
'label' => 'TestSearchFor' . $entity,
'api_entity' => $entity,
'api_params' => [
'version' => 4,
'select' => [
'id',
'contact_id.display_name',
],
'orderBy' => [],
'where' => [],
'groupBy' => [],
'join' => [],
'having' => [],
],
])
->execute();
}

$params = [
'return' => 'page:1',
'display' => NULL,
'afform' => 'testMultipleSearchForm',
];

// This filter will not work because the search display is not within an <af-field>
$params['savedSearch'] = 'TestSearchForPhone';
$params['filters'] = ['location_type_id' => 1];
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount(4, $result);

$params['savedSearch'] = 'TestSearchForEmail';
$params['filters'] = ['location_type_id' => 1, 'contact_id.display_name' => __FUNCTION__];
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount(1, $result);

// Email filter will not work because it's in the wrong fieldset on the form
$params['filters'] = ['email' => $email, 'contact_id.display_name' => __FUNCTION__];
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount(3, $result);

// No filters will work; they are in the fieldset belonging to the non-default display
$params['savedSearch'] = 'TestContactEmailSearch';
$params['filters'] = ['source' => 'afform_multi_test', 'Contact_Email_contact_id_01.location_type_id' => 1];
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertGreaterThanOrEqual(3, $result->count());

// Now the filters will work because they are in the fieldset for this display
$params['display'] = 'TestContactEmailDisplay';
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount(1, $result);
}

public function testSearchReferencesToAfform() {
$search = SavedSearch::create(FALSE)
->setValues([
Expand Down

0 comments on commit 7ae3799

Please sign in to comment.