Skip to content

Commit

Permalink
Generate sitemaps from CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
stefandoorn committed Jul 29, 2019
1 parent 0c4b841 commit 0f957d5
Show file tree
Hide file tree
Showing 33 changed files with 364 additions and 181 deletions.
6 changes: 6 additions & 0 deletions UPGRADE-2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@
## TL-DR

* Plugin structure upgraded to PluginSkeleton:^1.3
* Removed the `all.xml` endpoint - use the sitemap index
* Sitemaps are now generated via the command line, see below.
* Dropped support for relative URL's
* Models (& their interfaces) renamed
* Drop suggestion that other formats than XML were supported

## New features

* Generation of sitemaps is done via the CLI, schedule them in a cronjob:
* Sitemap Index: `bin/console sylius:sitemap:generate-index`
* Sitemap URLs now support adding images. The default providers do this where possible. It can be disabled using the `images` configuration key.

## Removed features

* Dropped support for relative URL's; Google advises to [use fully qualified URL's](https://support.google.com/webmasters/answer/183668?hl=en).
* Unintentionally the plugin could suggest that other formats than XML were allowed. This was never properly supported and therefore removed.
* Removed the `all.xml` endpoint, which put all URL's in a single file. It's better to use the index file.

## Config changes

Expand All @@ -34,3 +39,4 @@
* `hasImage(SitemapImageUrlInterface $image): bool`
* `removeImage(SitemapImageUrlInterface $image): void`
* `public function hasImages(): bool`
* Providers now need to have a ChannelContext supplied.
13 changes: 6 additions & 7 deletions spec/Builder/SitemapBuilderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use SitemapPlugin\Model\SitemapInterface;
use SitemapPlugin\Model\UrlInterface;
use SitemapPlugin\Provider\UrlProviderInterface;
use Sylius\Component\Core\Model\ChannelInterface;

final class SitemapBuilderSpec extends ObjectBehavior
{
Expand All @@ -32,19 +33,17 @@ function it_implements_sitemap_builder_interface(): void
function it_builds_sitemap(
$sitemapFactory,
UrlProviderInterface $productUrlProvider,
UrlProviderInterface $staticUrlProvider,
SitemapInterface $sitemap,
UrlInterface $bookUrl,
UrlInterface $homePage
ChannelInterface $channel
): void {
$sitemapFactory->createNew()->willReturn($sitemap);
$this->addProvider($productUrlProvider);
$this->addProvider($staticUrlProvider);
$productUrlProvider->generate()->willReturn([$bookUrl]);
$staticUrlProvider->generate()->willReturn([$homePage]);

$sitemap->setUrls([$bookUrl, $homePage])->shouldBeCalled();
$productUrlProvider->generate($channel)->willReturn([$bookUrl]);

$this->build()->shouldReturn($sitemap);
$sitemap->setUrls([$bookUrl])->shouldBeCalled();

$this->build($productUrlProvider, $channel)->shouldReturn($sitemap);
}
}
12 changes: 3 additions & 9 deletions spec/Provider/ProductUrlProviderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use SitemapPlugin\Provider\ProductUrlProvider;
use SitemapPlugin\Provider\UrlProviderInterface;
use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductRepository;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductImageInterface;
use Sylius\Component\Core\Model\ProductInterface;
Expand All @@ -35,10 +34,9 @@ function let(
UrlFactoryInterface $urlFactory,
AlternativeUrlFactoryInterface $alternativeUrlFactory,
LocaleContextInterface $localeContext,
ChannelContextInterface $channelContext,
ProductImagesToSitemapImagesCollectionGeneratorInterface $productToImageSitemapArrayGenerator
): void {
$this->beConstructedWith($repository, $router, $urlFactory, $alternativeUrlFactory, $localeContext, $channelContext, $productToImageSitemapArrayGenerator);
$this->beConstructedWith($repository, $router, $urlFactory, $alternativeUrlFactory, $localeContext, $productToImageSitemapArrayGenerator);
}

function it_is_initializable(): void
Expand All @@ -57,7 +55,6 @@ function it_generates_urls_for_the_unique_channel_locale(
UrlFactoryInterface $urlFactory,
AlternativeUrlFactoryInterface $alternativeUrlFactory,
LocaleContextInterface $localeContext,
ChannelContextInterface $channelContext,
LocaleInterface $locale,
Collection $products,
\Iterator $iterator,
Expand All @@ -74,7 +71,6 @@ function it_generates_urls_for_the_unique_channel_locale(
): void {
$now = new \DateTime();

$channelContext->getChannel()->willReturn($channel);
$localeContext->getLocaleCode()->willReturn('en_US');

$locale->getCode()->willReturn('en_US');
Expand Down Expand Up @@ -139,7 +135,7 @@ function it_generates_urls_for_the_unique_channel_locale(

$url->addAlternative($alternativeUrl)->shouldNotBeCalled();

$this->generate();
$this->generate($channel);
}

function it_generates_urls_for_all_channel_locales(
Expand All @@ -148,7 +144,6 @@ function it_generates_urls_for_all_channel_locales(
UrlFactoryInterface $urlFactory,
AlternativeUrlFactoryInterface $alternativeUrlFactory,
LocaleContextInterface $localeContext,
ChannelContextInterface $channelContext,
LocaleInterface $enUSLocale,
LocaleInterface $nlNLLocale,
Collection $products,
Expand All @@ -166,7 +161,6 @@ function it_generates_urls_for_all_channel_locales(
): void {
$now = new \DateTime();

$channelContext->getChannel()->willReturn($channel);
$localeContext->getLocaleCode()->willReturn('en_US');

$enUSLocale->getCode()->willReturn('en_US');
Expand Down Expand Up @@ -238,6 +232,6 @@ function it_generates_urls_for_all_channel_locales(

$url->addAlternative($alternativeUrl)->shouldBeCalled();

$this->generate();
$this->generate($channel);
}
}
29 changes: 3 additions & 26 deletions src/Builder/SitemapBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SitemapPlugin\Factory\SitemapFactoryInterface;
use SitemapPlugin\Model\SitemapInterface;
use SitemapPlugin\Provider\UrlProviderInterface;
use Sylius\Component\Core\Model\ChannelInterface;

final class SitemapBuilder implements SitemapBuilderInterface
{
Expand All @@ -21,47 +22,23 @@ public function __construct(SitemapFactoryInterface $sitemapFactory)
$this->sitemapFactory = $sitemapFactory;
}

/**
* {@inheritdoc}
*/
public function addProvider(UrlProviderInterface $provider): void
{
$this->providers[] = $provider;
}

/**
* @return array
*/
public function getProviders(): iterable
{
return $this->providers;
}

/**
* {@inheritdoc}
*/
public function build(array $filter = []): SitemapInterface
public function build(UrlProviderInterface $provider, ChannelInterface $channel): SitemapInterface
{
$sitemap = $this->sitemapFactory->createNew();
$urls = [];

foreach ($this->filter($filter) as $provider) {
$urls[] = $provider->generate();
}
$urls[] = $provider->generate($channel);

$sitemap->setUrls(\array_merge(...$urls));

return $sitemap;
}

private function filter(array $filter): array
{
if (empty($filter)) {
return $this->providers;
}

return \array_filter($this->providers, function (UrlProviderInterface $provider) use ($filter) {
return \in_array($provider->getName(), $filter);
});
}
}
6 changes: 4 additions & 2 deletions src/Builder/SitemapBuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
namespace SitemapPlugin\Builder;

use SitemapPlugin\Model\SitemapInterface;
use SitemapPlugin\Provider\UrlProviderInterface;
use Sylius\Component\Core\Model\ChannelInterface;

interface SitemapBuilderInterface extends BuilderInterface
{
public function build(array $filter = []): SitemapInterface;
public function build(UrlProviderInterface $provider, ChannelInterface $channel): SitemapInterface;

/**
* @return array
* @return UrlProviderInterface[]
*/
public function getProviders(): iterable;
}
2 changes: 2 additions & 0 deletions src/Builder/SitemapIndexBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public function build(): SitemapInterface
$sitemap = $this->sitemapIndexFactory->createNew();
$urls = [];

/** @var IndexUrlProviderInterface $indexProvider */
foreach ($this->indexProviders as $indexProvider) {
/** @var UrlProviderInterface $provider */
foreach ($this->providers as $provider) {
$indexProvider->addProvider($provider);
}
Expand Down
116 changes: 116 additions & 0 deletions src/Command/GenerateSitemapCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

declare(strict_types=1);

namespace SitemapPlugin\Command;

use SitemapPlugin\Builder\SitemapBuilderInterface;
use SitemapPlugin\Builder\SitemapIndexBuilderInterface;
use SitemapPlugin\Filesystem\Writer;
use SitemapPlugin\Renderer\SitemapRendererInterface;
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class GenerateSitemapCommand extends Command
{
/** @var \SitemapPlugin\Builder\SitemapBuilderInterface */
private $sitemapBuilder;

/** @var SitemapIndexBuilderInterface */
private $sitemapIndexBuilder;

/** @var SitemapRendererInterface */
private $sitemapRenderer;

/** @var SitemapRendererInterface */
private $sitemapIndexRenderer;

/** @var Writer */
private $writer;

/** @var ChannelRepositoryInterface */
private $channelRepository;

public function __construct(
SitemapRendererInterface $sitemapRenderer,
SitemapRendererInterface $sitemapIndexRenderer,
SitemapBuilderInterface $sitemapBuilder,
SitemapIndexBuilderInterface $sitemapIndexBuilder,
Writer $writer,
ChannelRepositoryInterface $channelRepository
) {
$this->sitemapRenderer = $sitemapRenderer;
$this->sitemapIndexRenderer = $sitemapIndexRenderer;
$this->sitemapBuilder = $sitemapBuilder;
$this->sitemapIndexBuilder = $sitemapIndexBuilder;
$this->writer = $writer;
$this->channelRepository = $channelRepository;

parent::__construct('sylius:sitemap:generate');
}

protected function configure(): void
{
$this->addArgument('channel', InputArgument::IS_ARRAY, 'Channel codes to render. If none supplied, all channels will generated.');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
foreach ($this->channels($input) as $channel) {
$this->executeChannel($channel, $output);
}
}

private function executeChannel(ChannelInterface $channel, OutputInterface $output)
{
// TODO make sure providers are every time emptied (reset call or smth?)
foreach ($this->sitemapBuilder->getProviders() as $provider) {
$output->writeln(\sprintf('Start generating sitemap "%s" for channel "%s"', $provider->getName(), $channel->getCode()));

$sitemap = $this->sitemapBuilder->build($provider, $channel); // TODO use provider instance, not the name
$xml = $this->sitemapRenderer->render($sitemap);
$path = $path = $this->path($channel, \sprintf('%s.xml', $provider->getName()));

$this->writer->write(
$path,
$xml
);

$output->writeln(\sprintf('Finished generating sitemap "%s" for channel "%s" at path "%s"', $provider->getName(), $channel->getCode(), $path));
}

$output->writeln(\sprintf('Start generating sitemap index for channel "%s"', $channel->getCode()));

$sitemap = $this->sitemapIndexBuilder->build();
$xml = $this->sitemapIndexRenderer->render($sitemap);
$path = $this->path($channel, 'sitemap_index.xml');

$this->writer->write(
$path,
$xml
);

$output->writeln(\sprintf('Finished generating sitemap index for channel "%s" at path "%s"', $channel->getCode(), $path));
}

private function path(ChannelInterface $channel, string $path): string
{
return \sprintf('%s/%s', $channel->getCode(), $path);
}

/**
* @return ChannelInterface[]
*/
private function channels(InputInterface $input): iterable
{
if (!empty($input->getArgument('channel'))) {
return $this->channelRepository->findBy(['code' => $input->getArgument('channel'), 'enabled' => true]);
}

return $this->channelRepository->findBy(['enabled' => true]);
}
}
23 changes: 17 additions & 6 deletions src/Controller/AbstractController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@

namespace SitemapPlugin\Controller;

use SitemapPlugin\Model\SitemapInterface;
use SitemapPlugin\Renderer\SitemapRendererInterface;
use SitemapPlugin\Filesystem\Reader;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

abstract class AbstractController
{
/** @var SitemapRendererInterface */
protected $sitemapRenderer;
/** @var Reader */
protected $reader;

protected function createResponse(SitemapInterface $sitemap): Response
public function __construct(Reader $reader)
{
$response = new Response($this->sitemapRenderer->render($sitemap));
$this->reader = $reader;
}

protected function createResponse(string $path): Response
{
if (!$this->reader->has($path)) {
throw new NotFoundHttpException(\sprintf('File "%s" not found', $path));
}

$xml = $this->reader->get($path);

$response = new Response($xml);
$response->headers->set('Content-Type', 'application/xml');

return $response;
Expand Down
Loading

0 comments on commit 0f957d5

Please sign in to comment.