Permanently extend symfony twig template - symfony

In my symfony2 application I created a dashboard which currently consists of many navigation elements.
Now I am trying to split those elements into several bundles.
This is the code I have:
{# app/Resources/views/base.html.twig #}
{# ... #}
{% block body %} {% endblock %}
{# ... #}
Then in the ProfileBundle:
{# src/MyApp/ProfileBundle/Resources/views/Dashboard/index.html.twig #}
{% block body %}
<p>Heading</p>
<ul>
{% block dashboardNavi %} {% endblock %}
</ul>
{% block %}
edit: The controller:
class DashboardController extends Controller
{
public function indexAction()
{
return $this->render('MyAppProfileBundle:Dashboard:index.html.twig', array());
}
}
The routing:
pricecalc_profile_dashboad_security:
pattern: /dashboard
defaults: {_controller: MyAppProfileBundle:Dashboard:index }
That template is rendered correctly, when my route "/dashboard" is loaded.
What I now'd like to do, is extend that dashboardNavi-Block in multiple Bundles without changing the route from my ProfileBundle.
Each of those Bundles brings it`s own routes and controllers for custom actions, but all bundles should extend that one block to add links for their custom actions to the dashboard screen.
What I have so far is:
{# src/MyApp/ProfileNewsletterBundle/Resources/views/Dashboard/indexNewsletter.html.twig #}
{% extends 'MyAppProfileBundle:Dashboard:index.html.twig' %}
{% block dashboardNavi %}
{{ parent() }}
<li>Test</li>
{% endblock %}
but that template is never rendered.
edit 2:
Maybe my understanding of how symfony is working in terms of template inheritance is kind of wrong. I'll specify what I am trying to do.
I got one Bundle (DashboardBundle) which consists of an own route, controller, view etc. The view contains two blocks - like navigation and dashboard.
Now, I would like to have those two blocks extended by some other Bundles - just adding new navigation items and shortcuts on that dashboard and navigation block.
I would like to do those enhancements without modifying my Dashboard-Bundle - if that is possible at all.
When finished, I will have 16 Bundles, each providing own functionality in own Controllers - and they should just be linked on that dashboard.
Is it possible to have the dashboard-view extended that way without modifying the view itself?

I finally managed to fix that after having understood how symfony works in extending controllers and views.
I added a new Controller:
{# src/MyApp/ProfileNewsletterBundle/Controllers/DashboardController.php #}
class DashboardController extends Controller {
public function indexAction()
{
return $this->render('ProfileNewsletterBundle:Dashboard:index.html.twig', array());
}
}
modified the bundle ProfileNewsletterBundle to let the method getParent return ProfileBundle,
and modified the view:
{% extends 'ProfileBundle:Dashboard:index.html.twig' %}
{% block dashboardNavi %}
<li>Test</li>
{% endblock %}
That seems to work fine so far.
Thank you all for spending your time on that.

Related

Using raw Cypher to query Neo4j in Symfony

I am trying to go throught this tutorial: http://www.sitepoint.com/adding-social-network-features-php-app-neo4j/ But using the Symfony Framework instead of Silex.
I have been able to set up Neo4j to run with Symfony and am able to right user data to the graph. Now I would like to display all user email addresses in a list. I have taken this script:
public function home(Application $application, Request $request)
{
$neo = $application['neo'];
$q = 'MATCH (user:User) RETURN user';
$result = $neo->sendCypherQuery($q)->getResult();
$users = $result->get('user');
return $application['twig']->render('index.html.twig', array(
'users' => $users
));
}
And adapted it to read:
public function showUsersAction()
{
$em = $this->container->get('neo4j.manager');
$query = 'MATCH (n:`User`) RETURN n';
$users = $em->cypherQuery($query);
//print_r($users);
return $this->render('UserBundle:Account:showUsers.html.twig', array('users' =>$users));
}
And The twig looks as follows:
{% extends '::base.html.twig' %}
{% block content %}
<h1>get all users:</h1>
<ul>
{% for user in users %}
<li>{{ user.property('email') }}</li>
{% endfor %}
</ul>
{% endblock %}
But something in the twig is wrong, im getting the error:
Method "property" for object "Everyman\Neo4j\Query\Row" does not exist in UserBundle:Account:showUsers.html.twig at line 6
The problem was found in the syntax of the twig file. After consulting this page: https://github.com/jadell/neo4jphp/wiki/Cypher-and-gremlin-queries it became clear, that I had to include user['n'] in my twig template. The twig template now looks as such:
{% extends '::base.html.twig' %}
{% block content %}
<h1>get all users:</h1>
<ul>
{% for user in users %}
<li>{{ user['n'].getProperty('email') }}</li>
{% endfor %}
</ul>
{% endblock %}
I'm the author of the article you mentioned. The thing is that you use a different neo4j library than the one used in the article, hence neoclient, so the methods used in the article are different than the methods provided with neo4jphp.
As NeoClient uses heavily the Symfony components, integrating it in Symfony is really easy, you just need to override the DI. Example here : https://github.com/graphaware/GithubNeo4j/tree/master/src/GraphAware/Neo4jBundle
You'll then be able to use the methods illustrated in the 3 articles I wrote on Sitepoint.
So your problem with the twig template is that he doesn't find the getProperty method of the node object class, which is normal as neo4jphp returns Row object classes.
If you switch back to neoclient, as in the article, in the Twig template you can just write :
{% for user in users %}
<li>{{ user.getProperty('email') }}</li>
{% endfor %}

Overriding FOSUserBundle Login Form

Im following the documentation here:https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/overriding_templates.rst
I chose to Create A Child Bundle And Override Template so in my bundle I have
class MyBundle extends Bundle
{
//declare bundle as a child of the FOSUserBundle so we can override the parent bundle's templates
public function getParent()
{
return 'FOSUserBundle';
}
}
In my bundle I have added the following files
MyBundle
\Resources
\views
\Security
login.html.twig
Matching the FOS bundle structure as mentioned in the documentation
login.html.twig
{% extends 'AnotherBundle::layout.html.twig' %}
{% block title %}Log In{% endblock %}
{% block content %}
{% block fos_user_content %}{% endblock %}
{% endblock %}
When I go to the login page my header loads fine but there's no login form, what am doing wrong?
Because you didn't write the code that render the login form.
open /vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/views/Security/login.html.twig, copy the code in the fos_user_content block to your custom login.html.twig, reload the page, then you'll see the form.
If you want to customize the form, rewrite the code you've copied.
When you have nested blocks you need to tell explicitly which block you're closing. So, try this:
{% block content %}
{% block fos_user_content %}{% endblock fos_user_content %}
{% endblock content %}

SonataAdmin: replace ID in breadcrumbs

How can I replace Object's ID in SonataAdmin breadcrumbs by some other text?
If I set __toString() in my document, it works only for editing. When I attempt to create new record, there is something like MyDocument:0000000000e09f5c000000006a48ef49 in the last breadcumb.
I'm searching for a method which allows me to set some text as the last breadcump if Document::toString() returns null.
This behaviour is implemented directly in the entity:
public function __toString()
{
return $this->getFoo() ? : '-';
}
Bundles are using variants of this, including return (string)$this->getFoo(); or $this->getFoo() ? : 'n/a'; etc.
Related question: toString method for SonataAdminBundle Listing in Symfony2
BTW something cool to know, you can completely customize the breadcrumb via a Twig template:
{% block sonata_breadcrumb %}
{% set _breadcrumb %}
<li>Home</li>
<li>Library</li>
<li class="active">Data</li>
{% endset %}
{{ parent() }}
{% endblock %}

Symfony Twig path() not working when using render

I have a layout that includes some chuck of code form a controller called "Layout"
In the header section I have:
{% block accessinfo %} {% render "/layout/accessinfo" %} {% endblock %}
It works pretty fine, the view file content is:
{% extends '::layout.html.twig' %}
{% block body %}
{% if( is_logged == 0 ) %}
Welcome, access your <a id="accessAccount" title="Access your account">here</a>.
{% else %}
Hi, <b><em> {{ is_logged_user_name }}</em></b>, <a id="doLogout" href="javascript:void;">(Logout)</a>.
<i class="icon-user"></i> Your Account
{% endif %}
{% endblock %}
As one can figure out, path('account/manage') points to the Route named 'account/manage', but it's not returning the fully qualified URL to my project.
It returns:
http://localhost.project/account/manage
where it should be:
http://localhost.project/web/app_dev.php/account/manage
NOTE: I have path() all around my template files and they work like a charm.
IMPORTANT: I found out that when I call REQUEST URI inside the action method:
$this->get('request')->server->get('REQUEST_URI')
PHP will return the URL called by the render, in this case is:
/layout/accessinfo
Perhaps I'm not fully understanding your issue but it seems like you missunderstood the use of the path() and render() functions.
First of all if you like to render a controller and you follow the documentation here you would do it like this...
{{ render(controller('AcmeArticleBundle:Article:recentArticles') }}
{# with some parameters #}
{{ render(controller('AcmeArticleBundle:Article:recentArticles', {
'max': 3
})) }}
This assumes you're using Symfony >= 2.2. This follows the bundle:controller:action pattern, which is called Controller Naming Pattern
For a normal use of the path() function you would always use the name of the route and not a hardcoded URL (as it seems like you're passing in URLs and not route names?)
Let's say your route is called accountmanager, your routing.yml should look like this example
# app/config/routing.yml
accountmanager:
path: /account/manage
defaults: { _controller:YourBundleName:YourControllerName:ControllerAction }
And with that in your routing.yml in twig the use of path() is simply achieved by writing {{ path('accountmanager') }}
See the documentation on this topic. Using the name of the route and not a URL pattern ensures that you're getting to the right page which also includes your environment settings (like app_dev.php for your dev environment)

symfony2 - twig - how to render a twig template from inside a twig template

I have a xxx.html.twig file which shows a page, but when I want to refresh the page with different data and just update it with new data, I have a select and a submit button for it.
The thing is that I don't know how do I call an action in the controller which I pass parameters to from my twig and call for new data and then I render the same twig template again with new parameters.
How do I do so?
Here are a few different ways:
{{ render(app.request.baseUrl ~ '/helper/test', {"hostid2": hostid } ) }}
or
{% include 'MyCoreBundle:Helper:test.html.twig' with {"hostid2": hostid } only %}
or
{% render controller("MyCoreBundle:Helper:test", {'hostid2': hostid}) %}
Symfony 2.1:
{% render 'YourBundle:YourController:yourAction' with {'var': value} %}
Symfony 2.6+:
{{ render(controller('YourBundle:YourController:yourAction', {'var': value})) }}
And, of course, read the documentation.
I think some parts are depricated here.
To make the include work in latest Symfony 3.1.10, I solved it like this:
{% extends 'base.html.twig' %}
{% block body %}
{{ include('AppBundle:Default:inner_content.html.twig') }}
{% endblock %}
Note: include() with parentheses.
Then all the variables are included from the parent template. If you like to restrict some variables in the child template, you use with ... only (look over)

Resources