diff --git a/composer.json b/composer.json index f296672..b42c1d2 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "erusev/parsedown": "^1.7", "erusev/parsedown-extra": "^0.8.1", "symfony/asset": "^4.4|^5.1", + "symfony/expression-language": "^4.4|^5.1", "symfony/framework-bundle": "^4.4|^5.1", "symfony/http-kernel": "^4.4|^5.1", "symfony/routing": "^4.4|^5.1", diff --git a/spec/Helper/PageHelperSpec.php b/spec/Helper/PageHelperSpec.php index e7063c2..488fadc 100644 --- a/spec/Helper/PageHelperSpec.php +++ b/spec/Helper/PageHelperSpec.php @@ -3,14 +3,17 @@ namespace spec\Mobizel\Bundle\MarkdownDocsBundle\Helper; use Mobizel\Bundle\MarkdownDocsBundle\Helper\PageHelper; -use Mobizel\Bundle\MarkdownDocsBundle\Template\TemplateHandlerInterface; use PhpSpec\ObjectBehavior; class PageHelperSpec extends ObjectBehavior { - function let(TemplateHandlerInterface $templateHandler): void + function let(): void { - $this->beConstructedWith($templateHandler); + $this->beConstructedWith([ + 'first-page' => 'First page', + 'foo' => 'Foo fighters', + 'last-page' => 'Last page', + ]); } function it_is_initializable(): void @@ -18,24 +21,20 @@ function it_is_initializable(): void $this->shouldHaveType(PageHelper::class); } - function it_can_get_page_title(TemplateHandlerInterface $templateHandler): void + function it_can_get_page_title(): void { - $templateHandler->getTemplateAbsolutePath('foo')->willReturn('tests/docs/foo.md'); - $this->getTitle('foo')->shouldReturn('Foo fighters'); } - function it_return_default_title_when_no_title_has_been_found(TemplateHandlerInterface $templateHandler): void + function it_can_get_previous_page(): void { - $templateHandler->getTemplateAbsolutePath('bar')->willReturn('tests/docs/bar.md'); - - $this->getTitle('bar')->shouldReturn('Bar'); + $this->getPreviousPage('foo')->shouldReturn('first-page'); + $this->getPreviousPage('first-page')->shouldReturn(null); } - function it_return_default_title_when_file_is_empty(TemplateHandlerInterface $templateHandler): void + function it_can_get_next_page(): void { - $templateHandler->getTemplateAbsolutePath('empty')->willReturn('tests/docs/empty.md'); - - $this->getTitle('empty')->shouldReturn('Empty'); + $this->getNextPage('foo')->shouldReturn('last-page'); + $this->getNextPage('last-page')->shouldReturn(null); } } diff --git a/src/Controller/PageAction.php b/src/Controller/PageAction.php index 5ed68bf..f1546c1 100644 --- a/src/Controller/PageAction.php +++ b/src/Controller/PageAction.php @@ -23,8 +23,9 @@ final class PageAction extends AbstractController /** @var PageItemDataProvider */ private $pageItemDataProvider; - public function __construct(PageItemDataProvider $pageItemDataProvider) - { + public function __construct( + PageItemDataProvider $pageItemDataProvider + ) { $this->pageItemDataProvider = $pageItemDataProvider; } diff --git a/src/DataProvider/PageCollectionDataProvider.php b/src/DataProvider/PageCollectionDataProvider.php index 4a9215c..c7b7020 100644 --- a/src/DataProvider/PageCollectionDataProvider.php +++ b/src/DataProvider/PageCollectionDataProvider.php @@ -29,6 +29,29 @@ public function __construct(string $docsDir) $this->docsDir = $docsDir; } + public function getPagesMap(): array + { + $finder = new Finder(); + + $finder + ->files() + ->in($this->docsDir) + //->depth(0) + ->sort(PageSorter::sortByTitle()); + $pages = []; + + foreach ($finder as $file) { + $pageInfo = new PageInfo($file->getPathname(), $file->getRelativePath(), $file->getRelativePathname()); + + /** @var string $slug */ + $slug = preg_replace('/\.md$/', '', $pageInfo->getRelativePathName()); + + $pages[$slug] = $pageInfo->getTitle(); + } + + return $pages; + } + public function getRootPages(): iterable { $finder = new Finder(); diff --git a/src/DataProvider/PageCollectionDataProviderInterface.php b/src/DataProvider/PageCollectionDataProviderInterface.php index 4087a18..a8e0b89 100644 --- a/src/DataProvider/PageCollectionDataProviderInterface.php +++ b/src/DataProvider/PageCollectionDataProviderInterface.php @@ -15,6 +15,8 @@ interface PageCollectionDataProviderInterface { + public function getPagesMap(): array; + public function getRootPages(): iterable; public function getChildrenPages(string $parentSlug): iterable; diff --git a/src/Helper/PageHelper.php b/src/Helper/PageHelper.php index 9397fb5..dae56b8 100644 --- a/src/Helper/PageHelper.php +++ b/src/Helper/PageHelper.php @@ -13,24 +13,51 @@ namespace Mobizel\Bundle\MarkdownDocsBundle\Helper; -use Mobizel\Bundle\MarkdownDocsBundle\Page\PageInfo; -use Mobizel\Bundle\MarkdownDocsBundle\Template\TemplateHandlerInterface; +use Webmozart\Assert\Assert; final class PageHelper implements PageHelperInterface { - /** @var TemplateHandlerInterface */ - private $templateHandler; + /** @var array */ + private $pagesMap; - public function __construct(TemplateHandlerInterface $templateHandler) + public function __construct(array $pagesMap) { - $this->templateHandler = $templateHandler; + $this->pagesMap = $pagesMap; } public function getTitle(string $slug): string { - $path = $this->templateHandler->getTemplateAbsolutePath($slug); - $page = new PageInfo($path, '', ''); + return $this->pagesMap[$slug] ?? ''; + } + + public function getPreviousPage(string $slug): ?string + { + $currentPosition = $this->getCurrentPosition($slug); + + return $this->getPageFromPosition($currentPosition - 1); + } + + public function getNextPage(string $slug): ?string + { + $currentPosition = $this->getCurrentPosition($slug); + + return $this->getPageFromPosition($currentPosition + 1); + } + + private function getCurrentPosition(string $slug): int + { + /** @var int $currentPosition */ + $currentPosition = array_search($slug, array_keys($this->pagesMap)); + Assert::notFalse($currentPosition, 'Current position was not found'); + + return $currentPosition; + } + + private function getPageFromPosition(int $position): ?string + { + /** @var string|null $slug */ + $slug = array_keys($this->pagesMap)[$position] ?? null; - return $page->getTitle(); + return $slug; } } diff --git a/src/Helper/PageHelperInterface.php b/src/Helper/PageHelperInterface.php index 0d368b1..2607471 100644 --- a/src/Helper/PageHelperInterface.php +++ b/src/Helper/PageHelperInterface.php @@ -16,4 +16,8 @@ interface PageHelperInterface { public function getTitle(string $slug): string; + + public function getPreviousPage(string $slug): ?string; + + public function getNextPage(string $slug): ?string; } diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index e4c106d..7739eff 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -3,7 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"> - @@ -17,7 +16,7 @@ - + service('mobizel_markdown_docs.data_provider.page_collection').getPagesMap() @@ -39,6 +38,10 @@ + + + + diff --git a/src/Twig/Extension/PaginationExtension.php b/src/Twig/Extension/PaginationExtension.php new file mode 100644 index 0000000..4d6a306 --- /dev/null +++ b/src/Twig/Extension/PaginationExtension.php @@ -0,0 +1,47 @@ +pageHelper = $pageHelper; + } + + public function getFunctions() + { + return [ + new TwigFunction('previous_page', [$this, 'previousPage']), + new TwigFunction('next_page', [$this, 'nextPage']), + ]; + } + + public function previousPage(string $slug): ?string + { + return $this->pageHelper->getPreviousPage($slug); + } + + public function nextPage(string $slug): ?string + { + return $this->pageHelper->getNextPage($slug); + } +} diff --git a/src/Twig/Extension/PaginationExtensionInterface.php b/src/Twig/Extension/PaginationExtensionInterface.php new file mode 100644 index 0000000..365188c --- /dev/null +++ b/src/Twig/Extension/PaginationExtensionInterface.php @@ -0,0 +1,21 @@ +