diff --git a/Civi/Api4/Generic/BasicGetFieldsAction.php b/Civi/Api4/Generic/BasicGetFieldsAction.php
index 7bb18411efc4..483cf7353e7d 100644
--- a/Civi/Api4/Generic/BasicGetFieldsAction.php
+++ b/Civi/Api4/Generic/BasicGetFieldsAction.php
@@ -282,9 +282,16 @@ public function fields() {
],
[
'name' => 'required',
+ 'description' => 'Is this field required when creating a new entity',
'data_type' => 'Boolean',
'default_value' => FALSE,
],
+ [
+ 'name' => 'nullable',
+ 'description' => 'Whether a null value is allowed in this field',
+ 'data_type' => 'Boolean',
+ 'default_value' => TRUE,
+ ],
[
'name' => 'required_if',
'data_type' => 'String',
diff --git a/Civi/Api4/Service/Spec/FieldSpec.php b/Civi/Api4/Service/Spec/FieldSpec.php
index e1da6ce9c15f..89f13864160f 100644
--- a/Civi/Api4/Service/Spec/FieldSpec.php
+++ b/Civi/Api4/Service/Spec/FieldSpec.php
@@ -63,6 +63,11 @@ class FieldSpec {
/**
* @var bool
*/
+ public $nullable = TRUE;
+
+ /**
+ * @var string
+ */
public $requiredIf;
/**
@@ -127,6 +132,24 @@ public function getEntity() {
return $this->entity;
}
+ /**
+ * @return bool
+ */
+ public function getNullable() {
+ return $this->nullable;
+ }
+
+ /**
+ * @param bool $nullable
+ *
+ * @return $this
+ */
+ public function setNullable(bool $nullable) {
+ $this->nullable = $nullable;
+
+ return $this;
+ }
+
/**
* @return bool
*/
@@ -146,14 +169,14 @@ public function setRequired($required) {
}
/**
- * @return bool
+ * @return string
*/
public function getRequiredIf() {
return $this->requiredIf;
}
/**
- * @param bool $requiredIf
+ * @param string $requiredIf
*
* @return $this
*/
diff --git a/Civi/Api4/Service/Spec/SpecFormatter.php b/Civi/Api4/Service/Spec/SpecFormatter.php
index cee41e1ac1da..058f83dea24f 100644
--- a/Civi/Api4/Service/Spec/SpecFormatter.php
+++ b/Civi/Api4/Service/Spec/SpecFormatter.php
@@ -37,6 +37,7 @@ public static function arrayToField(array $data, $entity) {
$field->setTableName($data['custom_group_id.table_name']);
}
$field->setColumnName($data['column_name']);
+ $field->setNullable(empty($data['is_required']));
$field->setCustomFieldId($data['id'] ?? NULL);
$field->setCustomGroupName($data['custom_group_id.name']);
$field->setTitle($data['label']);
@@ -58,7 +59,8 @@ public static function arrayToField(array $data, $entity) {
$field = new FieldSpec($name, $entity, $dataTypeName);
$field->setType('Field');
$field->setColumnName($name);
- $field->setRequired(!empty($data['required']));
+ $field->setNullable(empty($data['required']));
+ $field->setRequired(!empty($data['required']) && empty($data['default']));
$field->setTitle($data['title'] ?? NULL);
$field->setLabel($data['html']['label'] ?? NULL);
if (!empty($data['pseudoconstant'])) {
diff --git a/Civi/Api4/Service/Spec/SpecGatherer.php b/Civi/Api4/Service/Spec/SpecGatherer.php
index c4f49b7c9b53..d166207bda1b 100644
--- a/Civi/Api4/Service/Spec/SpecGatherer.php
+++ b/Civi/Api4/Service/Spec/SpecGatherer.php
@@ -97,9 +97,6 @@ private function addDAOFields($entity, $action, RequestSpec $spec) {
) {
continue;
}
- if ($action !== 'create' || isset($DAOField['default'])) {
- $DAOField['required'] = FALSE;
- }
if ($DAOField['name'] == 'is_active' && empty($DAOField['default'])) {
$DAOField['default'] = '1';
}
diff --git a/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php b/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
index 454b65db0e67..d702f763357b 100644
--- a/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
+++ b/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
@@ -514,7 +514,7 @@ private function formatEditableColumn($column, $data) {
/**
* @param $key
- * @return array{entity: string, input_type: string, data_type: string, options: bool, serialize: bool, fk_entity: string, value_key: string, value_path: string, id_key: string, id_path: string}|null
+ * @return array{entity: string, input_type: string, data_type: string, options: bool, serialize: bool, nullable: bool, fk_entity: string, value_key: string, value_path: string, id_key: string, id_path: string}|null
*/
private function getEditableInfo($key) {
[$key] = explode(':', $key);
@@ -537,6 +537,7 @@ private function getEditableInfo($key) {
'data_type' => $field['data_type'],
'options' => !empty($field['options']),
'serialize' => !empty($field['serialize']),
+ 'nullable' => !empty($field['nullable']),
'fk_entity' => $field['fk_entity'],
'value_key' => $field['name'],
'value_path' => $key,
diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php
index f811dd772fca..21f87cfff69b 100644
--- a/ext/search_kit/Civi/Search/Admin.php
+++ b/ext/search_kit/Civi/Search/Admin.php
@@ -136,7 +136,7 @@ public static function getSchema(): array {
$entity['links'] = array_values($links);
}
$getFields = civicrm_api4($entity['name'], 'getFields', [
- 'select' => ['name', 'title', 'label', 'description', 'type', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize', 'entity', 'fk_entity', 'readonly', 'operators'],
+ 'select' => ['name', 'title', 'label', 'description', 'type', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize', 'entity', 'fk_entity', 'readonly', 'operators', 'nullable'],
'where' => [['name', 'NOT IN', ['api_key', 'hash']]],
'orderBy' => ['label'],
]);
diff --git a/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.component.js b/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.component.js
index 052b9dcfbc5f..f205e4a3aa05 100644
--- a/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.component.js
+++ b/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.component.js
@@ -29,6 +29,7 @@
options: col.edit.options,
fk_entity: col.edit.fk_entity,
serialize: col.edit.serialize,
+ nullable: col.edit.nullable
};
$(document).on('keydown.crmSearchDisplayEditable', function(e) {
@@ -36,8 +37,6 @@
$scope.$apply(function() {
ctrl.cancel();
});
- } else if (e.key === 'Enter') {
- $scope.$apply(ctrl.save);
}
});
@@ -71,7 +70,7 @@
action: 'update',
select: ['options'],
loadOptions: ['id', 'name', 'label', 'description', 'color', 'icon'],
- where: [['name', '=', ctrl.field.name]],
+ where: [['name', '=', ctrl.field.name]]
}, 0).then(function(field) {
ctrl.field.options = optionsCache[cacheKey] = field.options;
});
diff --git a/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.html b/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.html
index d0d8eca08f3c..767c64229470 100644
--- a/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.html
+++ b/ext/search_kit/ang/crmSearchDisplay/crmSearchDisplayEditable.html
@@ -1,9 +1,11 @@
-