What is the difference between using .html.twig and .twig? Is there some kind of standard, is it framework specific, or is it just user preference?
Under Symfony you have the possibility to deliver different formats automatically. So you can create files like test.json.twig, test.xml.twig for example. If you define all that extensions in your controller you can deliver all that formats under one action.
For example:
/**
* #Route("/hello/{name}.{_format}", defaults={"_format"="html"}, name="_demo_hello")
* #Template()
*/
public function helloAction($name) {
return array('name' => $name);
}
Something like this. So you can use the format in your route to define the response format.
According to Symfony Documentation
"this is simply an organizational tactic used in case the same resource needs to be rendered as HTML (index.html.twig), XML (index.xml.twig), or any other format."
So sounds like this is user preference, but a good standard to follow either way.
The only difference is the behavior of auto escaping.
Imagine you have a var variable containing: <div>I'm happy</div>.
On index.twig, {{ var }} will render <div>I'm happy</div>.
On index.html.twig, {{ var }} will render <div>I'm happy<div>
On index.js.twig, {{ var }} will render \x3Cdiv\x3EI\x27m\x20happy\x3Cdiv\x3E
And so on.
Always use the right extension to avoid any XSS vulnerability, and always use |raw wisely because it overlaps this extension's implicit protection.
Related
I'm trying to write a template in Twig. Inside it I would like it to perform some manipulations on the string data that comes from the controller. In particular, there's a common manipulation (convert from underscore_case to CamelCase) that I'd like to bring out into a separate function. Then later I can use it repeatedly like {% set x = magic(a) ~ magic(b) %}. However I cannot find how to make such a reusable function inside the template itself. There are macros, but those don't seem to be able to return values. Filters are another option that seem to fit the bill, but I can only define those on PHP side.
Can this be done? Or should I do all the advanced string manipulation controller-side? It kinda feels like I'm pulling parts of display logic in there; things that should be in the view.
Twig is for outputting data. If you need to "transform" the data you need to do that before you send it to twig or you need to extend twig
Ideally, all the data you send to twig is just variables and arrays that needs the least amount of manipulation on their own.
When you're actually "in" twig, the data processing can be assumed to be "done" and only needs to be outputted in the appropriate places with minimal logic to decide user interface styles.
So revisit your logic and prepare your data better before sending it to twig.
An example for extending a toolkit class that contains our magic methods to do real wizardry.
class CustomToolkit
{
public function magic_a($a)
{
return strtolower($a); }
public function magic_b($b)
{
return camel_case($b);
}
public function magic_tidle($a, $b)
{
return $this->magic_a($a) ~ $this->magic_b($b);
}
}
Then you add this to your twig instance. I added here a complete instantiation loop. if you have a service provider you can just grab the instance from there and add it to that one.
$twig = new Twig_Environment(new Twig_Loader_Array([
'html' => $contents
]),[
'auto_reload' => true,
'debug' => false,
]);
$twig->addExtension('toolkit', new CustomToolkit ());
echo $twig->render('html', $values);
Then in your twig code you should be able to do something along the lines of
{% set x = toolkit.magic_tidle("value","value_b") %}
You are right, macros do not have return values and you cannot really make them have any. All they do is outputting strings.
Still, you are able to capture string output using set: https://twig.symfony.com/doc/2.x/tags/set.html
The syntax looks similar to this:
{% set var %}
{{ call.macro() }}
{% endset %}
The output of the macro call is then stored inside var. You may want to strip the whitespace though.
But then, consider rethinking what you are doing. Is this still presentation logic, or is your controller simply "too lazy" to transform the strings prior to passing them to twig? If it's really presentation logic, simply adding a twig filter by extending twig surely is worth the hassle. Not only because your code becomes testable.
Can this be done?
Yes! However, Twig templates are not the ideal places to run logic. At least, we should do our best to avoid it. Instead, Controller should return what Twig template needs. Logic should run in Service, Utility, Helper (you name it) etc. and Controller returns it to Twig. Twig then just displays it.
Can twig macros return values?
Yes! Look at this example. It accepts parameters and returns (not a real "return" thing but you get the idea) something back.
Example:
Assuming that the data you are trying to manipulate is something simple.
use Doctrine\Common\Inflector\Inflector;
Controller
{
action() {
$data = Inflector::camelize('hello_world'); // This will become helloWorld
return ....;
}
}
Look into Inflector class. It has useful stuff.
I have the below content in my DB-
<p>This is dummy content for testing</p>
{{LandingPageController::getTest()}}
I want to render that into my view. But when I'm rendering this in Laravel view, this {{LandingPageController::getTest()}} is getting displayed as it is stored in DB.
I want to call the LandingPageController getTest method in my view.
Please suggest me a quick fix for this.
Landing Page Controller
public function getTest(){
return "Hello World!!!";
}
just make the function static
public static function getTest(){
return "Hello World!!!";
}
that's the only way you can call it like this {{LandingPageController::getTest()}} but I do advice not to do that in your blade file this not a good code design. you should do $test = LandingPageController::getTest() in the controller that you return the blade view and pass it like this return view('blade_file_name',compact('test')) and in your blade file just do {{$test}}
PS - if you doing it your controller use the class like this use Path\To\Controller\LandingPageController
Use namespace for that controller in your blade file. example
namespace App\Http\Controllers\LandingPageController;
You can evaluate a string as a php code using the eval() function
eval — Evaluate a string as PHP code
But it is highly discouraged.
The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.
You can use a generic string, {test} for example, when saving the content in the storage.
<p>This is dummy content for testing</p>
{test}
Then whenever you need to display the actual content, you can simply replace the generic string with the real value. You'll have this line in your blade file:
{{ str_replace('{str}', "Hello World", $content) }}
Take a look at Helper. You can call helper function in view to render your text or html
Got the solution, achieve the functionality with "laravel-shortcodes".
Found a very good tutorial on laravel-shortcodes like wordpress
Is it bad practice to use Model:CONST in a blade view or what is other approach?
For example in the model, I have like this:
class ServiceType extends Eloquent
{
protected $table = 'service_type';
const TYPE_LANDLINE = 1;
const TYPE_SIP = 4;
}
and in the controller:
if ($packageDb->service_type_id == ServiceType::TYPE_SIP) {
$summary[service_type] = $packageDb->service_type_id;
}
if ($packageDb->service_type_id == ServiceType::TYPE_LANDLINE) {
$summary[service_type] = $packageDb->service_type_id;
}
return View::make("order.order-billing")->with('summary', $summary);
In blade I could do something like this (not tested):
#if ($summary['service_type'] == ServiceType::TYPE_SIP)
..
#endif
tl;dr
It is up to you.
Alternate solution
You could create a variable for your constant and pass it to the view:
$roleNames = User::ROLE_NAMES;
return View::make("membership.edit", compact('roleNames'));
Then in the view:
<td>#lang("app.{$roleNames[$member->pivot->role_id]}")</td>
Advantages
Shorter: You don't have to write the fully qualified name of the model. This is pretty handy if you have a deep model structure or long model names.
You can "rename" the constant: Sometimes constant names are general. By passing a variable you can give a more descriptive name for the variable that can tell how exactly you use those constants in that given context.
It is clearer what the view works with: The controller's job is to provide the needed resources (for the view) to generate a response to the request. If you pass the constants to the view in the controller, you can see what resources the view works with.
Disadvantages
Of course there can be down sides too, when using this method is cumbersome. If you have many constants (for example for each user role) then probably you don't want to pass all of them to the view, because you will end up with something like this:
$noRole = User::NO_ROLE;
$memberRole = User::MEMBER_ROLE;
$adminRole = User::ADMIN_ROLE;
$moderatorRole = User::MODERATOR_ROLE;
$reviewerRole = User::REVIEWER_ROLE;
$publisherRole = User::PUBLISHER_ROLE;
return View::make("membership.edit", compact(
'noRole',
'memberRole',
'adminRole',
'moderatorRole',
'reviewerRole',
'publisherRole'
));
The main problems with this:
Lot of unnecessary code for a trivial functionality.
Hard to maintain, especially if your view uses only a few of them.
Violates DRY, especially if you need to do this in almost all function that returns a view.
Of course you could refactor this, create helper functions, but why would you deal with all of this hassle when (in this case) using the constants directly in the view is straightforward and easy to understand:
#if ($user->role === App\User::ADMIN_ROLE)
The rule of thumb is to use the solution that is easier to read and understand. Except if you have a style guide, then you should follow that.
In your blade file you can inject the model
#inject('ServiceTypeModel', 'App\Models\ServiceType')
and then use constants like this
{{ ServiceTypeModel::SIP }}
or
#if ($x < ServiceTypeModel::SIP)...
I'm playing around with a CMS idea for Symfony and I'm not sure if what I want to implement is possible. I'm looking for some guidance.
I want to create a twig function that acts more like a block.
I want to be able to write template like this:
{% content('main_content') %}
<p>Some markup here</p>
{% end content('main_content') %}
where content($id) is a function that gives me access to the markup inside and allows my twig extension to change that markup if needed.
The content($id) function's goal will be to change the mark up based on what $id is given.
Obviously my train of thought on this implementation is off because I cannot find any docs relevant to this.
I'm not sure how to word this question : Is it possible to write a twig function that acts like a block and gives me access to the inner data. If possible is there some examples or blogs you can link me to? I'm using Symfony so answers with Symfony implementations are a plus.
edit: an example of the content($id) function
class TwigExtension extends \Twig_Extension{
public function content ($id, $content) {
/* pseudo code now*/
if($id = "some condition"){
return $someNewContent;
}
return $content;
}
}
You need to create a new Twig tag. More documentation here: https://github.com/twigphp/Twig/blob/master/doc/advanced.rst#tags
Also the Twig_Node_Block will give a good idea of what you need to build:
https://github.com/twigphp/Twig/blob/1.x/lib/Twig/Node/Block.php
I have a template which calls a number of built in macros which I am including this template from several other places.
Sometimes, I need all of the macros to be called like this:
{{ Form::label('foo', 'Foo') }}
Other times I need them all to be called like this:
{{{ Form::label('foo', 'Foo') }}}
At the moment, I have two separate templates which are identical except for the extra { }, which means I have to edit two files every time I want to change anything.
Is there a way to switch the auto escaping on/off, so that I can use the same file for both situations?
Thanks
No, there's no feature in Laravel that would allow you to do that -- additionally, it'd probably be a bad idea from a code maintenance/security-audit point of view. Looking at a template a few weeks later and not knowing which variables were or were not escaped would be madness.
If you need to do this, "The Right" way would be to extend Blade with your own directive -- something like #escapeIsConfigIsOn and then put your logic for when to escape content in there. The top level function e is what blade uses internally for escaping.
#File: login/vendor/laravel/framework/src/Illuminate/Support/helpers.php
function e($value)
{
return htmlentities($value, ENT_QUOTES, 'UTF-8', false);
}