netglue / expressive-prismic
Module/Library for creating content driven websites with prismic.io and Zend Expressive
Installs: 479
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 0
Open Issues: 2
Type:module
Requires
- php: >=7.3
- ext-json: *
- dflydev/fig-cookies: ^1.0|^2.0
- laminas/laminas-diactoros: ^1.7.1 || ^2.0
- laminas/laminas-zendframework-bridge: ^1.2
- netglue/prismic-php-kit: ^4.2
- zendframework/zend-expressive: ^3.0.1
- zendframework/zend-expressive-helpers: ^5.0
- zendframework/zend-expressive-zendviewrenderer: ^2.0
- zendframework/zend-http: ^2.8
- zendframework/zend-paginator: ^2.8
Requires (Dev)
README
Introduction
This module/library's purpose is to ease development of content driven websites using prismic.io's content API as a backend service.
If you haven't heard of Prismic before, you can find out about it here.
Requirements
This module is only suitable for Zend Expressive ^3.0 and PHP ^7.1
Furthermore, it uses a fork of the official Prismic.io php library which you can see here: netglue/prismic-php-kit. This fork is quite different to the official kit and I recommend looking through the docs/code to make yourself aware of the differences if you are already familiar with the official lib.
Install
Install with composer ala composer require netglue/expressive-prismic
This should also ask you if you want to inject the config provider too.
Tests
$ composer install
$ vendor/bin/phpunit
Basic Configuration
This library exposes the Prismic API instance in your container as Prismic\Api
. At the very least, you'll need to
configure your credentials thus:
return [ 'prismic' => [ 'api' => [ 'token' => 'Permanent Access Token', 'url' => 'https://Repo-name.prismic.io/api', ], ], ];
Defining Routes
In order to allow you to specify properties of a document to look out for during routing, you must map the route parameter names you want to use to the prismic document/api equivalent. The defaults are:
'prismic' => [ 'route_params' => [ 'id' => 'prismic-id', 'bookmark' => 'prismic-bookmark', 'uid' => 'prismic-uid', 'type' => 'prismic-type', 'lang' => 'prismic-lang', ], ],
So, assuming the above, to define a route to a bookmarked document, you would configure something like this:
/** * @var \Zend\Expressive\Application $app * @var \Zend\Stratigility\MiddlewarePipeInterface $middlewarePipe */ $app->route('/', [$middlewarePipe], ['GET'], 'home') ->setOptions([ 'defaults' => [ 'template' => 'page::default', 'prismic-bookmark' => 'home', ], ]);
Normally, to save yourself some effort, you'd have a template that's capable of rendering perhaps any page of a given
type such as a 'case-study' type. Let's say you want the url /case-studies/{case-study-uid}
, then you'd define a
route like this (If you are using FastRoute):
$app->route('/case-studies/{prismic-uid}', [$middlewarePipe], ['GET'], 'case-studies') ->setOptions([ 'defaults' => [ 'template' => 'my:case-study', 'prismic-type' => 'case-study', ], ]);
Cache Busting Webhook
You will see in Factory\PipelineAndRoutesDelegator
that two routes are wired in by default, one of these is the
webhook to bust the cache. In order to use it, you should provide a shared secret that Prismic.io sends in
it's webhook payload to a local configuration file or Config Provider like this:
return [ 'prismic' => [ 'webhook_secret' => 'SomeSuperToughSharedSecret', ], ];
You would also need to enter this secret into the relevant place in your Prismic repository settings.
By default the secret is
null
, which means that the secret will not be validated even if it's sent by Prismic. If you decide not to setup a secret, I strongly recommend that you configure an obscure URL otherwise anyone will be able to POST to your webhook endpoint and bust the cache on demand, slowing down your site.
By default the url of the webhook will be /prismicio-cache-webhook
. You should change this to something sufficiently
random in the following way:
return [ 'prismic' => [ 'webhook_url' => '/send-prismic-webhooks-here', ], ];
If the webhook url is hit with a POST request and valid JSON payload, the pre-configured middleware will empty the cache attached to the Prismic API instance.
The webhook route points to a middleware pipe named ExpressivePrismic\Middleware\WebhookPipe
so if you want to modify
the pipeline to do other things, or replace it entirely, just alias that pipe to different factory or implement a
delegator factory for the pipe.
Link Resolver
The Link Resolver is a concept introduced by Prismic to turn documents, or document link fragments into local urls and
there's a concrete implementation in this package at ExpressivePrismic\LinkResolver
.
Using the same setup for routing parameters, it tries to use the Expressive URL helper to generate local URLs. It's
setup in the container as Prismic\LinkResolver
as well as ExpressivePrismic\LinkResolver
and throughout the package
it's retrieved by the name of Prismic\LinkResolver
so it's easy to replace with your own concrete implementation if
you need one.
Previews
There's another route that's auto-wired like the cache busting webhook for initiating previews. All you have to do is
add the URL in the settings on your Prismic repository and clicks on the preview button in the writing room will put
the site in preview mode. You can see how this is configured in Factory\PipelineAndRoutesDelegator
- the URL is
/prismic-preview
by default, but you can change the url by setting the following configuration value:
return [ 'prismic' => [ 'preview_url' => '/some-other-endpoint', ], ];
In 4.2.0, the middleware responsible for initiating a preview session was altered to catch an exception that is thrown when the preview token has expired and subsequently return a simple html response with a descriptive error. Previously, in this situation, an uncaught exception would have occurred.
View Helpers
URL Helper $this->prismicUrl()
This view helper will generate a local URL using the link resolver. It's __invoke()
method accepts
- string - Treated as a Document ID
- \Prismic\Document
- \Prismic\Document\Fragment\LinkInterface
Fragment Helper $this->fragment()
This view helper operates on the current resolved document and provides an easy way of rendering simple fragments to
views. It does not require the fully qualified fragment name, ie. documentType.fragmentName
and instead you can pass
it just 'fragmentName'
.
$this->fragment()->get('title');
will return the fragment object.
$this->fragment()->asText('title');
will return the text value of the fragment.
$this->fragment()->asHtml('title');
will return the HTML value of the fragment.
CMS Managed Error Pages for Production
Error handling is wired in by default… If you are using the Whoops error handler in development, you'll need to
disable development mode composer development-disable
to experience custom errors and 404's
404 Errors
In the event of a 404, by default, Expressive will execute the \Zend\Expressive\Handler\NotFoundHandler
. This module
provides a pipeline in \ExpressivePrismic\Middleware\NotFoundPipe
that initialises previews and experiments, locates
a bookmarked error document in the Prismic API and renders that document to a template.
To take advantage of pretty CMS managed 404's, first you will have to specify in your configuration the bookmark name for the error document in your repository and the template name to render like this:
return [ 'prismic' => [ 'error_handler' => [ 'template_404' => 'some::template-name', 'bookmark_404' => 'some-bookmark', ], ], ];
Note that in development mode, 404's experienced after a route has matched, i.e. the Prismic Document can't be found,
will cause a DocumentNotFound
exception, thereby delegating to the Whoops error handler.
Exceptions
Presenting a pretty error page during errors and exceptions are handled in much the same way as 404's. Again, you'll need to configure a bookmark and a template name used to render the content.
return [ 'prismic' => [ 'error_handler' => [ 'template_error' => 'some::template-name', 'bookmark_error' => 'some-bookmark', ], ], ];
The fallback (i.e. when the error document cannot be retrieved from the api) for exception situations is a simple plain text message stating that an error occurred. This fallback is not currently configurable to be anything more fancy than that.