From ac4dfebfabfd2ecf0926eedd21f13687445fd260 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 29 Nov 2024 13:00:09 -0500 Subject: [PATCH] Update Fields class to keep an index of Field flags that can be read before fields are loaded. Also added a findByFlag() method that uses the index. --- wire/core/Fields.php | 93 +++++++++++++++++++++++++++++---- wire/core/WireSaveableItems.php | 16 ++++-- 2 files changed, 97 insertions(+), 12 deletions(-) diff --git a/wire/core/Fields.php b/wire/core/Fields.php index 6846047d..823f2dbf 100644 --- a/wire/core/Fields.php +++ b/wire/core/Fields.php @@ -5,7 +5,7 @@ * * Manages collection of ALL Field instances, not specific to any particular Fieldgroup * - * ProcessWire 3.x, Copyright 2023 by Ryan Cramer + * ProcessWire 3.x, Copyright 2024 by Ryan Cramer * https://processwire.com * * #pw-summary Manages all custom fields in ProcessWire, independently of any Fieldgroup. @@ -107,6 +107,13 @@ class Fields extends WireSaveableItems { */ protected $flagNames = array(); + /** + * Flags to field IDs + * + * @var array + */ + protected $flagsToIds = array(); + /** * Field names that are native/permanent to this instance of ProcessWire (configurable at runtime) * @@ -184,6 +191,50 @@ class Fields extends WireSaveableItems { return $this->wire(new Field()); } + /** + * Called after rows loaded from DB but before populated to this instance + * + * @param array $rows + * + */ + protected function loadRowsReady(array &$rows) { + for($flag = 1; $flag <= 256; $flag *= 2) { + $this->flagsToIds[$flag] = array(); + } + foreach($rows as $row) { + $flags = (int) $row['flags']; + if(empty($flags)) continue; + foreach($this->flagsToIds as $flag => $ids) { + if($flags & $flag) $this->flagsToIds[$flag][] = (int) $row['id']; + } + } + } + + /** + * Given field ID return native property + * + * This avoids loading the field if the property can be obtained natively. + * + * #pw-internal + * + * @param int $id + * @param string $property + * @return array|bool|mixed|string|null + * @since 3.0.243 + * + */ + public function fieldIdToProperty($id, $property) { + $id = (int) $id; + if(isset($this->lazyIdIndex[$id])) { + $n = $this->lazyIdIndex[$id]; + if(isset($this->lazyItems[$n][$property])) { + return $this->lazyItems[$n][$property]; + } + } + $field = $this->get($id); + return $field ? $field->get($property) : null; + } + /** * Make an item and populate with given data * @@ -250,7 +301,7 @@ class Fields extends WireSaveableItems { * @since 3.0.194 * */ - protected function initItem(array &$row, WireArray $items = null) { + protected function initItem(array &$row, ?WireArray $items = null) { /** @var Field $item */ $item = parent::initItem($row, $items); $fieldtype = $item ? $item->type : null; @@ -317,7 +368,7 @@ class Fields extends WireSaveableItems { * $fields->save($field); * ~~~~~ * - * @param Field|Saveable $item The field to save + * @param Field $item The field to save * @return bool True on success, false on failure * @throws WireException * @@ -432,7 +483,7 @@ class Fields extends WireSaveableItems { * * This method will throw a WireException if you attempt to delete a field that is currently in use (i.e. assigned to one or more fieldgroups). * - * @param Field|Saveable $item Field to delete + * @param Field $item Field to delete * @return bool True on success, false on failure * @throws WireException * @@ -473,7 +524,7 @@ class Fields extends WireSaveableItems { /** * Create and return a cloned copy of the given Field * - * @param Field|Saveable $item Field to clone + * @param Field $item Field to clone * @param string $name Optionally specify name for new cloned item * @return Field $item Returns the new clone on success, or false on failure * @@ -1093,6 +1144,30 @@ class Fields extends WireSaveableItems { return $items; } + /** + * Find fields by flag + * + * #pw-internal + * + * @param int $flag + * @param bool $getFieldNames + * @return array|Field[] + * @since 3.0.243 + * + */ + public function findByFlag($flag, $getFieldNames = false) { + if(!isset($this->flagsToIds[$flag])) return array(); + $items = []; + foreach($this->flagsToIds[$flag] as $id) { + if($getFieldNames) { + $items[] = $this->fieldIdToProperty($id, 'name'); + } else { + $items[] = $this->get($id); + } + } + return $items; + } + /** * Find fields by type * @@ -1239,7 +1314,7 @@ class Fields extends WireSaveableItems { * * #pw-internal * - * @param Field|int|string Field to check + * @param Field Field to check * @param string $permission Specify either 'view' or 'edit' * @param Page|null $page Optionally specify a page for context * @param User|null $user Optionally specify a user for context (default=current user) @@ -1247,7 +1322,7 @@ class Fields extends WireSaveableItems { * @throws WireException if given invalid arguments * */ - public function _hasPermission(Field $field, $permission, Page $page = null, User $user = null) { + public function _hasPermission(Field $field, $permission, ?Page $page = null, ?User $user = null) { if($permission != 'edit' && $permission != 'view') { throw new WireException('Specify either "edit" or "view"'); } @@ -1285,7 +1360,7 @@ class Fields extends WireSaveableItems { * * #pw-hooker * - * @param Field|Saveable $item + * @param Field $item * @param Fieldtype $fromType * @param Fieldtype $toType * @@ -1297,7 +1372,7 @@ class Fields extends WireSaveableItems { * * #pw-hooker * - * @param Field|Saveable $item + * @param Field $item * @param Fieldtype $fromType * @param Fieldtype $toType * diff --git a/wire/core/WireSaveableItems.php b/wire/core/WireSaveableItems.php index f046d495..6ab4be9f 100644 --- a/wire/core/WireSaveableItems.php +++ b/wire/core/WireSaveableItems.php @@ -220,7 +220,9 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { $query->execute(); $rows = $query->fetchAll(\PDO::FETCH_ASSOC); $n = 0; - + + $this->loadRowsReady($rows); + foreach($rows as $row) { if($useLazy) { $this->lazyItems[$n] = $row; @@ -238,6 +240,14 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { return $items; } + /** + * Called after rows loaded from DB but before populated to this instance + * + * @param array $rows + * + */ + protected function loadRowsReady(array &$rows) { } + /** * Create a new Saveable item from a raw array ($row) and add it to $items * @@ -247,7 +257,7 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { * @since 3.0.194 * */ - protected function initItem(array &$row, WireArray $items = null) { + protected function initItem(array &$row, ?WireArray $items = null) { if(!empty($row['data'])) { if(is_string($row['data'])) $row['data'] = $this->decodeData($row['data']); @@ -753,7 +763,7 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate { * @return WireLog * */ - public function log($str, Saveable $item = null) { + public function log($str, ?Saveable $item = null) { $logs = $this->wire()->config->logs; $name = $this->className(array('lowercase' => true)); if($logs && in_array($name, $logs)) {