Skip to content

Commit

Permalink
Defer field instantiation until actually needed
Browse files Browse the repository at this point in the history
Resolves #13992
  • Loading branch information
brandonkelly committed Dec 10, 2023
1 parent c5696ef commit 81f6766
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- It’s no longer possible to dismiss asset conflict resolution modals by pressing <kbd>Esc</kbd> or clicking outside of the modal. ([#14002](https://github.com/craftcms/cms/issues/14002))
- Improved performance for sites with lots of custom fields in non-global contexts. ([#13992](https://github.com/craftcms/cms/issues/13992))
- Added `craft\db\Connection::onAfterTransaction()`.
- Added `craft\fieldlayoutelements\TextField::$inputType`. ([#13988](https://github.com/craftcms/cms/issues/13988))
- Deprecated `craft\fieldlayoutelements\TextField::$type`. `$inputType` should be used instead. ([#13988](https://github.com/craftcms/cms/issues/13988))
Expand Down
77 changes: 52 additions & 25 deletions src/services/Fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,16 @@ class Fields extends Component
private ?MemoizableArray $_groups = null;

/**
* @var MemoizableArray<FieldInterface>|null
* @see _fields()
* @var MemoizableArray<array>|null
* @see _fieldConfigs()
*/
private ?MemoizableArray $_fields = null;
private ?MemoizableArray $_fieldConfigs = null;

/**
* @var FieldInterface[]
* @see _field()
*/
private array $_fields = [];

/**
* @var FieldLayout[]|null[]
Expand Down Expand Up @@ -620,36 +626,55 @@ public function createField(mixed $config): FieldInterface
}

/**
* Returns a memoizable array of all fields.
* Returns a memoizable array of field configs.
*
* @param string|string[]|false|null $context The field context(s) to fetch fields from. Defaults to [[\craft\services\Content::$fieldContext]].
* Set to `false` to get all fields regardless of context.
*
* @return MemoizableArray<FieldInterface>
* @return MemoizableArray<array>
*/
private function _fields(mixed $context = null): MemoizableArray
private function _fieldConfigs(mixed $context = null): MemoizableArray
{
if (!isset($this->_fields)) {
$fields = [];
foreach ($this->_createFieldQuery()->all() as $result) {
$fields[] = $this->createField($result);
}
$this->_fields = new MemoizableArray($fields);
$context ??= Craft::$app->getContent()->fieldContext;

if (!isset($this->_fieldConfigs)) {
$this->_fieldConfigs = new MemoizableArray($this->_createFieldQuery()->all());
}

if ($context === false) {
return $this->_fields;
return $this->_fieldConfigs;
}

if ($context === null) {
$context = Craft::$app->getContent()->fieldContext;
if (is_array($context)) {
return $this->_fieldConfigs->whereIn('context', $context, true);
}

if (is_array($context)) {
return $this->_fields->whereIn('context', $context, true);
return $this->_fieldConfigs->where('context', $context, true);
}

/**
* @param array[] $configs
* @return FieldInterface[]
*/
private function _fields(array $configs): array
{
return array_map(fn(array $config) => $this->_field($config), $configs);
}

/**
* @param array|null $config
* @return FieldInterface|null
*/
private function _field(?array $config): ?FieldInterface
{
if ($config === null) {
return null;
}

return $this->_fields->where('context', $context, true);
if (!isset($this->_fields[$config['id']])) {
$this->_fields[$config['id']] = $this->createField($config);
}
return $this->_fields[$config['id']];
}

/**
Expand All @@ -661,7 +686,7 @@ private function _fields(mixed $context = null): MemoizableArray
*/
public function getAllFields(mixed $context = null): array
{
return $this->_fields($context)->all();
return $this->_fields($this->_fieldConfigs($context)->all());
}

/**
Expand Down Expand Up @@ -720,7 +745,7 @@ public function getFieldsByType(string $type, mixed $context = null): array
*/
public function getFieldById(int $fieldId): ?FieldInterface
{
return $this->_fields(false)->firstWhere('id', $fieldId);
return $this->_field($this->_fieldConfigs(false)->firstWhere('id', $fieldId));
}

/**
Expand All @@ -731,7 +756,7 @@ public function getFieldById(int $fieldId): ?FieldInterface
*/
public function getFieldByUid(string $fieldUid): ?FieldInterface
{
return $this->_fields(false)->firstWhere('uid', $fieldUid, true);
return $this->_field($this->_fieldConfigs(false)->firstWhere('uid', $fieldUid, true));
}

/**
Expand All @@ -754,7 +779,7 @@ public function getFieldByUid(string $fieldUid): ?FieldInterface
*/
public function getFieldByHandle(string $handle, mixed $context = null): ?FieldInterface
{
return $this->_fields($context)->firstWhere('handle', $handle, true);
return $this->_field($this->_fieldConfigs($context)->firstWhere('handle', $handle, true));
}

/**
Expand All @@ -777,7 +802,7 @@ public function doesFieldWithHandleExist(string $handle, ?string $context = null
*/
public function getFieldsByGroupId(int $groupId): array
{
return $this->_fields(false)->where('groupId', $groupId)->all();
return $this->_fields($this->_fieldConfigs(false)->where('groupId', $groupId)->all());
}

/**
Expand Down Expand Up @@ -1028,7 +1053,8 @@ public function applyFieldDelete(string $fieldUid): void
}

// Clear caches
$this->_fields = null;
$this->_fieldConfigs = null;
$this->_fields = [];

// Update the field version
$this->updateFieldVersion();
Expand Down Expand Up @@ -1089,7 +1115,8 @@ private function _dropOldFieldColumns(string $handle, ?string $columnSuffix, arr
*/
public function refreshFields(): void
{
$this->_fields = null;
$this->_fieldConfigs = null;
$this->_fields = [];
$this->updateFieldVersion();
}

Expand Down

0 comments on commit 81f6766

Please sign in to comment.