How can I minify HTML with Twig? - twig

I'm using Twig and I'd like to be able to minify the HTML output. How do I do this? I tried {% spaceless %}, but that requires adding that to all my templates. Can I add minification within the Twig engine?

This may help you little.
use html-compress-twig you can compress html,css,js in one package.use composer to install composer require nochso/html-compress-twig and you have to add Extension with twig by using this code.
$app->extend('twig_theme', function($twig_theme, $ojt) {
$twig_theme->addExtension(new nochso\HtmlCompressTwig\Extension());
return $ojt_theme;});
finally go to your template file add this code.
{% htmlcompress %} ....your coding... {% endhtmlcompress %}
{{ htmlcompress('<ul> <li>') }}
{{ '<ul> <li>'|htmlcompress }}

For example you have the BaseController in your src/Controller directory.
You should create BaseController
Extends it from Controller
Override render method of the Controller class
And use this method in every controller
class BaseController extends Controller {
protected function render($view, array $parameters = array(), Response $response = null)
{
if ($this->container->has('templating')) {
$content = $this->container->get('templating')->render($view, $parameters);
} elseif ($this->container->has('twig')) {
$content = $this->container->get('twig')->render($view, $parameters);
} else {
throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".');
}
if (null === $response) {
$response = new Response();
}
$content = preg_replace(array('/<!--(.*)-->/Uis',"/[[:blank:]]+/"),array('',' '),str_replace(array("\n","\r","\t"),'',$content));
$response->setContent($content);
return $response;
}
}
You also can extends BaseController in others controllers.

Use a Listener on kernel.response event to automatize the process:
In config/services.yaml:
services:
// ...
app.listener.compression:
class: App\Event\CompressionSubscriber
arguments:
tags:
- { name: kernel.event_subscriber }
In src/Event/CompressionSubscriber.php:
<?php
namespace App\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class CompressionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => ['onKernelResponse', -256]
];
}
public function onKernelResponse($event)
{
if ($event->getRequestType() != HttpKernelInterface::MAIN_REQUEST) {
return;
}
$response = $event->getResponse();
$content = preg_replace(
['/<!--(.*)-->/Uis',"/[[:blank:]]+/"],
['',' '],
str_replace(["\n","\r","\t"], '', $response->getContent())
);
$response->setContent($content);
}
}
Based on this post

Use
{% spaceless %}
YOUR WHOLE PAGE GOES HERE HTML, TWIG, JS EVERYTHING...
{% endspaceless %}
It can be that your twig version does not recognize the tags, just update the latest version of twig.
This will minify the output html generated and page load will go up because it only loads the compiled version of html.
While you can still view the code in a readable situation.

Related

Get subcategories with twig on listing page

