Frontend optimizations for Flex Pages

This commit is contained in:
Matias Griese
2019-12-04 21:03:58 +02:00
parent b58f36b5af
commit 71bdb5e25e
8 changed files with 101 additions and 70 deletions

View File

@@ -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 }}`

View File

@@ -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;
}
}

View File

@@ -44,6 +44,9 @@ class Pages
/** @var array<PageInterface|string> */
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);
}

View File

@@ -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)) {

View File

@@ -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]);
}
}
}

View File

@@ -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();
}
/**

View File

@@ -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) {

View File

@@ -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;
}
/**