In my template, I need to know if a user has certain role to display things according to it. So far, I've implemented a little function in my user class:
public function hasRole($role) {
$roles = array();
foreach ($this->getRoles() as $rol) {
$roles[] = $rol->getRole();
}
return in_array($role, $roles);
}
which tells me if this user has the role specified by the string passed as a parameter. This work and can be called from a twig template, but doesn't allow me to know anything about the roles hierarchy. Is there a way to access the role hierarchy from a controller? and directly from a twig template? I've looked through the official docs and didn't find anything about.
You can check the roles in twig templete by using below code,It explains that if the current user has the below role,then show something
{% if is_granted('ROLE_ADMIN') %}
//show things related to admin role
{%else if is_granted('ROLE_USER')%}
//show things related to user role
{% endif %}
Hope this helps you.
Happy Coding!!
Related
I'm making a presonification menu on my navbar so that i can access from any page i don't want to use the getRepository inside a controller and pass to the frontend.
Is there a way on symfony 4 that I can get all the users from my database and call it to front? like the app.session.user (to get info on the logged user) for example?
Thanks!
As Cerad mentioned, you can embed a Controller in your templates : https://symfony.com/doc/current/templates.html#embedding-controllers
Instead of doing {% include 'base/navbar.html.twig' %} you will do
In your template :
{{ render(controller('App\\Controller\\BaseController::renderNavbar')) }}
In BaseController
public function renderNavbar(UserRepository $userRepository) {
return $this->render('base/navbar.html.twig', [
'activeUsers' => $userRepository->findActiveUsers(),
]);
}
It's not a route, it's simply a function that renders html, so you don't need to add annotations.
I have a many many to many relation in Sonata (two one to many relations to be exact), Brand and Retailer.
In the Brand admin Edit page, I want to display all the retailers as a list (so just a read only version), instead of having the normal edit (at the moment, on this brand edit page, I can manage the relationship between this brand and retailers - add a new one, delete an existing one).
I tried to explore two routes so far:
Edit page will load a custom twig
Using a custom field type for this field only
My issue is, with both options, I didn't manage to get to a solution
So here is what I have done:
1 - Loading a custom edit twig:
services:
xx_brand.admin.brand_brand:
calls:
- [ setTemplate, [edit, xxBrandBundle:Admin:base_edit.html.twig]]
On this case, base_edit is an exact copy of the sonata base_edit, but it loads my custom base_edit_form:
{% use 'xxBrandBundle:Admin:base_edit_form.html.twig' with form as parentForm %}
From here I can exclude the default rendering of the retailers, but can't find a way to then render it as I want, as I am not sure how the retailers entity is managed here:
{% if admin.formfielddescriptions[field_name] is defined and field_name != 'retailers' %}
{{ form_row(form[field_name])}}
{% else %}
<ul>
<li>retailer1</li>
<li>retailer2</li>
</ul>
{% endif %}
2 - For the approach of a custom field type, I tried to follow the documentation
Creating the Bundle/Form/Type/ListType.php
Creating the /BrandBundle/Resources/views/form/list.html.twig
Using the ListType in configureFormFields:
use XX\BrandBundle\Form\Type\ListType;
...
->add('retailers', 'ListType');
But I then get an error XX\BrandBundle\Form\Type\ListType
So basically, because I couldn't get it to work, are any of these two options good to solve my issue ?
If so, could anyone please advice on what I am missing there ??
Any help will be very much appreciated :)
You can use sonata_type_model_list: https://sonata-project.org/bundles/doctrine-orm-admin/master/doc/reference/form_field_definition.html#example
Like so:
class BrandAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('retailer', 'sonata_type_model_list', array(
'btn_add' => false,
'btn_delete' => false,
));
}
}
I'm relatively new to Symfony, and have an issue I just can't fix.
The for in the twig view is for "enquiry" entity, trying to pass the author from a "post" entity through the enquire controller to the view. Hope that makes sense.
Controller:
public function viewEnquireAction($id )
{
$em = $this->getDoctrine()->getManager();
$enquire = $em->getRepository('Bundle:Enquire')->find($id);
$project = new Post();
$author = $project->getAuthor(); //mutator from Post entity
return $this->render('Bundle:Page:staff.html.twig', ['enquire' => $enquire, 'author' => $author]);
}
Twig:
{% for project in enquire %}
{% if app.user == author.username %} //issue here.
//return data will go here
{% endif %}
{% endfor %}
In essence, I'm trying to display data from the database only if the user who is logged in the author.
Thanks.
The code #davidvelilla looping over projects in enquire make more sens, thend check if the project author is the same as the logged in user, that will show only the projects of the connected user, other thing you can do is using a repository or relation between either projects and user or user and author which make sens. then use a function in the repository to get projects by user_id, which you will not have to loop over all projects which is bad practice and take load of resources.
For example you have 1000 projects in database, you have a user with 1 project. Why loading from Database 1000 projects and loop them to check if user == author while you can do $repository->getProjectsById($user->getId()); // which load only the $projects needed(1) and no need for twig check. only data display.
A more general attempt that will let you re-use your "Author is allowed to see" code everywhere in your app is using voters:
http://symfony.com/doc/current/cookbook/security/voters_data_permission.html
You can keep your existing loop but instead of comparing the users directly you can then use something like this:
{% if is_granted("LIST", project) %}
show project
{% endif %}
As mentioned one advantage is that you can also use this anywhere else for instance inside a controller:
public function editAction(Project $project)
{
if (!$this->get('security.context')->isGranted('LIST', $project)) {
// Throw access denied exception
}
// ...
}
And you can add additional rules besides the "User is Author" check.
Maybe you want users with ROLE_SUPER_ADMIN to always see everything.
Just add this to your voter class.
Your variable $project has just been instanciated and will contain no information about the $author except for the default one that you defined in Post.php
If your $enquire entity has the right mapping information, ONLY pass that variable to the template, and them I presume you are trying to get the author of all of the projects of that enquire. Something like this might give you a clue:
public function viewEnquireAction($id )
{
$em = $this->getDoctrine()->getManager();
$enquire = $em->getRepository('Bundle:Enquire')->find($id);
return $this->render('Bundle:Page:staff.html.twig', ['enquire' => $enquire]);
}
{% for project in enquire.projects %}
{% if app.user == project.author.username %} //issue here.
//return data will go here
{% endif %}
{% endfor %}
I'm new to Symfony2 and after several tutorials decided to migrate on of my projects to this framework.
In my project I've got few modules that got different templates but share the same data. For example, menu: menu items are lifted from database and used by standart website menu and also by footer menu. I've read that the best practice is to create controller for such task, and embed it straight into main layout, like this:
//Controller
class PageComponentController extends Controller
{
public function menuAction()
{
//get menu items from database...
$menu_items = ...
return $this->render('MyProjectBundle:PageComponent:menu.html.twig', [
'menu_items' => $menu_items
]);
}
}
//Template
{% block body %}
{% render controller('MyProjectBundle:PageComponent:menu', { 'current_page': current_page }) %}
{% endblock %}
But then I need to pass those $menu_items in footer as well, and render footer from the footerAction() in PageComponentController.
I know that there are ways to do it, but what is the best way to share such mutual data between different embedded controllers in Symfony2?
UPDATE (solution based on oligan's answer):
Service container scope is both accessible from main controller (responsible for page rendering) and from embedded controllers. So it would be pretty clean and dry to write service class with getMenu() method that fetches data from DB, and outputMenu() method that returns existing data. Then, service data is set from main controller and could be retrieved from service container in embedded controllers.
I think the wisest is to use a service which would retrieve all the data you want http://symfony.com/doc/current/book/service_container.html (it's more or less like a controller but accessible everywhere )
To give you an idea what it is
class MenuService
{
public function getMyMenu()
{
... your code to get the menu
}
}
Then declare it in services.yml in your bundle
services:
getMenuService:
class: ..\...\...\MenuService
And then just use it in any controller by doing so
$menu = $this->container->get('getMenuService');
if you have to use template heritage then you can still access to an object in the parent template by doing
{% extends "...Bundle..:myMenu.html.twig" %}
{% set menu = myMenuTwigvar %}
and then myMenu for example menu.button1
My site I have some content can be voted (+/-). It is working fine now, when all content has its own voter.
Now I'm looking for a way to create a single voting bundle with a entity(votedModel,votedId, user, vote).
Basically the bundle is ready. My problem is how to use it. I'd like to be able to do something like:
class ... extends Controller {
function showAction(Request $request,$id) {
...
$voter=new Voter('myCOntentType',$id,$userid);
...
return $this->render('...', array('voter'=>$voter->getVoter(),...))
}
}
getVoter() would create the voter view.
but I'm stacked how exactly start. I tried to call for the other controller in this manner, but can't create the voter form.
It worked with $voter=$this->forward('VoterbundleNewAction', array('id=>$id,'user'=>$user)->getContent();But this wasn't I had in mind.
I think my approach is all wrong and I may need to do this as service. I cant find my way around.
You can use include or render in your twig template to get other templates' output. So you could create a template (say, voter.html.twig) that contains the HTML for your voting system, and in the Twig, in any place where you need a voter, you could use:
{% include "AcmeVoterBundle:Voter:voter.html.twig" %}
or
{% render "AcmeVoterBundle:Voter:voter" with {"item": item} %}
In the first example, you simply include another template (see also: http://symfony.com/doc/current/book/templating.html#including-other-templates), in the latter situation you actually execute another action method of a controller and the output of that is put into your current template (see also: http://symfony.com/doc/current/book/templating.html#embedding-controllers)