I'm trying to get the subcategories on a listing page in Shopware 6. However i can't seem to find the functionality to get an array with the subcategories with the template variables.
My goal is to loop over the children and make some kind of fastlinks in a CMS-element.
Is there a standard functionality build in shopware to get the children by id or name in TWIG?
I've tried to find anything relevant in
page.header.navigation.active
But the child data isn't available.
Thanks!
I don't think there is a build-in function to fetch this, but if you're doing it in a new CMS-Element you can take advantage of it by adding a new DataResolver for your new element and pass the subcategories to your CMS-element.
// myPlugin/src/DataResolver/SubcategoryListCmsElementResolver.php
<?php
namespace MyPlugin\DataResolver;
use Shopware\Core\Content\Category\CategoryDefinition;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
use Shopware\Core\Content\Cms\DataResolver\CriteriaCollection;
use Shopware\Core\Content\Cms\DataResolver\Element\AbstractCmsElementResolver;
use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection;
use Shopware\Core\Content\Cms\DataResolver\ResolverContext\ResolverContext;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
class SubcategoryListCmsElementResolver extends AbstractCmsElementResolver
{
public function getType(): string
{
return 'my-subcategory-list';
}
public function collect(CmsSlotEntity $slot, ResolverContext $resolverContext): ?CriteriaCollection
{
/** #var CategoryEntity $categoryEntity */
$categoryEntity = $resolverContext->getEntity();
$criteria = new Criteria([$categoryEntity->getId()]);
$criteria->addAssociation('children');
$criteriaCollection = new CriteriaCollection();
$criteriaCollection->add('category_' . $slot->getUniqueIdentifier(), CategoryDefinition::class, $criteria);
return $criteriaCollection;
}
public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void
{
/** #var CategoryEntity $categoryEntity */
$categoryEntity = $result->get('category_' . $slot->getUniqueIdentifier())?->getEntities()->first();
$slot->setData($categoryEntity->getChildren()?->sortByPosition()->filter(static function ($child) {
/** #var CategoryEntity $child */
return $child->getActive();
}));
}
}
services.xml
<service id="MyPlugin\DataResolver\SubcategoryListCmsElementResolver">
<tag name="shopware.cms.data_resolver"/>
</service>
You then provide a new template, e.g.
{% block element_my_subcategory_list %}
{% set subcategories = element.data.elements %}
{% set activeCategory = page.header.navigation.active %}
<ul>
{% for category in subcategories %}
<li>do something with your category</li>
{% endfor %}
</ul>
{% endblock %}
You can read more about data resolvers here in the docs: https://developer.shopware.com/docs/guides/plugins/plugins/content/cms/add-data-to-cms-elements#create-a-data-resolver
Not sure if I understood your problem correctly, but let's explain. If you need to fetch array inside array, you can create a loop inside a new loop something like that:
{% for item in category.item %}
{% for subcategory in category.subcategory %}
{{ subcategory.title}}
{% endfor %}
{% endfor %}

Grav - Pass JSON data from Plugin to Twig

I want to retrieve a JSON from a webservice and incorporate it into a Twig template.
I went through the docs and I found that I could use this option.
I have followed the steps from the doc and I have prepared this plugin:
/var/www/html/grav/user/plugins/category# ls
category.php category.yaml twig
/var/www/html/grav/user/plugins/category# cat category.yaml
enabled: true
/var/www/html/grav/user/plugins/category# cat category.php
<?php
namespace Grav\Plugin;
use \Grav\Common\Plugin;
class CategoryPlugin extends Plugin
{
public static function getSubscribedEvents()
{
return [
'onTwigExtensions' => ['onTwigExtensions', 0]
];
}
public function onTwigExtensions()
{
require_once(__DIR__ . '/twig/CategoryTwigExtension.php');
$this->grav['twig']->twig->addExtension(new CategoryTwigExtension());
}
}
/var/www/html/grav/user/plugins/category# cat twig/CategoryTwigExtension.php
<?php
namespace Grav\Plugin;
class CategoryTwigExtension extends \Twig_Extension
{
public function getName()
{
return 'CategoryTwigExtension';
}
public function getFunctions()
{
return [
new \Twig_SimpleFunction('get_child_category', [$this, 'getChildCategoryFunction'])
];
}
public function getChildCategoryFunction()
{
$json = file_get_contents('http://localhost:8888/get_child_category/2/es_ES');
$obj = json_decode($json);
return $json;
}
}
I then incorporate the following function invocation in the Twig template:
{{ get_child_category() }}
But:
I can get $json string, but how can I pass the whole JSON data and retrieve individually the fields?
In my case if I use:
<span>{{ get_child_category() }}</span>
in Twig I get the following string:
[{"id": 11, "name": "Racoons"}, {"id": 10, "name": "Cats"}]
How would I access individual records in Twig, including iteration over the JSON array and individual field extraction (id, name) for each record?
Your function is returning an array. You need to iterate through it. Here is an example from the Grav docs.
<ul>
{% for cookie in cookies %}
<li>{{ cookie.flavor }}</li>
{% endfor %}
</ul>
A simple list of names from your example is a simple edit.
<ul>
{% for child in get_child_category() %}
<li>{{ child.name }}</li>
{% endfor %}
</ul>

Extends template in Twig with Twig_Loader_String

I have to render a template with Twig_Loader_String and I need to extend a template, like below:
$body='{% extends "/views/path/to/my/template" %}
{% block body %} Hello {% endblock %}';
And in PHPs side I wrote:
$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader);
$twig->render($body,array());
I don't understand why the result after render is just:
/views/path/to/my/template
You only have a string loader, that very string is your actual template. You're extending it, and overriding the body block - but it has no body block.
Expanding the answer by #Maerlyn with some code how to do it:
$loaders = [
new Twig_Loader_Filesystem('path/to/twig/views'),
new Twig_Loader_String()
];
$loader = new Twig_Loader_Chain($loaders);
$twig = new Twig_Environment($loader);

Only Display a # of Characters in Twig

I am very unfamiliar with twig. Here's what I have:
{% if wp.get_post_meta(post.ID, '_property_website').0 %}
<tr>
<th>{{ wp.__('Website', 'aviators') }}:</th>
<td>{{ wp.get_post_meta(post.ID, '_property_website').0 }}
</td>
</tr>
{% endif %}
I need to restrict this output to 35 characters without killing the link. It needs to still be active but only display 35 characters, plus ideally it would end with... to designate that the url is cut off but that's a bonus. Can anyone help?
I believe http://twig.sensiolabs.org/doc/filters/slice.html is what you are looking for
EDIT
Just found that Twig has an extension called text it includes the wordwrap filter that is exactly what you're looking for
Link: https://github.com/fabpot/Twig-extensions/blob/master/lib/Twig/Extensions/Extension/Text.php
You can make your own Twig Extension. It's very easy.
First you must create the file with the filter code:
<?php
//Acme/AcmeBundle/Twig/AnExtension.php
namespace Acme\AcmeBundle\Twig;
class AnExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('cutText', array($this, 'cutTextFilter'))
);
}
public function cutTextFilter($text, $size = 50)
{
if (strlen($text) > $size)
{
return substr($text, 0, $size) . '...';
}
else
{
return $text;
}
}
public function getName()
{
return 'an_extension';
}
}
Then edit the services.yml file from this bundle, located at: /Acme/AcmeBundle/Resources/config/services.yml and add:
services:
acme.twig.an_extension:
class: Acme\AcmeBundle\Twig\AnExtension
tags:
- { name: twig.extension }
and now you can use the filter in your code:
<a href="http://{{ wp.get_post_meta(post.ID, '_property_website').0 }}">
{{ wp.get_post_meta(post.ID, '_property_website').0 | cutText(30) }}
</a>
More info: http://symfony.com/doc/current/cookbook/templating/twig_extension.html

How to prevent acces to constants in twig?

There is some security reasons and I want to prevent access to class constants in twig. How can I do it?
Note: It is possible to access constants with code below.
{{ constant('Entity\\Demo::MY_CONSTANT') }}
sandbox is not the cure. Because in the system the user writes his own template, this brings security issues.
For that reason function overriding in extension may be a good solution.
$environment = new Twig_Environment($loader, array('autoescape' => self::$_autoEscapeOpened));
$myTwigExtension = new MyTwigExtension();
//note that MyTwigExtension extends Twig_Extension
$environment->addExtension($myTwigExtension);
//here is MyTwigExtension
class MyTwigExtension extends \Twig_Extension {
//in my twig extension, there is a method called getFunctions. If not, write one.
public function getFunctions() {
$functions = array(
new \Twig_SimpleFunction('constant', array($this, 'constant')),
);
return $functions;
}
//and add the customized constant function in your extension here!
public function constant($variable){
return '';
}
}
if you don't want to use extension, see http://twig.sensiolabs.org/doc/advanced.html#functions
Ant the result is nice, there is no output in the screen, without any sandbox use. (solution is on backend)
Hope this helps.
I believe you can do this with the Sandbox extension:
http://twig.sensiolabs.org/doc/api.html#sandbox-extension
This extension allows you to define a security policy which basically has a whitelist of functions, tags, filters...
You can enable sandbox mode globally, or just use sandbox mode for a specific include (default behavior):
{% sandbox %}
{% include 'user.html' %}
{% endsandbox %}

Resources