So I want to keep linebreaks from the database while using the Blade Template Engine. I came up on the idea using
{!! nl2br(e($task->text)) !!}
It works. But it looks like a needlessly complicated solution. Is there a better way?
You can define your own "echo format" that will be used with the regular content tags {{ ... }}. The default format is e(%s) (sprintf is used to apply the formatting)
To change that format call setEchoFormat() inside a service provider:
public function boot(){
\Blade::setEchoFormat('nl2br(e(%s))');
}
Now you can just use the normal echo tags:
{{ $task->text }}
For echos you don't want nl2br() applied, use the triple brackets {{{ ... }}}
To switch the function of the brackets (triple and double) around, do this:
\Blade::setContentTags('{{{', '}}}');
\Blade::setEscapedContentTags('{{', '}}');
Simple approach which works for Laravel 4 + Laravel 5.
{!! nl2br(e($task->text)) !!}
Below solution worked in blade file in Laravel 5.7 version for me:
{!! nl2br(e($contactusenquiry_message), false) !!}
Thanks to ask this question.
A slightly cleaner alternative if you're using Eloquent is Mutators. On your Task model create a method like this:
public function getTextAttribute($value)
{
return nl2br(e($value), false);
}
Now you can use {!! $task->text !!} and it will output the HTML correctly and securely. The good thing about this method is you can do all manner of conversions in the get...Attribute method, such as adding wrapper tags or using Markdown.
If you need access to both the raw data and HTML version you could replace the above with this:
public function getTextHtmlAttribute()
{
return nl2br(e($this->text), false);
}
Then you would use {{ $task->text }} for the original and {!! $task->text_html !!} for the HTML version.
This is a way to do it while keeping everything safe
<?php foreach (explode("\n", $text) as $line) { ?>
{{$line}}<br />
<?php } ?>
Related
I created a custom twig function in AppExtension Class. I need to call form_label() from this new function. ¿Is it posible? I tried but does not work:
from template I call:
{{ myFunc(form.someField) }}
public function myFunc( $field )
{
$html = form_label($field);
}
The idea is to render each form field in a different order/way than the form_widget(form) twig function. The "form_label()" function it's not reconized.
Thx for any suggestion.
I feel like this is the wrong approach to handle this. Extensions are for transforming data not really to manipulate the form definition itself.
First of all the order is defined as in the form type, so you can swap those around. To render the fields differently you can use form themes, or even rendering a custom form type.
Alternatively if its a one time thing (you could also create a macro for this) you can also instead of form_widget(form) order them in the way you like.
{{ form_start(form) }}
{{ form_row(form.field3) }}
{{ form_row(form.field1, { attr: { class: 'im-different' } }) }}
{{ form_row(form.field2) }}
{{ form_end(form) }}
Or even go deeper.
{{ form_start(form) }}
{{ form_row(form.field3) }}
<div>
{{ form_label(form.field1) }}
{{ form_widget(form.field1) }}
{{ form_errors(form.field1) }}
</div>
{{ form_row(form.field2) }}
{{ form_end(form) }}
To see these functions and how they all rendered by default you can look at form_div_layout.html.twig.
I agree with Jenne van der Meer and Nico Haase that your approach isn't particularly optimal. If I had the choice, I would go a different route: Instead of rendering in your function, render in twig, then pass the result to the function (like {{ myFunc(form_label(form), form) }}). Since you omit what your function actually needs and/or does, it's hard to provide further advice. However, I'm absolutely sure, that rendering can be done in twig before or after entering your function, via a macro/block, maybe even a form theme).
However, if you really really require your function to render the form field ... the following will possibly help you. I strongly advise against doing this, there's probably a better suited solution.
The form_label function is slightly more complex than a simple function. Instead, it uses twig's compile mechanisms to generate specific php code. It will eventually call:
FormRenderer::searchAndRenderBlock(FormView $view, string $blockNameSuffix, array $variables = [])
Diving deep into the compiler, the template call form_label(form, options) would be turned into:
$this->env->getRuntime('Symfony\Component\Form\FormRenderer')->searchAndRenderBlock(
$form, 'label', $options
);
where the $this->env seems to be the twig environment. That means, to call this in your twig extension you need to have access to the proper Twig environment, and then it should already work with the recipe I just provided. Especially if you can omit the options argument, I didn't take a deeper look into how that one's assembled (but it's probably just straight forward).
So your twig function must be defined via:
public function getFunctions(): array
{
return [
new TwigFunction('myFunc', [&$this, 'myFunc'], [
'needs_environment' => true, // <--- this!
'is_safe' => ['html'],
]),
];
}
public function myFunc(\Twig\Environment $env, $field) {
// other stuff
$html = $env->getRuntime(\'Symfony\Component\Form\FormRenderer\')->searchAndRenderBlock(
$field, 'label', $options
);
return $html;
}
Need to convert script with "spaghetti code" to Twig. Read Twig documentation and got basics working. However, I need advice on how to do everything properly, so no re-conversion is needed later. Let's say current script looks following:
file index.php:
<?php
$page_message="do it";
function display_dropdown($max)
{
for ($i=0; $i<$max; $i++)
{
echo "<option value='$i'>Option $i</option>";
}
}
?>
<!DOCTYPE html>
<html>
<body>
<h1><?php echo $page_message; ?></h1>
<form method="post" action="<?php echo basename($_SERVER["SCRIPT_FILENAME"]); ?>">
<select name="whatever"><?php display_dropdown(10); ?></select>
<input type="submit" value="go">
<?php include("footer.php");?>
</body>
</html>
footer.php looks:
<?php
$footer_text="blah blah";
?>
<footer><?php echo $footer_text; ?></footer>
As far I understand, my index.php should look like this when converted to Twig:
<?php
$page_message="do it";
function display_dropdown($max)
{
for ($i=0; $i<$max; $i++)
{
echo "<option value='$i'>Option $i</option>";
}
}
$twig_params_array=array("page_message"=>$page_message, "footer_text"=>"blah blah");
require_once("../lib/Twig/Autoloader.php");
Twig_Autoloader::register();
$loader=new Twig_Loader_Filesystem("templates");
$twig=new Twig_Environment($loader);
echo $twig->render("index_template.html", $twig_params_array);
?>
Then I should create index_template.html and footer_template.html (or whatever) with following code:
index_template.html
<!DOCTYPE html>
<html>
<body>
<h1>{{ page_message }}</h1>
<form method="post" action="{{ _self }}>">
<select name="whatever"><?php display_dropdown(10); ?></select>
<input type="submit" value="go">
{{ include('footer_template.html') }}
</body>
</html>
footer_template.html
<footer>{{ footer_text }}</footer>
If I understand right, it's also possible to "include" functions in Twig templates (with some tweaking in templates), so I don't need to rewrite existing PHP functions like display_dropdown(). Because dropdown is not displayed at the moment...
The thing that concerns me is array with variables (passed to Twig render function). Do I miss something, or is it really needed to manually define each variable (like $page_message and $footer_text) before Twig can work?
It seems like a lot of work to do, because in "spaghetti code" if I define variable somewhere, I can access it at any time just by using echo function. Now, it looks I need to view every single variable that exists in PHP code and manually pass it to Twig parameters array. Really?
Since no solution was provided, I found it myself. All you have to do is use such a line of code, so all the variables you have in your PHP file (counting included files) become available in Twig templates, and you don't need to re-write anything when new variables are added to PHP file later (they also automatically become available in Twig):
echo $twig->render("template_file_name.extension", get_defined_vars());
Mindaugas, just changing the technology you use to render views is not going to make the code less tangled.
I think you should go one step further and divide responsibilities, by using an MVC framework, like Symfony, which uses Twig as its default templating language.
Good luck!
I have a twig template with the navbar and all other templates (the pages) include this template. I have a value in it which should be equal to all pages. How to set this value?
I tries something like this in a controller:
public function setNotificationsAction() {
$this->setNotifications();
return $this->render('AcmeMyBundle::navbar.html.twig', array(
'debts' => $this->notifications,
));
}
and then this in the template:
<span class="badge badge-important">
{% render(controller('AcmeMyBundle:DebtsLoansController:setNotifications')) %}
{{ debts }}
</span>
The result I want it like this:
<span class="badge badge-important">
3
</span>
but the number should be different and the controller should tell it.
I also tried to create a function which returns the value and to call it in the way like above.
I also tried this syntax
{{ render(controller('AcmeMyBundle:DebtsLoansController:setNotifications')) }}
but it isn't working, too.
I get the following mistake:
The function "controller" does not exist in AcmeMyBundle::navbar.html.twig at line 6
Do you have any idea how to achive this and not to have to edit each controller and each template :S Thanks very much in advance!
Well, I would suggest creating your own Twig extension. Something around the lines of:
<span class="badge">
{{ acme_notifications() }}
</span>
namespace Acme\DemoBundle\Twig\AcmeDemoExtension
class AcmeDemoExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
'acme_notifications' => new \Twig_Function_Method($this, 'getNotifications');
);
}
public function getNotifications()
{
$notifications = ...;
return $notifications;
}
}
Read more about creating your own Twig extension in the Symfony2 documentation.
You don't need the controller part :
{% render "AcmeBundle:MyController:MyAction" %}
Be aware however, that a render is a completely new request going through the whole Symfony lifecycle and thus can impact performance if you abuse it.
Edit : And as #Wouter J has pointed out : prior to Symfony 2.2 use above notation. After Symfony 2.2 the following has to be used :
{{ render(controller('AcmeArticleBundle:Article:recentArticles', { 'max': 3 })) }}
I'm nearly new in Symfony2 and I have a little question:
I'm developing an email template, which has txt and html parts (no problem with it)
The only 'problem' I have is with the absolute paths of assets in TWIG.
Inside my email.html.twig file I have something like this:
<img src="{{ asset('images/my-image.png') }}" alt="My image" /> but it writes the route with relative path.
I discovered a little solution to add absolute paths, something like this:
{% set abs = app.request.scheme ~ '://' ~ app.request.host %}
<img src="{{ abs ~ asset('images/my-image.png') }}" alt="My image" />
It works! But I want to improve this solution and also learn to create custom filters (I read the documentation, but I got a bit lost)
I want to create something like this:
<img src="{{ asset('images/my-image.png' | absolute) }}" alt="My image" />
But I don't know how to properly override the assetics extension. Can you help me?
Thanks a lot!!
Well, it is a little hard to copy paste the solution but I can make a short cookbook so you can go step by step and do it yourself:
1) You will have to implement Assetic/Filter/FilterInterface
2) If you look at the FilterInterface class, you will see that you have to to implement two methods: filterLoad and filterDump.
So, you will do something like this:
<?php
namespace You\YourBundle\Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
class YourAsseticFilter implements FilterInterface
{
public function filterLoad(AssetInterface $asset)
{
// do something
}
public function filterDump(AssetInterface $asset)
{
$content = $asset->getContent();
// do something
$asset->setContent($content);
}
}
And after you to this, you have to something very similar to registering twig extensions in services.yml in YourBundle. Sure, it depens if you use YML, XML... configuration. I use yml so I will type it in yml :)
parameters:
your_bundle.class: You\YourBundle\Assetic\Filter\YourAsseticFilter
services:
your_bundle.assetic.your_assetic_filter:
class: %your_bundle.class%
tags:
- { name: assetic.filter }
- { alias: yourChosenNameForYourNewAsseticFilter }
And then you call it like | yourChosenNameForYourNewAsseticFilter, of course.
I'm sharing templates between client and server and would like to output the raw template inside a script tag which is possible with verbatim.
http://twig.sensiolabs.org/doc/tags/verbatim.html
However it would be nicer if this could be applied as a filter to the include but it doesn't seem possible?
I'm new to twig so excuse me if i've missed obvious functionality.
I ran into the same problem, and this page came up in my search results. In the time since this question was answered, the Twig developers added this functionality into the library. I figured I should add some details for future searchers.
The functionality to include raw text (aka for client-side templates using the same syntax as Twig) is accomplished with the source function.
Ie: {{ source('path/to/template.html.twig') }}
http://twig.sensiolabs.org/doc/functions/source.html
I was looking for something like this too because I'm using Twig.js for some client-side templating along with Symfony. I was trying to share templates between the server-side and client-side code, so I needed content to be parsed in some cases and treated as verbatim in others, which proved to be a bit tricky.
I couldn't find anything built into Twig to help with this, but luckily, it's pretty easy to extend Twig to get what you're looking for. I implemented it as a function, but you may be able to do it as a filter too.
services.yml
statsidekick.twig.include_as_template_extension:
class: StatSidekick\AnalysisBundle\Twig\IncludeAsTemplateExtension
tags:
- { name: twig.extension }
IncludeAsTemplateExtension.php
<?php
namespace StatSidekick\AnalysisBundle\Twig;
use Twig_Environment;
use Twig_Extension;
class IncludeAsTemplateExtension extends Twig_Extension {
/**
* Returns a list of global functions to add to the existing list.
*
* #return array An array of global functions
*/
public function getFunctions() {
return array(
new \Twig_SimpleFunction( 'include_as_template', array( $this, 'includeAsTemplate' ), array( 'needs_environment' => true, 'is_safe' => array( 'html' ) ) )
);
}
function includeAsTemplate( Twig_Environment $env, $location, $id ) {
$contents = $env->getLoader()->getSource( $location );
return "<script data-template-id=\"{$id}\" type=\"text/x-twig-template\">{$contents}</script>";
}
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName() {
return 'include_as_template_extension';
}
}
Usage in Twig file
{{ include_as_template( 'Your:Template:here.html.twig', 'template-id' ) }}
If you have a Twig file like this:
<ul class="message-list">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
The output will be this:
<script data-template-id="template-id" type="text/x-twig-template">
<ul class="message-list">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</script>
The work is derived from Kari Söderholm's answer here. Hope that helps!