Skip to content

Commit

Permalink
Merge pull request #27121 from colemanw/afformPrefill
Browse files Browse the repository at this point in the history
Afform - Fix broken prefill functionality
  • Loading branch information
seamuslee001 authored Aug 24, 2023
2 parents 72f1869 + 3c52227 commit b588537
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,13 @@ protected function loadEntities() {
public function loadEntity(array $entity, array $ids) {
// Limit number of records based on af-repeat settings
// If 'min' is set then it is repeatable, and max will either be a number or NULL for unlimited.
$ids = array_slice($ids, 0, isset($entity['min']) ? $entity['max'] : 1);
if (isset($entity['min']) && isset($entity['max'])) {
foreach (array_keys($ids) as $index) {
if ($index >= $entity['max']) {
unset($ids[$index]);
}
}
}

$api4 = $this->_formDataModel->getSecureApi4($entity['name']);
$idField = CoreUtil::getIdFieldName($entity['type']);
Expand Down
28 changes: 16 additions & 12 deletions ext/afform/core/ang/af/afForm.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@
// With no arguments this will prefill the entire form based on url args
// With selectedEntity, selectedIndex & selectedId provided this will prefill a single entity
this.loadData = function(selectedEntity, selectedIndex, selectedId, selectedField) {
var toLoad = 0,
params = {name: ctrl.getFormMeta().name, args: {}};
let toLoad = false;
const params = {name: ctrl.getFormMeta().name, args: {}};
// Load single entity
if (selectedEntity) {
toLoad = 1;
toLoad = !!selectedId;
params.matchField = selectedField;
params.args[selectedEntity] = {};
params.args[selectedEntity][selectedIndex] = selectedId;
Expand All @@ -57,7 +57,7 @@
args = _.assign({}, $scope.$parent.routeParams || {}, $scope.$parent.options || {});
_.each(schema, function (entity, entityName) {
if (args[entityName] || entity.actions.update) {
toLoad++;
toLoad = true;
}
if (args[entityName] && typeof args[entityName] === 'string') {
args[entityName] = args[entityName].split(',');
Expand All @@ -67,19 +67,23 @@
}
if (toLoad) {
crmApi4('Afform', 'prefill', params)
.then(function(result) {
_.each(result, function(item) {
data[item.name] = data[item.name] || {};
_.extend(data[item.name], item.values, schema[item.name].data || {});
.then((result) => {
result.forEach((item) => {
// Use _.each() because item.values could be cast as an object if array keys are not sequential
_.each(item.values, (values, index) => {
data[item.name][index].joins = {};
angular.merge(data[item.name][index], values, {fields: _.cloneDeep(schema[item.name].data || {})});
});
});
});
}
// Clear existing contact selection
else if (selectedEntity) {
data[selectedEntity][selectedIndex].fields = {};
if (data[selectedEntity][selectedIndex].joins) {
data[selectedEntity][selectedIndex].joins = {};
}
// Delete object keys without breaking object references
Object.keys(data[selectedEntity][selectedIndex].fields).forEach(key => delete data[selectedEntity][selectedIndex].fields[key]);
// Fill pre-set values
angular.merge(data[selectedEntity][selectedIndex].fields, _.cloneDeep(schema[selectedEntity].data || {}));
data[selectedEntity][selectedIndex].joins = {};
}
};

Expand Down
103 changes: 103 additions & 0 deletions ext/afform/mock/tests/phpunit/api/v4/AfformPrefillUsageTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

/**
* Test case for Afform with autocomplete.
*
* @group headless
*/
class api_v4_AfformPrefillUsageTest extends api_v4_AfformUsageTestCase {
use \Civi\Test\Api4TestTrait;

/**
* Ensure that Afform restricts autocomplete results when it's set to use a SavedSearch
*/
public function testPrefillWithRepeat(): void {
$layout = <<<EOHTML
<af-form ctrl="afform">
<af-entity data="{contact_type: 'Individual'}" type="Contact" name="Individual1" label="Individual 1" actions="{create: true, update: true}" security="RBAC" url-autofill="1" />
<fieldset af-fieldset="Individual1" class="af-container" af-title="Individual 1" af-repeat="Add" min="1" max="3">
<div class="af-container">
<af-field name="id"></af-field>
<af-field name="preferred_communication_method"></af-field>
<afblock-name-individual></afblock-name-individual>
</div>
<div af-join="Email" af-repeat="Add" af-copy="Copy" min="1">
<afblock-contact-email></afblock-contact-email>
</div>
<div af-join="Phone" af-repeat="Add" af-copy="Copy" min="1" max="2">
<afblock-contact-phone></afblock-contact-phone>
</div>
</fieldset>
</af-form>
EOHTML;

$this->useValues([
'layout' => $layout,
'permission' => CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION,
]);

$cid = $this->saveTestRecords('Contact', [
'records' => [
['first_name' => 'A', 'last_name' => '_A', 'preferred_communication_method' => [1, 3]],
['first_name' => 'B', 'last_name' => '_B', 'email_primary.email' => '[email protected]'],
['first_name' => 'C', 'last_name' => '_C'],
['first_name' => 'D', 'last_name' => '_D', 'email_primary.email' => '[email protected]'],
],
])->column('id');

$this->saveTestRecords('Phone', [
'records' => [
['contact_id' => $cid[0], 'phone' => '0-1'],
['contact_id' => $cid[0], 'phone' => '0-2'],
['contact_id' => $cid[0], 'phone' => '0-3'],
['contact_id' => $cid[2], 'phone' => '2-1'],
['contact_id' => $cid[3], 'phone' => '3-1'],
],
]);

$prefill = Civi\Api4\Afform::prefill()
->setName($this->formName)
->setArgs(['Individual1' => $cid])
->execute()
->indexBy('name');

// Form entity has `max="3"`
$this->assertCount(3, $prefill['Individual1']['values']);
$this->assertEquals('A', $prefill['Individual1']['values'][0]['fields']['first_name']);
$this->assertEquals([1, 3], $prefill['Individual1']['values'][0]['fields']['preferred_communication_method']);
$this->assertEquals('B', $prefill['Individual1']['values'][1]['fields']['first_name']);
$this->assertEquals('C', $prefill['Individual1']['values'][2]['fields']['first_name']);

// One email should have been filled
$this->assertCount(1, $prefill['Individual1']['values'][1]['joins']['Email']);
$this->assertEquals('[email protected]', $prefill['Individual1']['values'][1]['joins']['Email'][0]['email']);
$this->assertEmpty($prefill['Individual1']['values'][0]['joins']['Email']);
$this->assertEmpty($prefill['Individual1']['values'][2]['joins']['Email']);

// Phone join has `max="2"`
$this->assertCount(2, $prefill['Individual1']['values'][0]['joins']['Phone']);
$this->assertCount(1, $prefill['Individual1']['values'][2]['joins']['Phone']);
$this->assertEquals('2-1', $prefill['Individual1']['values'][2]['joins']['Phone'][0]['phone']);
$this->assertEmpty($prefill['Individual1']['values'][1]['joins']['Phone']);

// Prefill a specific contact for the af-repeat entity
$prefill = Civi\Api4\Afform::prefill()
->setName($this->formName)
->setArgs(['Individual1' => [1 => $cid[3]]])
->execute()
->indexBy('name');
$this->assertCount(1, $prefill['Individual1']['values']);
$this->assertEquals('D', $prefill['Individual1']['values'][1]['fields']['first_name']);
$this->assertEquals('_D', $prefill['Individual1']['values'][1]['fields']['last_name']);
$this->assertEquals('[email protected]', $prefill['Individual1']['values'][1]['joins']['Email'][0]['email']);
$this->assertEquals('3-1', $prefill['Individual1']['values'][1]['joins']['Phone'][0]['phone']);

// Form entity has `max="3"` so a forth contact (index 3) is out-of-bounds
$prefill = Civi\Api4\Afform::prefill()
->setName($this->formName)
->setArgs(['Individual1' => [3 => $cid[0]]])
->execute();
$this->assertTrue(empty($prefill['Individual1']['values']));
}

}

0 comments on commit b588537

Please sign in to comment.