1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-08 15:57:01 +02:00

Update PagesLoader.preloadPage() method to support a loadPageRefs option which loads page references separately and as a group. Not yet certain this will remain though, as it seems like the FieldtypePage loader is already fast enough

This commit is contained in:
Ryan Cramer
2024-11-15 14:31:03 -05:00
parent ca74514288
commit 53b7aa39eb

View File

@@ -2052,9 +2052,10 @@ class PagesLoader extends Wire {
* @param Page $page Page to preload fields for * @param Page $page Page to preload fields for
* @param array $fieldNames Names of fields to preload * @param array $fieldNames Names of fields to preload
* @param array $options * @param array $options
* - `debug` (bool): Specify true to return array of debug info (default=false). * - `debug` (bool): Specify true to include additional debug info in return value (default=false).
* - `useFieldtypeMulti` (bool): Enable FieldtypeMulti for testing purposes (default=false). * - `useFieldtypeMulti` (bool): Enable FieldtypeMulti for testing purposes (default=false).
* @return int|array Number of fields preloaded, or array of details (if debug) * - `loadPageRefs` (bool): Optimization to early load pages in page reference fields? (default=true)
* @return array Array containing what was loaded and skipped
* @since 3.0.243 * @since 3.0.243
* *
*/ */
@@ -2063,13 +2064,14 @@ class PagesLoader extends Wire {
$defaults = [ $defaults = [
'debug' => is_bool($options) ? $options : false, 'debug' => is_bool($options) ? $options : false,
'useFieldtypeMulti' => false, 'useFieldtypeMulti' => false,
'loadPageRefs' => true,
]; ];
static $level = 0; static $level = 0;
$options = is_array($options) ? array_merge($defaults, $options) : $defaults; $options = is_array($options) ? array_merge($defaults, $options) : $defaults;
$debug = $options['debug']; $debug = $options['debug'];
$database = $page->wire()->database; $database = $this->wire()->database;
$fieldNames = array_unique($fieldNames); $fieldNames = array_unique($fieldNames);
$fields = $page->wire()->fields; $fields = $page->wire()->fields;
$loadFields = []; $loadFields = [];
@@ -2084,10 +2086,9 @@ class PagesLoader extends Wire {
'skipped' => [], 'skipped' => [],
'blank' => [], 'blank' => [],
'queries' => 1, 'queries' => 1,
'timer' => 0.0,
]; ];
if(!$page->id || !$page->template) return $debug ? $log : 0; if(!$page->id || !$page->template) return $log;
foreach($fieldNames as $fieldKey => $fieldName) { foreach($fieldNames as $fieldKey => $fieldName) {
@@ -2099,7 +2100,7 @@ class PagesLoader extends Wire {
if($error) { if($error) {
unset($fieldNames[$fieldKey]); unset($fieldNames[$fieldKey]);
if($debug) $log['skipped'][$fieldName] = $error; if($fieldName) $log['skipped'][] = "$fieldName ($error)";
continue; continue;
} }
@@ -2127,7 +2128,7 @@ class PagesLoader extends Wire {
unset($fieldNames[$fieldKey]); unset($fieldNames[$fieldKey]);
} }
if(!count($selects)) return $debug ? $log : 0; if(!count($selects)) return $log;
$level++; $level++;
$timer = $debug ? Debug::timer() : false; $timer = $debug ? Debug::timer() : false;
@@ -2158,58 +2159,90 @@ class PagesLoader extends Wire {
} }
// wake up loaded values and populate to $page // wake up loaded values and populate to $page
$pageIds = [];
foreach($data as $fieldName => $sleepValue) { foreach($data as $fieldName => $sleepValue) {
if(!isset($loadFields[$fieldName])) continue; if(!isset($loadFields[$fieldName])) {
unset($data[$fieldName]);
continue;
}
$field = $loadFields[$fieldName]; $field = $loadFields[$fieldName];
$fieldtype = $field->type; $fieldtype = $field->type;
$cols = array_keys($sleepValue); $cols = array_keys($sleepValue);
if(count($cols) === 1 && array_key_exists('data', $sleepValue)) { if(count($cols) === 1 && array_key_exists('data', $sleepValue)) {
$sleepValue = $sleepValue['data']; $sleepValue = $sleepValue['data'];
} }
if($sleepValue === null) continue; // force to getBlankValue in loop below this if($sleepValue === null) {
unset($data[$fieldName]);
continue; // force to getBlankValue in loop below this
}
if($options['useFieldtypeMulti'] && $fieldtype instanceof FieldtypeMulti) { if($options['useFieldtypeMulti'] && $fieldtype instanceof FieldtypeMulti) {
if(strrpos($sleepValue, FieldtypeMulti::multiValueSeparator)) { if(strrpos($sleepValue, FieldtypeMulti::multiValueSeparator)) {
$sleepValue = explode(FieldtypeMulti::multiValueSeparator, $sleepValue); $sleepValue = explode(FieldtypeMulti::multiValueSeparator, $sleepValue);
} }
} }
if($fieldtype instanceof FieldtypePage && $sleepValue && $options['loadPageRefs']) {
if(!is_array($sleepValue)) $sleepValue = [ $sleepValue ];
foreach($sleepValue as $pageId) {
$pageId = (int) $pageId;
if(!$pageId) continue;
if($this->pages->cacher()->hasCache($pageId)) continue;
$parentId = $field->get('parent_id');
$templateId = FieldtypePage::getTemplateIDs($field, true);
if(!ctype_digit("$parentId")) $parentId = 0;
if(!ctype_digit("$templateId")) $templateId = 0;
$groupKey = "$parentId,$templateId";
if(!isset($pageIds[$groupKey])) $pageIds[$groupKey] = [];
$pageIds[$groupKey][$pageId] = $pageId;
}
}
$data[$fieldName] = $sleepValue;
}
// preload all pages in template or parent groups
if(count($pageIds)) {
foreach($pageIds as $groupKey => $ids) {
list($parentId, $templateId) = explode(',', $groupKey);
$this->pages->getByID($ids, [ 'template' => $templateId, 'parent_id' => $parentId ]);
}
}
foreach($data as $fieldName => $sleepValue) {
$field = $loadFields[$fieldName];
$fieldtype = $field->type;
$value = $fieldtype->wakeupValue($page, $field, $sleepValue); $value = $fieldtype->wakeupValue($page, $field, $sleepValue);
$page->_parentSet($field->name, $value); $page->_parentSet($field->name, $value);
$loadedFields[$field->name] = $fieldName; $loadedFields[$field->name] = $fieldName;
unset($loadFields[$field->name]); unset($loadFields[$field->name]);
if($debug) { $log['loaded'][] = $fieldName;
$log['loaded'][$fieldName] = "$fieldtype->shortName: " . implode(',', $cols);
}
} }
// any remaining loadFields not present in DB should get blank value // any remaining loadFields not present in DB should get blank value
foreach($loadFields as $field) { foreach($loadFields as $field) {
$value = $field->type->getBlankValue($page, $field); $value = $field->type->getBlankValue($page, $field);
$page->_parentSet($field->name, $value); $fieldName = $field->name;
if($debug) $log['blank'][$field->name] = $field->type->shortName; $page->_parentSet($fieldName, $value);
$log['blank'][] = $fieldName;
} }
$numLoaded = count($loadedFields);
// go recursive for any remaining fields // go recursive for any remaining fields
if(count($fieldNames)) { if(count($fieldNames)) {
$result = $this->preloadFields($page, $fieldNames, $debug); $result = $this->preloadFields($page, $fieldNames, $options);
if($debug) { foreach($log as $key => $value) {
foreach($log as $key => $value) { if(is_array($value)) {
if(is_array($value)) { $log[$key] = array_merge($value, $result[$key]);
$log[$key] = array_merge($value, $result[$key]); } else if(is_int($value)) {
} else if(is_int($value)) { $log[$key] += $result[$key];
$log[$key] += $result[$key];
}
} }
} else {
$numLoaded += $result;
} }
} }
$level--; $level--;
if($debug && $timer && !$level) $log['timer'] = Debug::timer($timer); if($debug && $timer && !$level) $log['timer'] = Debug::timer($timer);
return $debug ? $log : $numLoaded; return $log;
} }
/** /**
@@ -2285,7 +2318,7 @@ class PagesLoader extends Wire {
} else if($fieldtype instanceof FieldtypePage && $field->get('derefAsPage') > 0) { } else if($fieldtype instanceof FieldtypePage && $field->get('derefAsPage') > 0) {
// allow single-page matches // allow single-page matches
} else { } else {
$error = "$shortName: Unsupported"; $error = "$shortName: Unsupported without useFieldtypeMulti=true";
} }
} else if($fieldtype instanceof FieldtypeFieldsetOpen) { } else if($fieldtype instanceof FieldtypeFieldsetOpen) {
$error = 'Fieldset: Unsupported'; $error = 'Fieldset: Unsupported';