Add custom pagelet

Overview

In this guide you'll learn how to create custom pagelets for your Storefront pages.
In short: Pages are exactly that, a fully functioning page of your store with a template loaded by a route. A pagelet is an important and reusable fraction of several pages, such as a footer or the navigation.

Prerequisites

In order to add your own custom pagelet for your plugin, you first need a plugin as base. Therefore, you can refer to the Plugin Base Guide. Since a pagelet is just part of another page, we're going to use the page created in our guide about adding a custom page.

Adding custom pagelet

Bssically a pagelet is created exactly like a page: You need a pagelet loader, a pagelet struct to hold the data and a pagelet loaded event.
Since creating this kind of classes is explained in detail in our guide about adding a custom page, it is not going to be explained here in detail again. Yet, there's some differences worth mentioning:
  • The struct to hold the data has to extend from the Shopware\Storefront\Pagelet\Pagelet class instead of Shopware\Storefront\Page\Page
  • A pagelet doesn't have to be bound to a controller, e.g. with an extra route. It can have a route though!
  • A pagelet is mostly loaded by another page or multiple pages, that's their purpose
  • The GenericPageLoaderInterface is not used, since it is responsible to load the footer or header pagelet. You don't want to load
    a pagelet (footer or header) into your pagelet
  • The pagelet instance is not created via Pagelet::createFrom(), but rather you just create a new instance yourself. That's because the
    Pagelet::createFrom() was only necessary to create a new instance of your page, which already contains the footer & header pagelets.
    Once again: You don't want that in your pagelet.
  • The pagelet loaded event class extends from Shopware\Storefront\Pagelet\PageletLoadedEvent instead of Shopware\Storefront\Page\PageLoadedEvent
Let's now have a look at the example classes. The pagelet is going to be called ExamplePagelet in the following examples.

The ExamplePageletLoader

<plugin root>/src/Storefront/Pagelet/Example/ExamplePageletLoader.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Storefront\Pagelet\Example;
4
5
use Shopware\Core\System\SalesChannel\SalesChannelContext;
6
use Shopware\Storefront\Page\GenericPageLoaderInterface;
7
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
8
use Symfony\Component\HttpFoundation\Request;
9
10
class ExamplePageletLoader
11
{
12
private EventDispatcherInterface $eventDispatcher;
13
14
public function __construct(EventDispatcherInterface $eventDispatcher)
15
{
16
$this->eventDispatcher = $eventDispatcher;
17
}
18
19
public function load(Request $request, SalesChannelContext $context): ExamplePagelet
20
{
21
$pagelet = new ExamplePagelet();
22
23
// Do additional stuff, e.g. load more data from repositories and add it to page
24
$pagelet->setExampleData(...);
25
26
$this->eventDispatcher->dispatch(
27
new ExamplePageletLoadedEvent($pagelet, $context, $request)
28
);
29
30
return $pagelet;
31
}
32
}
Copied!
Note the instance creation without the ::createFrom() call. The rest is quite equal, you can load your data, set it to the pagelet struct, you fire an event and you return the pagelet.

The ExamplePagelet struct

<plugin root>/src/Storefront/Pagelet/Example/ExamplePagelet.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Storefront\Pagelet\Example;
4
5
use Shopware\Storefront\Pagelet\Pagelet;
6
use Swag\BasicExample\Core\Content\Example\ExampleEntity;
7
8
class ExamplePagelet extends Pagelet
9
{
10
protected ExampleEntity $exampleData;
11
12
public function getExampleData(): ExampleEntity
13
{
14
return $this->exampleData;
15
}
16
17
public function setExampleData(ExampleEntity $exampleData): void
18
{
19
$this->exampleData = $exampleData;
20
}
21
}
Copied!
Just like the page struct, this is basically just a class holding data. Note the different extend though, you're not extending from Shopware\Storefront\Page\Page here. It only contained helper method for the header & footer pagelets.

The ExamplePageletLoadedEvent

<plugin root>/src/Storefront/Pagelet/Example/ExamplePageletLoadedEvent.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Storefront\Pagelet\Example;
4
5
use Shopware\Core\System\SalesChannel\SalesChannelContext;
6
use Shopware\Storefront\Pagelet\PageletLoadedEvent;
7
use Symfony\Component\HttpFoundation\Request;
8
9
class ExamplePageletLoadedEvent extends PageletLoadedEvent
10
{
11
protected ExamplePagelet $pagelet;
12
13
public function __construct(ExamplePagelet $pagelet, SalesChannelContext $salesChannelContext, Request $request)
14
{
15
$this->pagelet = $pagelet;
16
parent::__construct($salesChannelContext, $request);
17
}
18
19
public function getPagelet(): ExamplePagelet
20
{
21
return $this->pagelet;
22
}
23
}
Copied!
Note the different extends, which uses the PageletLoadedEvent class instead. Also, the getter method is no longer getPage, but getPagelet instead.

Loading the pagelet

Loading the pagelet via another page

Most times you want to load your pagelet as part of another page. This is simply done by calling the load method of your pagelet in another page's load method.
Using the example from our adding a custom page guide, this is what the load method could look like:
<plugin root>/src/Storefront/Page/Example/ExamplePageLoader.php
1
public function load(Request $request, SalesChannelContext $context): ExamplePage
2
{
3
$page = $this->genericPageLoader->load($request, $context);
4
$page = ExamplePage::createFrom($page);
5
6
$page->setExamplePagelet($this->examplePageletLoader->load($request, $context));
7
8
// Do additional stuff, e.g. load more data from repositories and add it to page
9
$page->setExampleData(...);
10
11
$this->eventDispatcher->dispatch(
12
new ExamplePageletLoadedEvent($page, $context, $request)
13
);
14
15
return $page;
16
}
Copied!
Of course, in this example your ExamplePage struct needs a method setExamplePagelet, as well as the respective getter method getExamplePagelet. And then that's it, you've loaded your pagelet as part of another page.

Loading the pagelet via route

As already mentioned, a pagelet can be loaded via a route if you want it to. For that case, you can simply add a new route to your controller and load the pagelet via the ExamplePageletLoader:
1
/**
2
* @Route("/example-pagelet", name="frontend.example.pagelet", methods={"POST"}, defaults={"XmlHttpRequest"=true})
3
*/
4
public function examplePagelet(Request $request, SalesChannelContext $context): Response
5
{
6
$pagelet = $this->examplePageletLoader->load($request, $context);
7
8
return $this->renderStorefront('@Storefront/storefront/pagelet/example/index.html.twig', [
9
'pagelet' => $pagelet
10
]);
11
}
Copied!
Using the part defaults={"XmlHttpRequest"=true} in the annotation ensures, that this pagelet can be loaded using an XML HTTP Request.