I've solved it myself by extending the default framework router.
# src/Your/Bundle/Resources/config/service.yml
parameters:
router.class: Your\Bundle\Routing\PreviewRouter
<?php
// src/Your/Bundle/Routing/PreviewRouter.php
namespace Your\Bundle\Routing;
use Symfony\Bundle\FrameworkBundle\Routing\Router as BaseRouter;
/**
* extends the base router's generate function to always append
* the preview query param on all generated urls
*/
class PreviewRouter extends BaseRouter
{
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
{
// parent router generates url
$url = parent::generate($name, $parameters, $referenceType);
// check for existing preview query string
parse_str($this->getContext()->getQueryString(), $contextQueryParams);
if(isset($contextQueryParams['preview']) && filter_var($contextQueryParams['preview'], FILTER_VALIDATE_BOOLEAN))
{
// put possible query string params into $queryParams array
$urlParts = parse_url($url);
parse_str(isset($urlParts['query']) ? $urlParts['query'] : '', $urlQueryParams);
// strip everything after '?' from generated url
$url = preg_replace('/\?.*$/', '', $url);
// append merged query string to generated url
$url .= '?'.http_build_query(array_merge(
array('preview' => $contextQueryParams['preview']),
$urlQueryParams
));
}
// remove preview param if set to false deliberately
$url = preg_replace('/(\?|&)preview=(false|0|off)/', '', $url);
return $url;
}
}