I'm using Symfony 2.0.19. I'm trying to create a hyperlink to an external URL, which is retrieved from a database.
I tried doing this
<td>{{dominio.url}}</td>
but the path I get is a relative path to the URL inside the base URL example "localhost/web/www.tralalalala.com" instead of just "www.tralalalala.com".
How do I do this?
Here's a concrete example of what Pierrickouw is suggesting:
Create a Twig extension or filter under src/Twig, and call it for example ExternalLinkFilter. Define the class as follows:
<?php
namespace AppBundle\Twig;
class ExternalLinkFilter extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('external_link', array($this, 'externalLinkFilter')),
);
}
/* source: http://stackoverflow.com/a/2762083/3924118 */
public function externalLinkFilter($url)
{
if (!preg_match("~^(?:f|ht)tps?://~i", $url)) {
$url = "http://" . $url;
}
return $url;
}
public function getName()
{
return 'external_link_filter';
}
}
?>
Now, you should register this class as a service in config/services.yml as follows:
services:
# other services
app.twig.external_link:
class: AppBundle\Twig\ExternalLinkFilter
public: false
tags:
- { name: twig.extension }
Now you can simply use the filter called external_link as you would use any Twig's default one, e.g.:
...
{{check.hostname}}
...
I suggest you to build your own Twig filter.
If your url aldready have http://, don't add it, otherwise, add it.
Check here for how-tos.
A symfony 5 version of nbro's answer will be:
(This will take care of all kind of url that is read from the db)
source: https://symfony.com/doc/current/templating/twig_extension.html
in src/Twig create the file
<?php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class ExternalLinkFilter extends AbstractExtension
{
public function getFilters()
{
return array(
new TwigFilter('external_link', array($this, 'externalLinkFilter')),
);
}
/* source: http://stackoverflow.com/a/2762083/3924118 */
public function externalLinkFilter($url)
{
if (!preg_match("~^(?:f|ht)tps?://~i", $url)) {
$url = "http://" . $url;
}
return $url;
}
public function getName()
{
return 'external_link_filter';
}
}
?>
You don't need to register it as a service. Symfony 5 will notice the presence of that Twig extension
Use it in twig template as suggested by #nbro
{{check.hostname}}
Related
In Symfony 4.1 I created an twig extension and I tried to use it as an service
twig.extension.active.algos:
class: App\Twig\AppExtension
public: true
tags:
- { name: twig.extension, priority: 1024 }
Unfortunately I receive 'Unable to register extension "App\Twig\AppExtension" as it is already registered'
After many searches I saw that there was a bag in the version of symfony 3.4 but they say the error would have solved. So it's my mistake or just another mistake from symfony team.
My extension is:
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends \Twig_Extension {
public function getFunctions() {
return array(
new \Twig_SimpleFunction('get_active_algos', array($this, getActiveAlgos')),
);
}
public function getActiveAlgos()
{
return [1,2,3];
}
public function getName()
{
return 'get_active_algos';
}
}
Got bored. Here is a working example of a custom twig function for S4.1. No service configuration required (Update: except for the added $answer argument). I even injected the default entity manager using autowire just because.
namespace App\Twig;
use Doctrine\ORM\EntityManagerInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class TwigExtension extends AbstractExtension
{
private $em;
private $answer;
public function __construct(EntityManagerInterface $em, int $answer)
{
$this->em = $em;
$this->answer = $answer;
}
public function getFunctions()
{
return array(
new TwigFunction('get_active_algos', [$this, 'getActiveAlgos']),
);
}
public function getActiveAlgos()
{
$dbName = $this->em->getConnection()->getDatabase();
return 'Some Active Algos ' . $dbName . ' ' . $answer;
}
}
Update: Based on the first comment, I updated the example to show injecting a scaler parameter which autowire cannot handle.
# services.yaml
App\Twig\TwigExtension:
$answer: 42
Note that there is still no need to tag the service as an extension. Autoconfig takes care of that by automatically tagging all classes which extend the AbstractExtension.
After adding a custom filter I get Twig Syntax Errors like:
"The function "asset" does not exist in "TwigBundle:Exception:exception_full.html.twig" at line 4".
My Extension:
<?php
namespace AlexanderBuerkle\ShopBundle\Twig;
use AlexanderBuerkle\ShopBundle\Twig\Extension;
use Twig_Extension;
class UnescapeExtension extends \Twig_Extension
{
public function getFilters()
{
new \Twig_SimpleFilter('unescape', array($this, 'unescape'));
}
public function unescape($value)
{
return html_entity_decode($value);
}
public function getName()
{
return 'unescape_extension';
}
}
?>
I call the service in my config.yml (because my predecessor did so, too):
# Twig Extensions
services:
ShopBundle.twig.unescape_extension:
class: AlexanderBuerkle\ShopBundle\Twig\UnescapeExtension
tags:
- { name: twig.extension }
Why are my Twig functions overwritten? Oviously I am missing something...
Any help would be greatly appreciated!
The Problem was that the filters could not be loaded in vendor/twig/twig/lib/Twig/Environment.php, because getFilters() did not return an array. Working function:
public function getFilters()
{
return array
(
new \Twig_SimpleFilter('unescape', array($this, 'unescape')),
);
}
Is there a way in Twig to add a space where the word has camel case letters.
For instance: helloWorldHowAreYouDoing would be hello World How Are You Doing
Thanks!
From php version 5.5 the preg_replace_callback function is required as the "/e" modifier is now deprecated.
Create an extension method and call it from the twig page as a filter.
Extension Class:
<?php
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
class yourExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('camelToSpace', array($this, 'convertCamelCaseToHaveSpacesFilter')),
);
}
/*
* Converts camel case string to have spaces
*/
public function convertCamelCaseToHaveSpacesFilter($camelCaseString)
{
$pattern = '/(([A-Z]{1}))/';
return preg_replace_callback(
$pattern,
function ($matches) {return " " .$matches[0];},
$camelCaseString
);
}
public function getName()
{
return 'app_extension';
}
}
Where the function convertCamelCaseToHaveSpacesFilter will do the work if you pass it a camel case string.
In Twig page:
<body>
{% set yourString = 'helloWorldHowAreYouDoing' %}
{{ yourString|camelToSpace }}
</body>
This should also work for Pascal casing but it may require triming the string afterwards.
If autowiring is not enabled, remember to register your extension class. In services.yml:
services:
app.twig_extension:
class: RouteToExtensionClass\yourExtension
public: false
tags:
- { name: twig.extension }
You should create Twig Extension and create a function to do that.
Something like this would do it..
/**
* Convert under_score to title Case
*
* #param $underscore
* #return string
*/
public static function convertUnderscoreToTitleCase($underscore)
{
return preg_replace('/(?:^|_)(.?)/e', "strtoupper(' $1')", $underscore);
}
It's not there out of the box. You can create twig extension to do that:
http://symfony.com/doc/current/cookbook/templating/twig_extension.html
After read the docs I tried my first filter but got this error
The filter "sanitize_for_image_url" does not exist in
/var/www/html/src/CategoryBundle/Resources/views/Default/menu.html.twig
at line 5.
What I did was:
Create a folder under my bundle directory and call it Twig.
Under that folder create the file CategoryExtension.php and add this code:
<?php
namespace CategoryBundle\Twig;
class CategoryExtension extends \Twig_Extension {
public function getFilters() {
return array(
new \Twig_SimpleFilter('price', array($this, 'priceFilter')),
);
}
public function sanitize_for_image_urlFilter($image) {
$image = strtolower($image);
$image = preg_replace('/[^a-z0-9 -]+/', '', $image);
$image = str_replace(' ', '-', $image);
return $image;
}
public function getName() {
return 'category_extension';
}
}
Create a folder inside \CategoryBundle\Resources and called "config" and under config created the file "services.yml" with this content:
services:
category.twig.category_extension:
class: CategoryBundle\Twig\CategoryExtension
tags:
- { name: twig.extension }
Call the filter in my twig template as follow:
<img src="{{ asset('bundles/dashboard/img/categories/' ~ entity.getName|lower|sanitize_for_image_url ~ '.gif') }}">
Did I miss something else?
You're not telling twig about your new filter. You'll also need to modify the getFilters() method of your class.
I have a loop in Twig template, which returns multiple values. Most important - an ID of my entry. When I didn't use any framework nor template engine, I used simply file_exists() within the loop. Now, I can't seem to find a way to do it in Twig.
When I display user's avatar in header, I use file_exists() in controller, but I do it because I don't have a loop.
I tried defined in Twig, but it doesn't help me. Any ideas?
If you want want to check the existence of a file which is not a twig template (so defined can't work), create a TwigExtension service and add file_exists() function to twig:
src/AppBundle/Twig/Extension/TwigExtension.php
<?php
namespace AppBundle\Twig\Extension;
class FileExtension extends \Twig_Extension
{
/**
* Return the functions registered as twig extensions
*
* #return array
*/
public function getFunctions()
{
return array(
new Twig_SimpleFunction('file_exists', 'file_exists'),
);
}
public function getName()
{
return 'app_file';
}
}
?>
Register your service:
src/AppBundle/Resources/config/services.yml
# ...
parameters:
app.file.twig.extension.class: AppBundle\Twig\Extension\FileExtension
services:
app.file.twig.extension:
class: %app.file.twig.extension.class%
tags:
- { name: twig.extension }
That's it, now you are able to use file_exists() inside a twig template ;)
Some template.twig:
{% if file_exists('/home/sybio/www/website/picture.jpg') %}
The picture exists !
{% else %}
Nope, Chuck testa !
{% endif %}
EDIT to answer your comment:
To use file_exists(), you need to specify the absolute path of the file, so you need the web directory absolute path, to do this give access to the webpath in your twig templates
app/config/config.yml:
# ...
twig:
globals:
web_path: %web_path%
parameters:
web_path: %kernel.root_dir%/../web
Now you can get the full physical path to the file inside a twig template:
{# Display: /home/sybio/www/website/web/img/games/3.jpg #}
{{ web_path~asset('img/games/'~item.getGame.id~'.jpg') }}
So you'll be able to check if the file exists:
{% if file_exists(web_path~asset('img/games/'~item.getGame.id~'.jpg')) %}
I've created a Twig function which is an extension of the answers I have found on this topic. My asset_if function takes two parameters: the first one is the path for the asset to display. The second parameter is the fallback asset, if the first asset does not exist.
Create your extension file:
src/Showdates/FrontendBundle/Twig/Extension/ConditionalAssetExtension.php:
<?php
namespace Showdates\FrontendBundle\Twig\Extension;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ConditionalAssetExtension extends \Twig_Extension
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Returns a list of functions to add to the existing list.
*
* #return array An array of functions
*/
public function getFunctions()
{
return array(
'asset_if' => new \Twig_Function_Method($this, 'asset_if'),
);
}
/**
* Get the path to an asset. If it does not exist, return the path to the
* fallback path.
*
* #param string $path the path to the asset to display
* #param string $fallbackPath the path to the asset to return in case asset $path does not exist
* #return string path
*/
public function asset_if($path, $fallbackPath)
{
// Define the path to look for
$pathToCheck = realpath($this->container->get('kernel')->getRootDir() . '/../web/') . '/' . $path;
// If the path does not exist, return the fallback image
if (!file_exists($pathToCheck))
{
return $this->container->get('templating.helper.assets')->getUrl($fallbackPath);
}
// Return the real image
return $this->container->get('templating.helper.assets')->getUrl($path);
}
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName()
{
return 'asset_if';
}
}
Register your service (app/config/config.yml or src/App/YourBundle/Resources/services.yml):
services:
showdates.twig.asset_if_extension:
class: Showdates\FrontendBundle\Twig\Extension\ConditionalAssetExtension
arguments: ['#service_container']
tags:
- { name: twig.extension }
Now use it in your templates like this:
<img src="{{ asset_if('some/path/avatar_' ~ app.user.id, 'assets/default_avatar.png') }}" />
I've had the same problem as Tomek. I've used Sybio's solution and made the following changes:
app/config.yml => add "/" at the end of web_path
parameters:
web_path: %kernel.root_dir%/../web/
Call file_exists without "asset" :
{% if file_exists(web_path ~ 'img/games/'~item.getGame.id~'.jpg') %}
Hope this helps.
Here is my solution, using SF4, autowire and autoconfigure:
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Symfony\Component\Filesystem\Filesystem;
class FileExistsExtension extends AbstractExtension
{
private $fileSystem;
private $projectDir;
public function __construct(Filesystem $fileSystem, string $projectDir)
{
$this->fileSystem = $fileSystem;
$this->projectDir = $projectDir;
}
public function getFunctions(): array
{
return [
new TwigFunction('file_exists', [$this, 'fileExists']),
];
}
/**
* #param string An absolute or relative to public folder path
*
* #return bool True if file exists, false otherwise
*/
public function fileExists(string $path): bool
{
if (!$this->fileSystem->isAbsolutePath($path)) {
$path = "{$this->projectDir}/public/{$path}";
}
return $this->fileSystem->exists($path);
}
}
In services.yaml:
services:
App\Twig\FileExistsExtension:
$projectDir: '%kernel.project_dir%'
In templates:
# Absolute path
{% if file_exists('/tmp') %}
# Relative to public folder path
{% if file_exists('tmp') %}
I am new to Symfony so every comments are welcome!
Also, as initial question is about Symfony 2, maybe my answer is not relevant and I would better ask a new question and answer by myself?
Improving on Sybio's answer, Twig_simple_function did not exist for my version and nothing here works for external images for example. So my File extension file is like this:
namespace AppBundle\Twig\Extension;
class FileExtension extends \Twig_Extension
{
/**
* {#inheritdoc}
*/
public function getName()
{
return 'file';
}
public function getFunctions()
{
return array(
new \Twig_Function('checkUrl', array($this, 'checkUrl')),
);
}
public function checkUrl($url)
{
$headers=get_headers($url);
return stripos($headers[0], "200 OK")?true:false;
}
Just add a little comment to the contribution of Sybio:
The Twig_Function_Function class is deprecated since version 1.12 and
will be removed in 2.0. Use Twig_SimpleFunction instead.
We must change the class Twig_Function_Function by Twig_SimpleFunction:
<?php
namespace Gooandgoo\CoreBundle\Services\Extension;
class TwigExtension extends \Twig_Extension
{
/**
* Return the functions registered as twig extensions
*
* #return array
*/
public function getFunctions()
{
return array(
#'file_exists' => new \Twig_Function_Function('file_exists'), // Old class
'file_exists' => new \Twig_SimpleFunction('file_exists', 'file_exists'), // New class
);
}
public function getName()
{
return 'twig_extension';
}
}
The rest of code still works exactly as said Sybio.