Skip to content

Instantly share code, notes, and snippets.

@dbu
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dbu/67c838a2002ff667eacb to your computer and use it in GitHub Desktop.
Save dbu/67c838a2002ff667eacb to your computer and use it in GitHub Desktop.
CMF FOSHttpCache integration
<?php
namespace Symfony\Cmf\CoreBundle\Listener;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Symfony\Cmf\CoreBundle\Cache\CmsInvalidator;
/**
* Doctrine listener to invalidate cached urls on changes.
*/
class CacheInvalidationSubscriber implements EventSubscriber
{
/**
* @var CmsInvalidator
*/
private $invalidator;
public function __construct(CmsInvalidator $invalidator)
{
$this->invalidator = $invalidator;
}
public function getSubscribedEvents()
{
return array(
'postCreate',
'postUpdate',
);
}
/**
* We care about newly created objects as they could have an impact on their parents.
*
* @param LifecycleEventArgs $e
*/
public function postCreate(LifecycleEventArgs $e)
{
$this->invalidator->invalidateObject($e->getObjectManager(), $e->getObject());
}
public function postUpdate(LifecycleEventArgs $e)
{
$this->invalidator->invalidateObject($e->getObjectManager(), $e->getObject());
}
}
<?php
namespace Symfony\Cmf\CoreBundle\Command;
use Doctrine\Common\Util\ClassUtils;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to trigger cache invalidation because of publish workflow.
*/
class CheckCacheCommand extends ContainerAwareCommand
{
public function configure()
{
$this
->setName('cmf:check-cache')
->setDescription('Invalidate cached pages that changed their published status')
->addOption('since', null, InputOption::VALUE_OPTIONAL, 'Timestamp for content to invalidate, any php date string is accepted', '1 day ago')
;
}
public function execute(InputInterface $input, OutputInterface $output)
{
$timestamp = new \DateTime($input->getOption('since'));
$registry = $this->getContainer()->get('doctrine_phpcr');
$invalidator = $this->getContainer()->get('cmf_core.cache.cms_invalidator');
$changeFinder = $this->getContainer()->get('cmf_core.publish_workflow.change_finder');
$changedDocuments = $changeFinder->findChangedDocuments($timestamp);
foreach ($changedDocuments as $document) {
$dm = $registry->getManagerForClass(ClassUtils::getClass($document));
$invalidator->invalidateObject($dm, $document);
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln(sprintf('Invalidating %s', $dm->getUnitOfWork()->getDocumentId($document)));
}
}
}
}
<?php
namespace Symfony\Cmf\Bundle\CoreBundle\Cache;
use Doctrine\ODM\PHPCR\DocumentManager;
use FOS\HttpCacheBundle\CacheManager;
use Liipch\CoreBundle\Document\HeaderContainer;
use Liipch\NewsBundle\Document\NewsItem;
use PHPCR\Util\PathHelper;
use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNodeBase;
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route;
use Symfony\Cmf\Component\Routing\RouteReferrersReadInterface;
/**
* Handle cache invalidation for objects.
*/
class CmsInvalidator
{
/**
* @var CacheManager
*/
private $cm;
public function __construct(CacheManager $cm)
{
$this->cm = $cm;
}
/**
* @param DocumentManager $dm The document manager responsible for this object.
* @param object $o A managed object to invalidate with varnish.
*/
public function invalidateObject(DocumentManager $dm, $o)
{
switch ($o) {
case $o instanceof Route:
$this->cm->refreshRoute($o);
break;
case $o instanceof RouteReferrersReadInterface:
foreach ($o->getRoutes() as $route) {
$this->cm->invalidateRoute($route);
}
// this is specific to my project, but left in here to show a possible usecase
if ($o instanceof NewsItem) {
foreach (array('de', 'fr', 'en') as $lang) {
$this->cm->invalidateRoute("/cms/routes/$lang/news");
}
$this->refreshHome();
}
break;
case $o instanceof MenuNodeBase:
$this->cm->invalidateRegex('.*');
break;
// this is specific to my project, but left in here to show a possible usecase
case $o instanceof HeaderContainer:
$this->cm->invalidateRegex('.*');
break;
// should use ESI for headers
default:
$this->invalidateParent($dm, $o);
break;
}
}
private function invalidateParent(DocumentManager $dm, $o)
{
$parentPath = PathHelper::getParentPath($dm->getUnitOfWork()->getDocumentId($o));
if ('/' === $parentPath) {
return;
}
$this->invalidateObject($dm, $dm->find(null, $parentPath));
}
private function refreshHome()
{
foreach (array('de', 'fr', 'en') as $lang) {
$this->cm->invalidateRoute("/cms/routes/$lang");
}
}
}
# needs to be loaded only when enabled, and rewritten to xml configuration
services:
cmf_core.cache.cms_invalidator:
class: Symfony\Cmf\CoreBundle\Cache\CmsInvalidator
arguments:
- @fos_http_cache.cache_manager
cmf_core.publish_workflow.change_finder:
class: Symfony\Cmf\CoreBundle\Cache\PublishWorkflowChangeFinder
arguments:
- @doctrine_phpcr.odm.document_manager
cmf_core.listener.cache_invalidation:
class: Symfony\Cmf\CoreBundle\Listener\CacheInvalidationSubscriber
arguments:
- @cmf_core.cache.cms_invalidator
tags:
- { name:"doctrine_phpcr.event_subscriber" }
<?php
namespace Symfony\Cmf\CoreBundle\Cache;
use Doctrine\ODM\PHPCR\DocumentManager;
use PHPCR\Query\QOM\QueryObjectModelConstantsInterface;
/**
* A helper service to query phpcr-odm for all documents that changed their
* publish status since a specified moment.
*/
class PublishWorkflowChangeFinder
{
/**
* @var DocumentManager
*/
private $dm;
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
}
/**
* Find all documents that changed their publication status due to the date
* based publish workflow fields.
*
* This method does *not* look at other recent changes like a changed
* publishable field. Use a doctrine listener to see active changes to
* documents.
*
* @param \DateTime $timestamp
*
* @return array The collection of all documents that changed their
* publication status since $timestamp
*/
public function findChangedDocuments(\DateTime $timestamp)
{
$queryBuilder = $this->dm->createPhpcrQueryBuilder();
$qomf = $queryBuilder->qomf();
$queryBuilder
->from($qomf->selector('u', 'nt:unstructured'))
->orWhere(
$qomf->andConstraint(
$qomf->comparison(
$qomf->propertyValue('u', 'publishStartDate'),
QueryObjectModelConstantsInterface::JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO,
$qomf->literal($timestamp)
),
$qomf->comparison(
$qomf->propertyValue('u', 'publishStartDate'),
QueryObjectModelConstantsInterface::JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO,
$qomf->literal(new \DateTime)
)
)
)
->orWhere(
$qomf->andConstraint(
$qomf->comparison(
$qomf->propertyValue('u', 'publishEndDate'),
QueryObjectModelConstantsInterface::JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO,
$qomf->literal($timestamp)
),
$qomf->comparison(
$qomf->propertyValue('u', 'publishEndDate'),
QueryObjectModelConstantsInterface::JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO,
$qomf->literal(new \DateTime)
)
)
)
;
return $this->dm->getDocumentsByPhpcrQuery($queryBuilder->getQuery());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment