I'm adding a custom twig extension service to a Drupal 8 module. My services file looks like this:
services:
analytics.my_twig_extension:
class: Drupal\analytics\TwigExtension\MyTwigExtension
tags:
- { name: twig.extension }
I get this error when running drush cr:
[warning] Drush command terminated abnormally. Check for an exit()
in your Drupal site.
When I remove the tags property in services file, like this:
services:
analytics.my_twig_extension:
class: Drupal\analytics\TwigExtension\MyTwigExtension
then drush cr works correctly, but my Twig extension functions are not not running at all.
The MyTwigExtension class:
<?php
namespace Drupal\analytics\TwigExtension;
use Twig_Extension;
use Twig_SimpleFilter;
class MyTwigExtension extends \TwigExtension {
public function __construct() {
}
public function getFunctions() {
return [
new \Twig_SimpleFunction('get_type', array($this, 'getType'))
];
}
public function getType($var) {
return gettype($var);
}
}
?>
Does anyone have any idea why this is happening?
Jacob, you're a dummy.
I fixed it by simply using extends \Twig_Extension instead of extends \TwigExtension. A coworker found the answer. Unfortunately, there was no indication in the logs that this was the problem.
Related
I'm trying to override translator class in Symfony 5.2. I tried this:
# config/services.yaml
services:
# ....
App\Translator:
decorates: translator
and this (App\Translator implements TranslatorInterface):
# config/services.yaml
services:
# ....
App\Translator:
arguments:
$translator: '#translator'
Symfony\Contracts\Translation\TranslatorInterface: '#App\Translator'
both methods work well in PHP code, but in development mode in the twig, translator service is still DataCollectorTranslator. So in twig templates the translator service remains not overridden. How can I fix it?
It's possible I am not understanding the question. If something works in one mode but not another then sometimes just deleting the var/cache directory and building a new cache with bin/console cache:clear might work.
Decorating services can be a bit interesting sometimes. I created a fresh 5.2 project and then added:
# src/Translation/Translation.php
namespace App\Translation;
use JetBrains\PhpStorm\Pure;
use Symfony\Component\Translation\MessageCatalogueInterface;
use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Bundle\FrameworkBundle\Translation\Translator as BaseTranslator;
class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
{
// Uses PHP8 constructor promotion
public function __construct(private BaseTranslator $translator)
{
}
#[Pure]
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
//return $this->translator->trans($id,$parameters,$domain,$locale);
return strtoupper($id); // Verify calling this class
}
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
return $this->translator->getCatalogue($locale);
}
#[Pure]
public function getLocale(): string
{
return $this->translator->getLocale();
}
public function setLocale(string $locale)
{
$this->translator->setLocale($locale);
}
}
# config/services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
App\Translation\Translator:
decorates: translator
# index.html.twig
<li>{{ 'Hello' | trans }}</li>
You can disregard the Pure stuff as well as some of the PHP8 stuff. I was using this as a PHP8 test as well.
But it all seems to work as advertised.
I want to create a simple twig extension ({{imgWidth(...)}}) that calls getimagesize() and returns the width and height of an image on the server.
I followed the instuctions you can find here.
When I reload my page I only can see a blank page - the error.log tells me that
PHP Fatal error: Class 'Fms\MediaBundle\Twig\Extension\ImgsizeExtension' not found in /var/www/fms/app/cache/dev/appDevDebugProjectContainer.php on line 4773
The service in MediaBundle\Resources\config\services.yml looks like:
services:
twig.extension.imgsize:
class: Fms\MediaBundle\Twig\Extension\ImgsizeExtension
tags:
- name: twig.extension
The class is:
<?
// src/Fms/MediaBundle/Twig/Extension/ImgsizeExtension.php
namespace Fms\MediaBundle\Twig\Extension;
class ImgsizeExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('imgsize', array($this, 'imgWidth'))
);
}
public function imgWidth($mediaId = 0, $mediaSize = 'L')
{
// ...
return $mediaId;
}
public function getName()
{
return 'imgsize';
}
}
Clearing cache via console or manually didnt help too.
Change <? to <?php. I copied your code and in with this modification symfony finally finds this class.
what I am trying to do is to have custom error page, not only will they be extending the base layout but also I want extra up selling content in those pages too so changing templates only is not an option
regardless of the reason (404 Not Found or just missing variable) I would like to show my template and my content instead
I have spent hours trying to get this going with no luck
app/console --version
Symfony version 2.5.6 - app/dev/debug
I tried some resources, but couldn't get it working. The name a few:
http://symfony.com/doc/current/reference/configuration/twig.html
http://symfony.com/doc/current/cookbook/controller/error_pages.html
I'm running in dev with no debug, see app_dev.php below:
$kernel = new AppKernel('dev', false);
following the tutorials i got these extra bits
app/config/config.yml
twig:
exception_controller: SomethingAppBundle:Exception:show
in my bundle
<?php
namespace Something\AppBundle\Controller;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpKernel\Exception\FlattenException;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ExceptionController extends Controller
{
public function showAction( FlattenException $error, DebugLoggerInterface $debug)
{
print_r($error);
}
}
but my error controller does not get executed,
I am on purpose causing error by trying to echo undefined variable in different controller, since it should handle error from entire application
At the beginning you need to create action in the controller:
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ErrorController extends Controller
{
public function notFoundAction()
{
return $this->render('error/404.html.twig');
}
}
Then you need to create a Listener:
<?php
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class NotFoundHttpExceptionListener
{
private $controller_resolver;
private $request_stack;
private $http_kernel;
public function __construct($controller_resolver, $request_stack, $http_kernel)
{
$this->controller_resolver = $controller_resolver;
$this->request_stack = $request_stack;
$this->http_kernel = $http_kernel;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
if ($event->getException() instanceof NotFoundHttpException) {
$request = new \Symfony\Component\HttpFoundation\Request();
$request->attributes->set('_controller', 'AppBundle:Error:notFound');
$controller = $this->controller_resolver->getController($request);
$path['_controller'] = $controller;
$subRequest = $this->request_stack->getCurrentRequest()->duplicate(array(), null, $path);
$event->setResponse($this->http_kernel->handle($subRequest, HttpKernelInterface::MASTER_REQUEST)); // Simulating "forward" in order to preserve the "Not Found URL"
}
}
}
Now register the service:
#AppBundle/Resources/config/services.yml
services:
kernel.listener.notFoundHttpException:
class: AppBundle\EventListener\NotFoundHttpExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: -10 }
arguments: [ #controller_resolver, #request_stack, #http_kernel ]
Not tested this, but rather it should work;)
EDIT:
Tested, it works. On the rendered page, you have a session, so you have access to app.user, his cart, and other matters related to the session.
I have in a Symfony2 application the following bundle architecture:
CommonBundle
FirstBundle
SecondBundle
Several features are implemented in the CommonBundle.
These features have to be available in the 2 other bundles.
The FirstBundle and SecondBundle have therefore their own features + the ones of the CommonBundle. These bundles each have their own host defined in the main application routing.yml file.
What I'm trying to do:
Features of the CommonBundle should be displayed with the layout of the current bundle.
For instance, if I hit http://firstbundle.myapp.com/common/feature1, I should see the layout of the FirstBundle bundle.
And if I hit http://secondbundle.myapp.com/common/feature1, the layout of the SecondBundle bundle should be used.
How can I do that?
I can't use bundle inheritance as the same bundle can't be extended twice.
In my current implementation, each bundle imports the routes of the CommonBundle in its own host.
You should create a controller response listener and change the template name depending on the request hostname in there.
A good read is the How to setup before/after filters chapter of the documentation.
You could aswell use a twig extension registering a global variable and decide which template to extend inside your base template:
config.yml
services:
twig.extension.your_extension:
class: Vendor\YourBundle\Twig\Extension\YourExtension
arguments: [ #request ]
tags:
- { name: twig.extension, alias: your_extension }
YourExtension.php
use Symfony\Component\HttpFoundation\Request;
class YourExtension extends \Twig_Extension
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function getGlobals()
{
// some logic involving $this->request
$baseTemplate = ($this->request->getHost() === 'first.host.tld') ? 'FirstBundle::base.html.twig' : 'SecondBundle::base.html.twig';
return array(
'base_template' => $baseTemplate,
);
}
public function getName()
{
return 'your_extension';
}
base.html.twig
{% extends base_template %}
I want to add my own twig functionality and to add new twig extension in Symfony 2.
To do that i created these folders: src/Ptracker/TasksBundle/Twig and src/Ptracker/TasksBundle/Twig/Extension and put file myTwigExtension.php in it with this content:
<?php
namespace Ptracker\TasksBundle\Twig\Extension;
class MyTwigExtension extends \Twig_Extension {
public function getFilters() {
return array(
'var_dump' => new \Twig_Filter_Function('var_dump'),
'linkable' => new \Twig_Filter_Method($this, 'linkable'),
);
}
public function linkable($sentence, $expr) {
return 'it works!!';
}
public function getName()
{
return 'my_twig_extension';
}
}
?>
Also i added some code to src/Ptracker/TasksBundle/Resources/config/services.yml :
services:
ptracker.twig.extension:
class: Ptracker\TasksBundle\Twig\Extension\MyTwigExtension
tags:
- { name: twig.extension }
The point is that i ALWAYS get the same fatal error:
Fatal error: Class 'Ptracker\TasksBundle\Twig\Extension\MyTwigExtension' not found in /home/renat/www/ptracker/app/cache/dev/appDevDebugProjectContainer.php on line 1092
What am i doing wrong? I've spent several ours to fix this problem, tried to put extension file in different folders, changed namespace.. nothing helps.
Please help me :)
File names are case-sensitive on linux and it doesn't find anything because it tries to load ../MyTwigExtension.php. Rename your file to MyTwigExtension.php.