Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/extract menus, and cache them properly #389

Merged
merged 10 commits into from Apr 13, 2019
2 changes: 1 addition & 1 deletion config/services.yaml
Expand Up @@ -39,7 +39,7 @@ services:
tags:
- { name: doctrine.event_listener, event: postLoad }

Bolt\Menu\MenuBuilder:
Bolt\Menu\BackendMenuBuilder:
arguments: ["@knp_menu.factory"]
tags:
- { name: knp_menu.menu_builder, method: createSidebarMenu, alias: sidebar } # The alias is what is used to retrieve the menu
Expand Down
32 changes: 13 additions & 19 deletions src/Menu/MenuBuilder.php → src/Menu/BackendMenuBuilder.php
Expand Up @@ -10,21 +10,18 @@
use Bolt\Repository\ContentRepository;
use Bolt\Twig\ContentExtension;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Contracts\Translation\TranslatorInterface;

class MenuBuilder
class BackendMenuBuilder
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
/** @var FactoryInterface */
private $menuFactory;

/** @var Config */
private $config;

/** @var Stopwatch */
private $stopwatch;

/** @var ContentRepository */
private $contentRepository;

Expand All @@ -37,21 +34,24 @@ class MenuBuilder
/** @var ContentExtension */
private $contentExtension;

public function __construct(FactoryInterface $menuFactory, Config $config, Stopwatch $stopwatch, ContentRepository $contentRepository, UrlGeneratorInterface $urlGenerator, TranslatorInterface $translator, ContentExtension $contentExtension)
{
public function __construct(
FactoryInterface $menuFactory,
Config $config,
ContentRepository $contentRepository,
UrlGeneratorInterface $urlGenerator,
TranslatorInterface $translator,
ContentExtension $contentExtension
) {
$this->menuFactory = $menuFactory;
$this->config = $config;
$this->stopwatch = $stopwatch;
$this->contentRepository = $contentRepository;
$this->urlGenerator = $urlGenerator;
$this->translator = $translator;
$this->contentExtension = $contentExtension;
}

public function createSidebarMenu()
private function createMenu(): ItemInterface
{
$this->stopwatch->start('bolt.sidebar');

$t = $this->translator;

$menu = $this->menuFactory->createItem('root');
Expand Down Expand Up @@ -277,8 +277,6 @@ public function createSidebarMenu()
],
]);

$this->stopwatch->stop('bolt.sidebar');

return $menu;
}

Expand All @@ -288,13 +286,10 @@ private function getLatestRecords($slug)
$contentType = ContentType::factory($slug, $this->config->get('contenttypes'));

/** @var Content[] $records */
$this->stopwatch->start('menuBuilder.findLatest');
$records = $this->contentRepository->findLatest($contentType, 5);
$this->stopwatch->stop('menuBuilder.findLatest');

$result = [];

$this->stopwatch->start('menuBuilder.parseLatest');
/** @var Content $record */
foreach ($records as $record) {
$result[] = [
Expand All @@ -305,14 +300,13 @@ private function getLatestRecords($slug)
'icon' => $record->getIcon(),
];
}
$this->stopwatch->stop('menuBuilder.parseLatest');

return $result;
}

public function getMenu()
public function getMenu(): array
{
$menu = $this->createSidebarMenu()->getChildren();
$menu = $this->createMenu()->getChildren();

$menuData = [];

Expand Down
51 changes: 51 additions & 0 deletions src/Menu/CachedBackendMenuBuilder.php
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Bolt\Menu;

use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Stopwatch\Stopwatch;

class CachedBackendMenuBuilder
JarJak marked this conversation as resolved.
Show resolved Hide resolved
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
/** @var CacheInterface */
private $cache;

/** @var BackendMenuBuilder */
private $menuBuilder;

/** @var Stopwatch */
private $stopwatch;

public function __construct(CacheInterface $cache, BackendMenuBuilder $menuBuilder, Stopwatch $stopwatch)
{
$this->cache = $cache;
$this->menuBuilder = $menuBuilder;
$this->stopwatch = $stopwatch;
}

public function getMenu(): array
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
if ($this->cache->has('backendmenu')) {
$menu = $this->cache->get('backendmenu');
} else {
$this->stopwatch->start('bolt.sidebarMenu');

$menu = $this->menuBuilder->getMenu();
$this->cache->set('backendmenu', $menu);

$this->stopwatch->stop('bolt.sidebarMenu');
}

return $menu;
}

public function getMenuJson(bool $jsonPrettyPrint = false): string
{
$menu = $this->getMenu();
$options = $jsonPrettyPrint ? JSON_PRETTY_PRINT : 0;

return json_encode($menu, $options);
}
}
55 changes: 55 additions & 0 deletions src/Menu/CachedFrontendMenuBuilder.php
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Bolt\Menu;

use Bolt\Collection\DeepCollection;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Stopwatch\Stopwatch;

class CachedFrontendMenuBuilder
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
/** @var CacheInterface */
private $cache;

/** @var FrontendMenuBuilder */
private $menuBuilder;

/** @var Stopwatch */
private $stopwatch;

public function __construct(CacheInterface $cache, FrontendMenuBuilder $menuBuilder, Stopwatch $stopwatch)
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
$this->cache = $cache;
$this->menuBuilder = $menuBuilder;
$this->stopwatch = $stopwatch;
}

public function getMenu(?string $name = ''): ?DeepCollection
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
$key = 'frontendmenu_' . ($name ?: 'default');

if ($this->cache->has($key)) {
$menu = $this->cache->get($key);
} else {
$this->stopwatch->start('bolt.frontendMenu');

$menu = $this->menuBuilder->getMenu($name);

$this->cache->set($key, $menu);

$this->stopwatch->stop('bolt.frontendMenu');
}

