From 71bdb5e25e08487fbc43664ac273201170b78bd7 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 4 Dec 2019 21:03:58 +0200 Subject: [PATCH] Frontend optimizations for `Flex Pages` --- CHANGELOG.md | 1 + .../Flex/Pages/Traits/PageContentTrait.php | 5 +- system/src/Grav/Common/Page/Pages.php | 82 ++++++++++--------- system/src/Grav/Common/Taxonomy.php | 22 ++--- .../src/Grav/Framework/Flex/FlexDirectory.php | 29 +++---- system/src/Grav/Framework/Flex/FlexObject.php | 9 +- .../Flex/Pages/Traits/PageLegacyTrait.php | 4 +- .../Flex/Pages/Traits/PageRoutableTrait.php | 19 ++++- 8 files changed, 101 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99c0958a5..cf689c356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Output the current username that Scheduler is using if crontab not setup * Translations: rename MODULAR to MODULE everywhere * Optimized `Flex Pages` collection filtering + * Frontend optimizations for `Flex Pages` 1. [](#bugfix) * Regression: Fixed Grav update bug [#2722](https://github.com/getgrav/grav/issues/2722) * Fixed fatal error when calling `{{ grav.undefined }}` diff --git a/system/src/Grav/Common/Flex/Pages/Traits/PageContentTrait.php b/system/src/Grav/Common/Flex/Pages/Traits/PageContentTrait.php index c1e8904d9..7606fc22a 100644 --- a/system/src/Grav/Common/Flex/Pages/Traits/PageContentTrait.php +++ b/system/src/Grav/Common/Flex/Pages/Traits/PageContentTrait.php @@ -68,7 +68,8 @@ trait PageContentTrait */ public function isPage(bool $bool = true): bool { - // FIXME: needs to be better - return !$this->exists() || !empty($this->getLanguages()) || $this->isModule(); + $meta = $this->getMetaData(); + + return !empty($meta['markdown']) !== $bool; } } diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 0e9872fd2..ea873094f 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -44,6 +44,9 @@ class Pages /** @var array */ protected $instances; + /** @var array */ + protected $index = []; + /** @var array */ protected $children; @@ -86,6 +89,9 @@ class Pages /** @var bool */ protected $initialized = false; + /** @var bool */ + protected $fire_events = false; + /** @var Types|null */ protected static $types; @@ -259,11 +265,13 @@ class Pages } $config = $this->grav['config']; - $this->ignore_files = $config->get('system.pages.ignore_files'); - $this->ignore_folders = $config->get('system.pages.ignore_folders'); - $this->ignore_hidden = $config->get('system.pages.ignore_hidden'); + $this->ignore_files = (array)$config->get('system.pages.ignore_files'); + $this->ignore_folders = (array)$config->get('system.pages.ignore_folders'); + $this->ignore_hidden = (bool)$config->get('system.pages.ignore_hidden'); + $this->fire_events = (bool)$config->get('system.pages.events.page'); $this->instances = []; + $this->index = []; $this->children = []; $this->routes = []; @@ -305,18 +313,14 @@ class Pages */ public function instances() { - $list = []; - foreach ($this->instances as $path => $instance) { - if (is_string($instance) && $this->directory) { - /** @var PageInterface $instance */ - $instance = $this->directory->getObject($instance, 'flex_key'); - } - if ($instance instanceof PageInterface) { - $list[$path] = $instance; + // Make sure we load all the instances. + foreach ($this->index as $path => $instance) { + if ($instance && !$instance instanceof PageInterface) { + $this->get($path); } } - return $list; + return $this->instances; } /** @@ -338,7 +342,8 @@ class Pages public function addPage(PageInterface $page, $route = null): void { $path = $page->path() ?? ''; - if (!isset($this->instances[$path])) { + if (!isset($this->index[$path])) { + $this->index[$path] = $page; $this->instances[$path] = $page; } $route = $page->route($route); @@ -711,22 +716,22 @@ class Pages */ public function get($path) { - $instance = $this->instances[(string)$path] ?? null; + $path = (string)$path; + // Check for local instances first. + if (array_key_exists($path, $this->instances)) { + return $this->instances[$path]; + } + + $instance = $this->index[$path] ?? null; if (\is_string($instance)) { $instance = $this->directory ? $this->directory->getObject($instance, 'flex_key') : null; - if ($instance && method_exists($instance, 'initialize') && $this->grav['config']->get('system.pages.events.page')) { + if ($instance && $this->fire_events && method_exists($instance, 'initialize')) { $instance->initialize(); } } - if (null === $instance) { - return null; - } - - if (!$instance instanceof PageInterface) { - throw new \RuntimeException('Routing failed on unknown type', 500); - } + $this->instances[$path] = $instance; return $instance; } @@ -1359,7 +1364,7 @@ class Pages $cached = $cache->get($this->pages_cache_id); if ($cached && $this->getVersion() === $cached[0]) { - [, $this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort] = $cached; + [, $this->index, $this->routes, $this->children, $taxonomy_map, $this->sort] = $cached; /** @var Taxonomy $taxonomy */ $taxonomy = $this->grav['taxonomy']; @@ -1376,10 +1381,11 @@ class Pages $root_path = $root->path(); $this->routes = []; $this->instances = [$root_path => $root]; + $this->index = [$root_path => $root]; $this->children = []; $this->sort = []; - if ($config->get('system.pages.events.page')) { + if ($this->fire_events) { $this->grav->fireEvent('onBuildPagesInitialized'); } @@ -1398,7 +1404,7 @@ class Pages continue; } - if ($config->get('system.pages.events.page')) { + if ($this->fire_events) { if (method_exists($page, 'initialize')) { $page->initialize(); } else { @@ -1416,14 +1422,15 @@ class Pages if (isset($this->routes[$route])) { $oldPath = $this->routes[$route]; if ($page->isPage()) { - unset($this->instances[$oldPath], $this->children[dirname($oldPath)][$oldPath]); + unset($this->index[$oldPath], $this->children[dirname($oldPath)][$oldPath]); } else { continue; } } $this->routes[$route] = $path; - $this->instances[$path] = $page->getFlexKey(); + $this->instances[$path] = $page; + $this->index[$path] = $page->getFlexKey(); // FIXME: ... better... $this->children[$parent][$path] = ['slug' => $page->slug()]; if (!isset($this->children[$path])) { @@ -1432,12 +1439,12 @@ class Pages } foreach ($this->children as $path => $list) { - $page = $this->get($path); + $page = $this->instances[$path] ?? null; if (null === $page) { continue; } // Call onFolderProcessed event. - if ($config->get('system.pages.events.page')) { + if ($this->fire_events) { $this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page])); } // Sort the children. @@ -1454,7 +1461,7 @@ class Pages $taxonomy_map = $taxonomy->taxonomy(); // save pages, routes, taxonomy, and sort to cache - $cache->set($this->pages_cache_id, [$this->getVersion(), $this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort]); + $cache->set($this->pages_cache_id, [$this->getVersion(), $this->index, $this->routes, $this->children, $taxonomy_map, $this->sort]); } } @@ -1526,7 +1533,7 @@ class Pages $cache = $this->grav['cache']; $cached = $cache->fetch($this->pages_cache_id); if ($cached && $this->getVersion() === $cached[0]) { - [, $this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort] = $cached; + [, $this->index, $this->routes, $this->children, $taxonomy_map, $this->sort] = $cached; /** @var Taxonomy $taxonomy */ $taxonomy = $this->grav['taxonomy']; @@ -1562,7 +1569,7 @@ class Pages $taxonomy = $this->grav['taxonomy']; // save pages, routes, taxonomy, and sort to cache - $cache->save($this->pages_cache_id, [$this->getVersion(), $this->instances, $this->routes, $this->children, $taxonomy->taxonomy(), $this->sort]); + $cache->save($this->pages_cache_id, [$this->getVersion(), $this->index, $this->routes, $this->children, $taxonomy->taxonomy(), $this->sort]); } } @@ -1588,7 +1595,7 @@ class Pages // Stuff to do at root page // Fire event for memory and time consuming plugins... - if ($parent === null && $config->get('system.pages.events.page')) { + if ($parent === null && $this->fire_events) { $this->grav->fireEvent('onBuildPagesInitialized'); } @@ -1601,7 +1608,8 @@ class Pages $page->orderBy($config->get('system.pages.order.by')); // Add into instances - if (!isset($this->instances[$page->path()])) { + if (!isset($this->index[$page->path()])) { + $this->index[$page->path()] = $page; $this->instances[$page->path()] = $page; if ($parent && $page->path()) { $this->children[$parent->path()][$page->path()] = ['slug' => $page->slug()]; @@ -1671,7 +1679,7 @@ class Pages $content_exists = true; - if ($config->get('system.pages.events.page')) { + if ($this->fire_events) { $this->grav->fireEvent('onPageProcessed', new Event(['page' => $page])); } } @@ -1699,7 +1707,7 @@ class Pages $this->children[$page->path()][$child->path()] = ['slug' => $child->slug()]; - if ($config->get('system.pages.events.page')) { + if ($this->fire_events) { $this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page])); } } @@ -1747,7 +1755,7 @@ class Pages $home = self::resetHomeRoute(); // Build routes and taxonomy map. /** @var PageInterface|string $page */ - foreach ($this->instances as $path => $page) { + foreach ($this->index as $path => $page) { if (\is_string($page)) { $page = $this->get($path); } diff --git a/system/src/Grav/Common/Taxonomy.php b/system/src/Grav/Common/Taxonomy.php index 6ffe695b9..ebf9356b5 100644 --- a/system/src/Grav/Common/Taxonomy.php +++ b/system/src/Grav/Common/Taxonomy.php @@ -57,23 +57,25 @@ class Taxonomy */ public function addTaxonomy(PageInterface $page, $page_taxonomy = null) { + if (!$page->published()) { + return; + } + if (!$page_taxonomy) { $page_taxonomy = $page->taxonomy(); } - if (empty($page_taxonomy) || !$page->published()) { + if (empty($page_taxonomy)) { return; } /** @var Config $config */ $config = $this->grav['config']; - if ($config->get('site.taxonomies')) { - foreach ((array)$config->get('site.taxonomies') as $taxonomy) { - if (isset($page_taxonomy[$taxonomy])) { - foreach ((array)$page_taxonomy[$taxonomy] as $item) { - $this->iterateTaxonomy($page, $taxonomy, '', $item); - } - } + $taxonomies = (array)$config->get('site.taxonomies'); + foreach ($taxonomies as $taxonomy) { + $current = $page_taxonomy[$taxonomy] ?? null; + foreach ((array)$current as $item) { + $this->iterateTaxonomy($page, $taxonomy, '', $item); } } } @@ -94,8 +96,8 @@ class Taxonomy { if (is_iterable($value)) { foreach ($value as $identifier => $item) { - $identifier = $key . '.' . $identifier; - $this->iterateTaxonomy($page, $taxonomy, $identifier, $item); + $identifier = "{$key}.{$identifier}"; + $this->iterateTaxonomy($page, $taxonomy, $identifier, $item); } } elseif (is_string($value)) { if (!empty($key)) { diff --git a/system/src/Grav/Framework/Flex/FlexDirectory.php b/system/src/Grav/Framework/Flex/FlexDirectory.php index c07fe4fd7..05f976c9d 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectory.php +++ b/system/src/Grav/Framework/Flex/FlexDirectory.php @@ -544,25 +544,26 @@ class FlexDirectory implements FlexAuthorizeInterface // Attempt to fetch missing rows from the cache. if ($fetch) { try { - $index = $this->loadIndex('storage_key'); - $debugger->startTimer('flex-objects', sprintf('Flex: Loading %d %s', $loading, $this->type)); $fetched = (array)$cache->getMultiple($fetch); + if ($fetched) { + $index = $this->loadIndex('storage_key'); - // Make sure cached objects are up to date: compare against index checksum/timestamp. - foreach ($fetched as $key => $value) { - if ($value instanceof FlexObjectInterface) { - $objectMeta = $value->getMetaData(); - } else { - $objectMeta = $value['__META'] ?? []; - } - $indexMeta = $index->getMetaData($key); + // Make sure cached objects are up to date: compare against index checksum/timestamp. + foreach ($fetched as $key => $value) { + if ($value instanceof FlexObjectInterface) { + $objectMeta = $value->getMetaData(); + } else { + $objectMeta = $value['__META'] ?? []; + } + $indexMeta = $index->getMetaData($key); - $indexChecksum = $indexMeta['checksum'] ?? $indexMeta['storage_timestamp'] ?? null; - $objectChecksum = $objectMeta['checksum'] ?? $objectMeta['storage_timestamp'] ?? null; - if ($indexChecksum !== $objectChecksum) { - unset($fetched[$key]); + $indexChecksum = $indexMeta['checksum'] ?? $indexMeta['storage_timestamp'] ?? null; + $objectChecksum = $objectMeta['checksum'] ?? $objectMeta['storage_timestamp'] ?? null; + if ($indexChecksum !== $objectChecksum) { + unset($fetched[$key]); + } } } diff --git a/system/src/Grav/Framework/Flex/FlexObject.php b/system/src/Grav/Framework/Flex/FlexObject.php index 0311c40f7..4aa8b5a75 100644 --- a/system/src/Grav/Framework/Flex/FlexObject.php +++ b/system/src/Grav/Framework/Flex/FlexObject.php @@ -261,7 +261,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface */ public function getMetaData(): array { - return $this->getStorage(); + return $this->_meta ?? []; } /** @@ -830,7 +830,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface 'storage_timestamp:protected' => $this->getTimestamp(), 'key:private' => $this->getKey(), 'elements:private' => $this->getElements(), - 'storage:private' => $this->getStorage() + 'storage:private' => $this->getMetaData() ]; } @@ -843,7 +843,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface 'type' => $this->getFlexType(), 'key' => $this->getKey(), 'elements' => $this->getElements(), - 'storage' => $this->getStorage() + 'storage' => $this->getMetaData() ]; } @@ -889,10 +889,11 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface /** * @return array + * @deprecated 1.7 Use `->getMetaData()` instead. */ protected function getStorage(): array { - return $this->_meta ?? []; + return $this->getMetaData(); } /** diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php index 457706565..b4b48133c 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php @@ -847,8 +847,8 @@ trait PageLegacyTrait */ public function children() { - $storage = $this->getStorage(); - $keys = array_keys($storage['children'] ?? []); + $meta = $this->getMetaData(); + $keys = array_keys($meta['children'] ?? []); $prefix = $this->getMasterKey(); if ($prefix) { foreach ($keys as &$key) { diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php index 184d89539..d836e4ff0 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php @@ -23,6 +23,11 @@ use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; */ trait PageRoutableTrait { + /** @var string */ + private $_route; + /** @var string */ + private $_path; + /** * Returns the page extension, got from the page `url_extension` config and falls back to the * system config `system.pages.append_url_extension`. @@ -176,6 +181,11 @@ trait PageRoutableTrait */ protected function routeInternal(): ?string { + $route = $this->_route; + if ($route) { + return $route; + } + // Root and orphan nodes have no route. $parent = $this->parent(); if (!$parent) { @@ -199,6 +209,8 @@ trait PageRoutableTrait $route .= $this->slug(); } + $this->_route = $route; + return $route; } @@ -316,6 +328,11 @@ trait PageRoutableTrait throw new \RuntimeException(__METHOD__ . '(string): Not Implemented'); } + $path = $this->_path; + if ($path) { + return $path; + } + if ($this->root()) { $folder = $this->getFlexDirectory()->getStorageFolder(); } else { @@ -328,7 +345,7 @@ trait PageRoutableTrait $folder = $locator($folder); } - return is_string($folder) ? $folder : null; + return $this->_path = is_string($folder) ? $folder : null; } /**