Lets say I have two controllers that work with the same entity, and use the same set of templates. Every template is supposed to generate buttons/forms/links with URLs to actions of controller that generated this template. So basically, the only thing that is different in the templates are the URLs generated.
You can imagine the problem like a admin CRUD controller with a newAction and editAction, which use the same template, but form actions must differ. My case is more complicated than that, though.
I considered following:
Setting the routes from the controller, but it feels like a lot of code that is out of place.
Different set of templates. Seems like anti-DRY and a lot of reused code.
Using template inheritance, where base template is the one with all the HTML, and child templates only contain the links, but it feels really hacky.
Is there a clever approach to solve this problem?
EDIT: My problem is not generating CRUD. This is more of a "best-practice" question.
I encountered this problem while implementing something like a eshop cart with tons of javascript logic bound to it. It appears twice: in the website, and in a iframe, used by some other devices (iPads and stuff). Both carts have to look the same, but because of different logic, the links must lead to different URLs.
What I ended up doing for now is having 2 templates
The one with all the markup, cart.html.twig:
{% set edit_cart_item = path('edit_cart_item') %}
{% set remove_cart_item = path('remove_cart_item') %}
...html...
Edit item
...more html...
The one for usage in iframe, public_cart.html.twig:
{% extends 'MyCartBundle::cart.html.twig' %}
{% set edit_cart_item = path('public_edit_cart_item') %}
{% set remove_cart_item = path('public_remove_cart_item') %}
Does the problem have some better solution in all the fancy OOP principles?
There isn't a 'clever' approach really. Why not just use the specified Doctrine command to generate it for you, and then go from there? http://symfony.com/doc/current/bundles/SensioGeneratorBundle/commands/generate_doctrine_crud.html
php app/console generate:doctrine:crud with various options
That will get you a solid base done in an 'appropriate' manner, and then you can perform your customizations.
EDIT:
After reading your updated post, I would have a look at this: http://symfony.com/doc/current/book/forms.html#changing-the-action-and-method-of-a-form
So, if you'd like, you can handle the logic of which form to display by passing in options to the form, and then setting the target via setAction():
$form = $this->createFormBuilder($task)
->setAction($this->generateUrl('target_route'))
;
By design, Symfony gives you a lot of flexibility in how to do things. A good reference of their best practices for forms can be found here: http://symfony.com/doc/current/best_practices/forms.html
I also think what you're doing is just fine. When I have complex cases for forms I like to create a Twig template just for the form itself, and then include that in my other templates. In that template you can pass the target route to it if you'd like, and then you'd just have one form template.
Related
I have trivial complex page.
For example: content(left) and sidebar(right).
Sidebar consists of a plurality of parts. We take a look at one of theese. This fragment contains list of users(for example) subscribed to the event. The list displays only 5 users and "More" button, which loads more users when clicked (ajax).
If i use:
{% render controller('SomeBundle:Event:subcribers', {page: 1, limit: 5}) %}
Or:
{% render path('event_subcribers_route', {...}) %}
It will cause a subrequest. Notice: in this case i use this route 'event_subcribers_route' for ajax request.
Question: Maybe exists way to avoid dozens subrequests?
I'm aware that i can use services, with render logic inside them, but maybe exists some best practice for this common task.
What is wrong with subrequests?
Maybe you think about more traffic between browser and server as it will happen if you have many images, css files or javascript directives in your HTML.
This is not the point using internal subrequests within the Symfony framework. actually this is just some program-logic or design pattern.
browser->request->[router->controller->view]->response->browser
^ |
| |
|<-subrequest-V
This is a very simplified schema of a subrequest like it happens using the {% render controller() %} in Twig. Between the [ and ] is inside Symfony. You just stay into the application.
Everytime you include that piece of HTML you will need to do the according subrequest to the database no matter which method you use.
But, you can optimize the query to recover those users. You can select only the fields you actually need (username, slug, etc.) and also you can do it on a single query. That single query will become a prepared statement and you can even put index on the table to make the query faster.
In addition, you can implement the symfony cache system to avoid those subrequest that symfony does on every load. If your snippet loads always the same 5 users or they change after X minutes you can easily implement an efficient cache.
I'm using Symfony2 with Twig templating engine.
Is there any way to output a list of all Twig templates files loaded in the current request, including the ones loaded through extends, include, etc.?
That would make my life much easier when overriding third-party bundles' blocks, but I can't find a way to do it.
I've been looking for such a tool for a long time but never found it... The debug options of twig are very limited, and there is no tool in the sf2 dev bar dedicated to it...
I always add twig or html comments on top of each of my templates to get an idea of where I am and why during development or on the final page.
You can try this code, it puts filenames in HTML like this:
<!-- START templatename.html.twig -->
...
<!-- END templatename.html.twig -->
I know, that it is not a good solution, but it is better than nothing.
not a problem when you are working in dev in app_dev.php
expand bottom SF toolbar, click on 200 status or on #your_rote_name
you will redirect to smt like localhost/_profiler/s0meha5h?panel=*
then click on left menu on TWIG then url will be like localhost/_profiler/s0meha5h?panel=twig
and you will see all templates like FolderYourBundle:Folder:twig_file_name.html.twig that loads one by one!
Is is possible to define regions in the template, that would pull content from the page?
Let's say I have in my template the following structure:
<div class=sidebar></div>
<div class=content></div>
And from the page content, I would like to pull some html content to the sidebar, and other content to content div.
Is this possible?
With Swig as the Engine
Yes, this is possible. Seehttps://github.com/assemble/boilerplate-swig, in particular, this example, which shows how to use {% macro %} tags to accomplish what you're asking about.
If you want to use Swig, be sure to look at the readme as the assemble-swig repo as well. You have to register swig as the current engine in assemble:
assemble: {
options: {
engine: 'swig'
}
}
With Handlebars as the Engine
If the sidebar content will always be the same, on every page then you can use partials for this. Even if the URLs or active classes change on each page, this should work.
However, dynamic content using template or "block" inheritance, e.g. extend can be achieved with Handlebars helpers.
But since layouts are used with assemble this is a bit trickier to do with "out-of-the-box" helpers. To clarify, just about any helper I can think of will work great with assemble out-of-the-box, except for this - specifically because of how layouts work.
My suggestion is that you add to the existing feature request(s) for this on assemble and/or the handlebars-helpers project to add your use case and thoughts on what you want to achieve:
https://github.com/assemble/assemble/issues/38
https://github.com/assemble/handlebars-helpers/issues/16
#jonschlinkert You should update assemble's documentation, cause start with Assemble isn't so easy and a lot of things are little hidden.
So Luis, you can try this method, which currently works great for me too!
Assemble: Multiple points of content insertion in layout?
This is more a discussion question:
Given you have a component in your bundle, that consists of a service, maybe a model and template and you want to give an easy way to include it in your main templates.
You could provide a twig extension which internally uses a helper to render the template like:
{{ acme_render_component({foo: 'bar'}) }}
or you would decide to let the main template use a embedded controller like:
{{ render(controller('AcmeBundle:CoolComponent:render', { 'foo': bar })) }}
I guess the cases behave different:
With the twig extension, you would probably use the service first to fill the model and fetch it again in the template.
With the controller you would probably execute the service on demand.
What are the differences, advantages or caveats?
When should you provide the one or the other, or both?
I guess one reason to use a custom Twig extension is performance. A call to "render" in a Twig template is a completely new Request that goes through the whole Symfony lifecycle.
I am writing a common Razor TBB, which will use in Component Template and Page template as per my requirement.
So that, I need a Page and Component object in the razor TBB according to applying TBB on Page Template and component Template
My requirement to display/use the metadata field values from Page/Component in specific area of the page.
That's why, i want to access metadata values by the object but unable to get the object,
Please also follow-up my comments with Frank.
Can any one suggest me?
Did you have a look at the (remarkably helpful) documentation that is available for the Razor mediator?
http://code.google.com/p/razor-mediator-4-tridion/
http://code.google.com/p/razor-mediator-4-tridion/downloads/detail?name=RazorMediatorDocumentation_v1.2.docx
These are full of examples that access the current Component and the Page. Just my 10 second search gives these fragments:
<body class=”#Page.Metadata.BodyClass”>
<div class=”#Component.Fields.NewsStyle”>
<img src=”#Fields.HeaderImage.ID” alt=”#Fields.HeaderImage.AltText” />
Edit: I see you added some more details in your follow-up comment. You might want to do as Bart suggests and add those details to the question. In the meantime, I'll spend a few more minutes searching the documentation for you.
The official documentation (the Word document I linked above), contains this example that seems to process metadata:
#foreach (var keyword in Publication.MetaData.SomeKeywordFields) {
<li>#keyword.Title (#keyword.Id)</li>
}
The output of the Razor template will become the Output item in the Package. So it doesn't make any sense to use a Razor mediator to process the Output item. For that you might as well use a regular C# (fragment or assembly) TBB.
Another edit: It seems that the Razor mediator's implicit Fields variable always maps to the Component fields and the Metadata variable always maps to the Component's meatadata fields. I've linked the above names to the relevant fragments on Google code for your convenience.
So you seem to have two options:
detect whether you are in Page or Component (e.g. by looking at whether the implicit Page variable is null or not) and then have conditional expressions everywhere (isInPage ? Page.Metadata : Metadata)
fix this limitation of the Razor mediator code yourself or hire someone to fix it for you