) {
$realField = $fields[$key]['is_pseudofield_for'] ?? $key;
$realFieldSpec = $fields[$realField];
- /* @var \CRM_Core_DAO $bao */
- $bao = $realFieldSpec['bao'];
- // Get names & labels - we will try to match name first but if not available then see if
- // we have a label that can be converted to a name.
- // For historical reasons use validate as context - ie disabled name matches ARE permitted per prior to change.
- $nameOptions = $bao::buildOptions($realField, 'validate');
- if (!isset($nameOptions[$value])) {
- $labelOptions = array_flip($bao::buildOptions($realField, 'match'));
- if (isset($labelOptions[$params[$key]])) {
- $values[$key] = $labelOptions[$params[$key]];
- }
- }
+ $values[$key] = $this->parsePseudoConstantField($value, $realFieldSpec);
}
break;
}
$cacheKey = $daoName . $fieldName . serialize($params);
// Retrieve cached options
- if (isset(self::$cache[$cacheKey]) && empty($params['fresh'])) {
- $output = self::$cache[$cacheKey];
+ if (isset(\Civi::$statics[__CLASS__][$cacheKey]) && empty($params['fresh'])) {
+ $output = \Civi::$statics[__CLASS__][$cacheKey];
}
else {
$daoName = CRM_Core_DAO_AllCoreTables::getClassForTable($pseudoconstant['table']);
}
}
CRM_Utils_Hook::fieldOptions($entity, $fieldName, $output, $params);
- self::$cache[$cacheKey] = $output;
+ \Civi::$statics[__CLASS__][$cacheKey] = $output;
}
return $flip ? array_flip($output) : $output;
}
return $error;
}
+ /**
+ * Parse a field which could be represented by a label or name value rather than the DB value.
+ *
+ * We will try to match name first but if not available then see if we have a label that can be converted to a name.
+ *
+ * @param string|int|null $submittedValue
+ * @param array $fieldSpec
+ * Metadata for the field
+ *
+ * @return mixed
+ */
+ protected function parsePseudoConstantField($submittedValue, $fieldSpec) {
+ /* @var \CRM_Core_DAO $bao */
+ $bao = $fieldSpec['bao'];
+ // For historical reasons use validate as context - ie disabled name matches ARE permitted.
+ $nameOptions = $bao::buildOptions($fieldSpec['name'], 'validate');
+ if (!isset($nameOptions[$submittedValue])) {
+ $labelOptions = array_flip($bao::buildOptions($fieldSpec['name'], 'match'));
+ if (isset($labelOptions[$submittedValue])) {
+ return array_search($labelOptions[$submittedValue], $nameOptions, TRUE);
+ }
+ }
+ return '';
+ }
+
}
private $_membershipTypeIndex;
private $_membershipStatusIndex;
+ /**
+ * Array of metadata for all available fields.
+ *
+ * @var array
+ */
+ protected $fieldMetadata = [];
+
/**
* Array of successfully imported membership id's
*
* Class constructor.
*
* @param $mapperKeys
- * @param null $mapperLocType
- * @param null $mapperPhoneType
*/
- public function __construct(&$mapperKeys, $mapperLocType = NULL, $mapperPhoneType = NULL) {
+ public function __construct($mapperKeys) {
parent::__construct();
- $this->_mapperKeys = &$mapperKeys;
+ $this->_mapperKeys = $mapperKeys;
}
/**
* @return void
*/
public function init() {
- $fields = CRM_Member_BAO_Membership::importableFields($this->_contactType, FALSE);
+ $this->fieldMetadata = CRM_Member_BAO_Membership::importableFields($this->_contactType, FALSE);
- foreach ($fields as $name => $field) {
+ foreach ($this->fieldMetadata as $name => $field) {
+ // @todo - we don't really need to do all this.... fieldMetadata is just fine to use as is.
$field['type'] = CRM_Utils_Array::value('type', $field, CRM_Utils_Type::T_INT);
$field['dataPattern'] = CRM_Utils_Array::value('dataPattern', $field, '//');
$field['headerPattern'] = CRM_Utils_Array::value('headerPattern', $field, '//');
break;
case 'membership_type_id':
+ // @todo - squish into membership status - can use same lines here too.
$membershipTypes = CRM_Member_PseudoConstant::membershipType();
if (!CRM_Utils_Array::crmInArray($val, $membershipTypes) &&
!array_key_exists($val, $membershipTypes)
break;
case 'status_id':
- if (!CRM_Utils_Array::crmInArray($val, CRM_Member_PseudoConstant::membershipStatus())) {
+ if (!empty($val) && !$this->parsePseudoConstantField($val, $this->fieldMetadata[$key])) {
CRM_Contact_Import_Parser_Contact::addToErrorMsg('Membership Status', $errorMessage);
}
break;
break;
case 'status_id':
- if (!is_numeric($val)) {
- unset($params['status_id']);
- $params['membership_status'] = $val;
- }
+ // @todo - we can do this based on the presence of 'pseudoconstant' in the metadata rather than field specific.
+ $params[$key] = $this->parsePseudoConstantField($val, $this->fieldMetadata[$key]);
break;
case 'is_override':
}
//date-Format part ends
- static $indieFields = NULL;
- if ($indieFields == NULL) {
- $tempIndieFields = CRM_Member_DAO_Membership::import();
- $indieFields = $tempIndieFields;
- }
-
$formatValues = [];
foreach ($params as $key => $field) {
if ($field == NULL || $field === '') {
$values['membership_type_id'] = $membershipTypeId;
break;
- case 'status_id':
- if (!CRM_Utils_Array::value($value, CRM_Member_PseudoConstant::membershipStatus())) {
- throw new Exception('Invalid Membership Status Id');
- }
- $values[$key] = $value;
- break;
-
- case 'membership_status':
- $membershipStatusId = CRM_Utils_Array::key(ucfirst($value),
- CRM_Member_PseudoConstant::membershipStatus()
- );
- if ($membershipStatusId) {
- if (!empty($values['status_id']) &&
- $membershipStatusId != $values['status_id']
- ) {
- throw new Exception('Mismatched membership Status and Membership Status Id');
- }
- }
- else {
- throw new Exception('Invalid Membership Status');
- }
- $values['status_id'] = $membershipStatusId;
- break;
-
default:
break;
}
if (isset(self::$$name)) {
self::$$name = NULL;
}
+ // The preferred source of membership pseudoconstants is in fact the Core class.
+ // which buildOptions accesses - better flush that too.
+ CRM_Core_PseudoConstant::flush();
}
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
+ *
+ * @throws \CRM_Core_Exception
*/
public function tearDown() {
$tablesToTruncate = [
'civicrm_membership_log',
'civicrm_contribution',
'civicrm_membership_payment',
+ 'civicrm_contact',
];
- $this->quickCleanup($tablesToTruncate);
+ $this->quickCleanup($tablesToTruncate, TRUE);
$this->relationshipTypeDelete($this->_relationshipTypeId);
$this->membershipTypeDelete(['id' => $this->_membershipTypeID]);
$this->membershipStatusDelete($this->_mebershipStatusID);
- $this->contactDelete($this->_orgContactID);
}
/**
$this->assertContains('Required parameter missing: Status', $importValues);
}
+ /**
+ * Test that the passed in status is respected.
+ *
+ * @throws \CRM_Core_Exception
+ */
public function testImportOverriddenMembershipWithStatus() {
$this->individualCreate(['email' => 'anthony_anderson3@civicrm.org']);
-
- $fieldMapper = [
- 'mapper[0][0]' => 'email',
- 'mapper[1][0]' => 'membership_type_id',
- 'mapper[2][0]' => 'membership_start_date',
- 'mapper[3][0]' => 'is_override',
- 'mapper[4][0]' => 'status_id',
- ];
- $membershipImporter = new CRM_Member_Import_Parser_Membership($fieldMapper);
- $membershipImporter->init();
- $membershipImporter->_contactType = 'Individual';
+ $membershipImporter = $this->createImportObject([
+ 'email',
+ 'membership_type_id',
+ 'membership_start_date',
+ 'is_override',
+ 'status_id',
+ ]);
$importValues = [
'anthony_anderson3@civicrm.org',
$this->assertContains('Required parameter missing: Status', $importValues);
}
+ /**
+ * Test that memberships can still be imported if the status is renamed.
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testImportMembershipWithRenamedStatus() {
+ $this->individualCreate(['email' => 'anthony_anderson3@civicrm.org']);
+
+ $this->callAPISuccess('MembershipStatus', 'get', [
+ 'name' => 'New',
+ 'api.MembershipStatus.create' => [
+ 'label' => 'New-renamed',
+ ],
+ ]);
+ $membershipImporter = $this->createImportObject([
+ 'email',
+ 'membership_type_id',
+ 'membership_start_date',
+ 'is_override',
+ 'status_id',
+ ]);
+
+ $importValues = [
+ 'anthony_anderson3@civicrm.org',
+ $this->_membershipTypeID,
+ date('Y-m-d'),
+ TRUE,
+ 'New-renamed',
+ ];
+
+ $importResponse = $membershipImporter->import(CRM_Import_Parser::DUPLICATE_UPDATE, $importValues);
+ $this->assertEquals(CRM_Import_Parser::VALID, $importResponse);
+ $createdStatusID = $this->callAPISuccessGetValue('Membership', ['return' => 'status_id']);
+ $this->assertEquals(CRM_Core_PseudoConstant::getKey('CRM_Member_BAO_Membership', 'status_id', 'New'), $createdStatusID);
+ $this->callAPISuccess('MembershipStatus', 'get', [
+ 'name' => 'New',
+ 'api.MembershipStatus.create' => [
+ 'label' => 'New',
+ ],
+ ]);
+ }
+
+ /**
+ * Create an import object.
+ *
+ * @param array $fields
+ *
+ * @return \CRM_Member_Import_Parser_Membership
+ */
+ protected function createImportObject(array $fields): \CRM_Member_Import_Parser_Membership {
+ $fieldMapper = [];
+ foreach ($fields as $index => $field) {
+ $fieldMapper['mapper[' . $index . '][0]'] = $field;
+ }
+ $membershipImporter = new CRM_Member_Import_Parser_Membership($fieldMapper);
+ $membershipImporter->init();
+ $membershipImporter->_contactType = 'Individual';
+ return $membershipImporter;
+ }
+
}