return $menu;
}

public function getMenuJson(?string $name = '', bool $jsonPrettyPrint = false): string
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
$menu = $this->getMenu($name);
$options = $jsonPrettyPrint ? JSON_PRETTY_PRINT : 0;

return json_encode($menu, $options);
}
}
41 changes: 41 additions & 0 deletions src/Twig/BackendMenuExtension.php
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Bolt\Twig;

use Bolt\Menu\CachedBackendMenuBuilder;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class BackendMenuExtension extends AbstractExtension
{
/** @var CachedBackendMenuBuilder */
private $menuBuilder;

public function __construct(CachedBackendMenuBuilder $menuBuilder)
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
$this->menuBuilder = $menuBuilder;
}

/**
* {@inheritdoc}
*/
public function getFunctions(): array
{
return [
new TwigFunction('admin_menu', [$this, 'getAdminMenu']),
new TwigFunction('admin_menu_json', [$this, 'getAdminMenuJson']),
];
}

public function getAdminMenu(): array
{
return $this->menuBuilder->getMenu();
}

public function getAdminMenuJson($jsonPrettyPrint = false): string
{
return $this->menuBuilder->getMenuJson($jsonPrettyPrint);
}
}
51 changes: 51 additions & 0 deletions src/Twig/FrontendMenuExtension.php
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Bolt\Twig;

use Bolt\Menu\CachedFrontendMenuBuilder;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class FrontendMenuExtension extends AbstractExtension
{
/** @var CachedFrontendMenuBuilder */
private $menuBuilder;

public function __construct(CachedFrontendMenuBuilder $menuBuilder)
JarJak marked this conversation as resolved.
Show resolved Hide resolved
{
$this->menuBuilder = $menuBuilder;
}

/**
* {@inheritdoc}
*/
public function getFunctions(): array
{
$safe = ['is_safe' => ['html']];
$env = ['needs_environment' => true];

return [
new TwigFunction('menu', [$this, 'getMenu'], $env + $safe),
new TwigFunction('menu_json', [$this, 'getMenuJson']),
];
}

public function getMenu(Environment $twig, ?string $name = null, string $template = '_sub_menu.twig', string $class = '', bool $withsubmenus = true): string
{
$context = [
'menu' => $this->menuBuilder->getMenu($name),
'class' => $class,
'withsubmenus' => $withsubmenus,
];

return $twig->render($template, $context);
}

public function getMenuJson(?string $name = null, bool $jsonPrettyPrint = false)
{
return $this->menuBuilder->getMenuJson($name, $jsonPrettyPrint);
}
}
41 changes: 2 additions & 39 deletions src/Twig/RecordExtension.php
Expand Up @@ -6,8 +6,6 @@

use Bolt\Entity\Content;
use Bolt\Entity\Field;
use Bolt\Menu\FrontendMenuBuilder;
use Bolt\Menu\MenuBuilder;
use Bolt\Repository\TaxonomyRepository;
use Bolt\Utils\Excerpt;
use Doctrine\Common\Collections\Collection;
Expand All @@ -24,29 +22,18 @@
*/
class RecordExtension extends AbstractExtension
{
/** @var MenuBuilder */
private $menuBuilder;

/** @var string */
private $sidebarMenu = null;

/** @var TaxonomyRepository */
private $taxonomyRepository;

/** @var FrontendMenuBuilder */
private $frontendMenuBuilder;

public function __construct(MenuBuilder $menuBuilder, TaxonomyRepository $taxonomyRepository, FrontendMenuBuilder $frontendMenuBuilder)
public function __construct(TaxonomyRepository $taxonomyRepository)
{
$this->menuBuilder = $menuBuilder;
$this->taxonomyRepository = $taxonomyRepository;
$this->frontendMenuBuilder = $frontendMenuBuilder;
}

/**
* {@inheritdoc}
*/
public function getFunctions()
public function getFunctions(): array
{
$safe = ['is_safe' => ['html']];
$env = ['needs_environment' => true];
Expand All @@ -55,8 +42,6 @@ public function getFunctions()
new TwigFunction('excerpt', [$this, 'excerpt'], $safe),
new TwigFunction('list_templates', [$this, 'getListTemplates']),
new TwigFunction('pager', [$this, 'pager'], $env + $safe),
new TwigFunction('menu', [$this, 'getMenu'], $env + $safe),
new TwigFunction('sidebar_menu', [$this, 'getSidebarMenu']),
new TwigFunction('selectoptionsfromarray', [$this, 'selectoptionsfromarray']),
new TwigFunction('taxonomyoptions', [$this, 'taxonomyoptions']),
new TwigFunction('taxonomyvalues', [$this, 'taxonomyvalues']),
Expand All @@ -81,33 +66,11 @@ public function pager(Environment $twig, Pagerfanta $records, string $template =
return $twig->render($template, $context);
}

public function getMenu(Environment $twig, ?string $name = null, string $template = '_sub_menu.twig', string $class = '', bool $withsubmenus = true): string
{
$context = [
'menu' => $this->frontendMenuBuilder->getMenu($name),
'class' => $class,
'withsubmenus' => $withsubmenus,
];

return $twig->render($template, $context);
}

public static function excerpt(string $text, int $length = 100): string
{
return Excerpt::getExcerpt($text, $length);
}

public function getSidebarMenu($pretty = false): string
{
if (! $this->sidebarMenu) {
$menuArray = $this->menuBuilder->getMenu();
$options = $pretty ? JSON_PRETTY_PRINT : 0;
$this->sidebarMenu = json_encode($menuArray, $options);
}

return $this->sidebarMenu;
}

public function icon(?Content $record = null, string $icon = 'question-circle'): string
{
if ($record instanceof Content) {
Expand Down