Symfony2 - add flashbag message on entity operations - symfony

I'm working on a Symfony2.3.6 project, and it works good. I've made a backend side with a few CRUD for some Entities, and it also work good.
Now what I want to do is notify an user when an operation is made on an entity. So I want to notify when an entity is saved, updated or deleted, as Symfony1.4 made.
I was in doubt where to put the flashbag message, if in the entity or in the controller or even with events?!
Which is the right place where I can put this kind of feature, and how I can do it?
Thanks...

The documentation describes perfectly how to store and display these messages in your controller.
In your controller
public function updateAction(Request $request)
{
$form = $this->createForm(...);
$form->handleRequest($request);
if ($form->isValid()) {
// do some sort of processing
$this->get('session')->getFlashBag()->add(
'notice',
'Your changes were saved!'
);
return $this->redirect($this->generateUrl(...));
}
return $this->render(...);
}
In your Twig template
% for flashMessage in app.session.flashbag.get('notice') %}
<div class="flash-notice">
{{ flashMessage }}
</div>
{% endfor %}
You can use different flashbags for other messages, for example an error :
In your controller
[...]
$this->get('session')->getFlashBag()->add(
'delete',
'The entity has been deleted!'
);
[...]
In your Twig template
% for flashMessage in app.session.flashbag.get('delete') %}
<div class="flash-notice delete">
{{ flashMessage }}
</div>
{% endfor %}
Use CSS to style the delete class.

Related

How to access an entity within an entity in twig

In my controller, I return the entity so that my twig template can use it like so:
return $this->render('review/index.html.twig',[
"list" => $applications
]);
$applications is a query that returns the object I'm looking for:
$applications = $this->getDoctrine()->getRepository(ApplicationQueue::class)->findBy(
array("assignment_mod_user" => $this->getUser()->getId())
);
And within my twig, I use the dump function to see if it's retrieving what I'm looking for. This is what is returned:
As you can see, there are two entities associated to this entity. In twig when I tired to do this, it failed to retrieve the data within:
{% for application in list %}
{{application.application.[whateverhere]}}
{% endfor %}
How do I access the entities within an entity in twig when the data is already being pushed? The current output returns the error of:
Neither the property "application" nor one of the methods "application()", "getapplication()"/"isapplication()"/"hasapplication()" or "__call()" exist and have public access in class "App\Entity\ApplicationQueue".
You need to add an accessor to get the value of $application. This property is currently not accessible from outside the ApplicationQueue class.
class ApplicationQueue
{
protected $application;
public function getApplication(): CreatorApplication
{
return $this->application;
}
}
You have tu put double curly braces around your variable, like that :
{{ application.application.name }}
Look at the twig doc :
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}

Should I repack the fetched entity object before passing it to the twig template?

I'm using Symfony 4. I have a fetched object $user that has relationships with other entities. I have setup all the getter so I can get other information from that $user object.
$user = $em->getDoctrine()->getRepository(User::class)->find($id);
$usergroup = $user->getGroup()->getName();
What I'll do is to create a new object for repacking the information I needs from the $user object before passing it to the template.
# controller
$repack_user = new \stdClass();
$repack_user->id = $user->getId();
$user_friends = $user->getFrends();
$friends_gf = [];
foreach($user_friends as $friend) {
$friends_gf[] = $friend->getGirlfriend()->getName();
}
$repack_user->friends_gf = $friends_gf;
return $this->render("home.html.twig", ['user' => $repack_user]);
And then in the template, I unpacked it with similar procedures.
# twig template
{{ user.id }}
{% for gf in user.friends_gf %}
{{ gf }}
{% endfor %}
But since I can also call entity function inside twig, I can skip all the whole repacking in the controller and pass the $user object right into the template.
# skipped repack in controller
# twig template
{{ user.getID() }}
{% for friend in user.getfriends %}
{{ friend.getGirlfriend().getName() }}
{% endfor %}
The first way is kind of redundant because I have to do it twice. The second way is kind of hard to read. So which one is the more reasonable approach? What is the common practice for this?
Common practice is definitely to hand over your entity graph directly to the view.
Why do you think your second example is harder to read?
If you don't want those chained calls in the template you might want to consider adding another getter in your User entity. Something like:
public function getNamesOfFriendsGirlfriends()
{
$gfNames = [];
foreach($this->getFriends() as $friend) {
$gfNames[] = $friend->getGirlfriend()->getName();
}
return $gfNames;
}
And then call that in your template:
{% for gfName in user.namesOfFriendsGirlfriends %}
{{ gfName }}
{% endfor %}
And if you need many of these helpers and don't want to spoil your nice and clean entities you might want to consider wrapping them in a Decorator object before using it in the view layer.

twig inheritance and symfony2 controller variables

Im trying my first project using symfony2 + twig. I created basic twig template with defined blocks. It basically looks like this
{% block content %}
some content...
{% endblock %}
{% block footer %}
{{ footer.content}}
{% endblock %}
I want footer to be same for all pages. Footer is loaded from DB and its set in controller. I wanted inherit from template described above to other pages but I have to always set footer in controller otherwise variable is not defined.
My questions is if exists any 'nice' way how to set footer variable for multiple templates inherited from parent template?
The solution : Embedding Controllers
In some cases, you need to do more than include a simple template.
Suppose you have a sidebar in your layout that contains the three most
recent articles. Retrieving the three articles may include querying
the database or performing other heavy logic that can't be done from
within a template.
The solution is to simply embed the result of an entire controller
from your template. First, create a controller that renders a certain
number of recent articles:
Controller
// src/AppBundle/Controller/ArticleController.php
namespace AppBundle\Controller;
// ...
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// make a database call or other logic
// to get the "$max" most recent articles
$articles = ...;
return $this->render(
'article/recent_list.html.twig',
array('articles' => $articles)
);
}
}
View
{# app/Resources/views/article/recent_list.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}
Layout
{# app/Resources/views/base.html.twig #}
{# ... #}
<div id="sidebar">
{{ render(controller(
'AppBundle:Article:recentArticles',
{ 'max': 3 }
)) }}
</div>
you can do with one of the following, 'write a custom Twig Extension'
<?php
namespace AppBundle\Extension;
class MyTwigExtension extends \Twig_Extension
{
private $em;
private $conn;
public function __construct(\Doctrine\ORM\EntityManager $em) {
$this->em = $em;
$this->conn = $em->getConnection();
}
public function getFunctions()
{
return array(
'users' => new \Twig_Function_Method($this, 'getUsers'),
);
}
public function getUsers()
{
$sql = "SELECT * FROM users ORDER BY accountname";
return $this->conn->fetchAll($sql);
}
public function getName()
{
return 'smproc4_twig_extension';
}
}
Register an Extension as a Service
services:
my.twig.extension:
class: AppBundle\Extension\MyTwigExtension
tags:
- { name: twig.extension }
arguments:
em: "#doctrine.orm.entity_manager"
Using the custom Extension
Hello {{ name }}!
<ul>
{% for user in users() %}
<li>{{ user.accountname }}</li>
{% endfor %}
</ul>
have a look at the
How to Write a custom Twig Extension
Creating an Extension

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 %}

symfony2 - Change layout at runtime

Is possible in Symfony2 to change the layout (using twig) at runtime?
The layout should change based on a record fetched from a database, so I am thinking to implement a sort of LayoutManager that decides what layout to load for every request, but still I can't find a way to do this at runtime.
Twig template
{% extends myLayoutName %}
{% block my_block_with_content %}
Some content here
{% endblock %}
Controller
public function myAction() {
$layoutName = '...'; // Calculate layout name
return $this->render(
'AcmeAcmeBundle::template_name.html.twig',
['myLayoutName' => $layoutName]
);
}

Resources