Twig/PHP Displaying data by the author - 2 tables (Symfony2) - php

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

Related

How to send repeating data from all symfony's controller routes to twig?

Here is the use case:
I have a simple navbar with a simple dropdown menu.
I have a list of cities in the Database with the name of the cities. I want to get all the cities and add them to the dropdown menu.
Simple way is to send additional data, like array of cities to twig. But i have a lot of routes and i don't think that this is good to repeat fetching every time. This is not good, right?
public function testpageAction(){
$em = $this->getDoctrine()->getEntityManager();
$cities = $em->getRepository('AppBundle:City')->findAll();
return $this->render('appviews/testpage.html.twig',array('cities'=>$cities));
}
There is a method to inject variable into all templates, but this is static data, so it is not the solution.
There is a plan in my head to make a method that will be run before controller methods and it will send the data to those methods. But it does not seem good and I believe there is a better way to solve it.
You can have a CityController or a BaseController with method getAllCities, and embed this controller in your base template.
From Symfony docs :
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.
Something like this:
class BaseController extends Controller
{
public function getAllCities()
{
// make a database call or other logic
// to get all cities
$cities = ...;
return $this->render(
'cities.html.twig',
array('cities' => $cities)
);
}
}
Your cities template:
{# app/Resources/views/cities.html.twig #}
{% for city in cities %}
{{ city}}
{% endfor %}
And in your base template:
{# app/Resources/views/base.html.twig #}
<div id="sidebar">
{{ render(controller(
'AppBundle:Base:getAllCities'
)) }}
</div>

Symfony2 fos login, register and forgot password in one view

I need to apply a purchased template to our dashboard. In this template, the login, register and forgot password forms are all under the same view, and switching between them using simple JQuery.
I have been looking for a nice, not-too-flashy way of combining all three forms into one, but I came up empty.
My standing options (as I see them), and why I don't like any of them:
Take the views from the fos bundle, copy them to /app/Resources/FOSUserBundle/views/, remove the {% extend %} part and {% include %} them in my own login view. Reason for dislike: to me this looks a little like a quick-n-dirty fix - "that part's not working? Let's break it off!" :)
Extend the fos bundle, accept an extra parameter in the LoginAction and RegisterAction, use {% render %} with parameters in my own login view. Reason for dislike: extending a whole bundle and modifying two different controllers just to change the way it renders feels like bad MVC.
XHR load everything. Reason for dislike: this approach makes sense when using inner pages, but for pages that reload anyway it just doesn't make sense.
TL;DR version: I'm looking for a non-hack way of including the login, register and forgot password form in one page.
Any help would be greatly appreciated!
I found a solution with which I am comfortable with for my current project. The advantages and disadvantages of the proposed solution upfront:
Advantages:
few LOC to implement
FOSUserBundle update proof (does not override the view scripts*)
Disadvantages:
performance overhead due to subrequests
only forms can be displayed, form submission (and subsequently error handling upon submission) will always go to the pages provided by FOSUserBundle
still feels like a quick-n-dirty fix, but better than other options
* only needs to override the layout.html.twig file
With that being said, here is what I have done:
Render the form in your template
Use embedded controllers to render the forms you need:
<div>
<h2>Login</h2>
{{ render(controller('FOSUserBundle:Security:login', { embeddedForm: true})) }}
</div>
<div>
<h2>Reset</h2>
{{ render(controller('FOSUserBundle:Resetting:request', { embeddedForm: true})) }}
</div>
Override FOSUserBundle layout
As I use the routes provided by the bundle, I had to override the FOSUserBundle layout template file to extend the standard layout of my application. As the overriden FOSUserBundle layout file extends the main applications layout file the layout would be repeated for each call {{ render ... }}. To prevent that, we need to dynamically disarm the extended layout file. Here is what the overriden layout file looks like:
{# app/Resources/FOSUserBundle/views/layout.html.twig #}
{% if app.request.get('embeddedForm') %}
{% set layout = 'AcmeBundle::layout-content.html.twig' %}
{% else %}
{% set layout = 'AcmeBundle::layout.html.twig' %}
{% endif %}
{% extends layout %}
{% block content %}
{% block fos_user_content %}{% endblock %}
{% endblock %}
Create the AcmeBundle::layout-content.html.twig file
This layout should only render the content block of the FOSUserBundle view scripts and is such short and simple:
{# src/Acme/DemoBundle/Resources/views/layout-content.html.twig #}
{% block content %}{% endblock %}
Now the forms will render nicely with all dependencies (CSRF and so forth). Submitting the form will however take you to the FOSUserBundle actions.
Alternative solution:
This answer describes how to manually implement the forms and link them to the FOSUserBundle controller.

twig: pass variables from view to controller

Setup:
Twig 1.13.1
PHP 5.4.3
Problem:
I have 10,000 articles in my DB. I need a way to only allow X amount of stories to display on the page. I know I can limit the number in the controller before i call the template but that number will be different depending on the template that is used. I will have one controller to handles all the articles. I need a way to pass the number from the template to the controller to limit the array. I don't want to pull down all 10,000 articles then use twig "slice" filter/func.
I know in django you can use the below. That will only load the top 3 stories.
{% get_latest_stories 3 sports as story_list %}
{% for story in story_list %}
{{ story.title }}
{% endfor %}
Here is my current files.
Controller
<?php
$stories = news_stories::getStories("sports",5); //getStories(section,limit);
?>
<?=$twig->render("storyList.html", array('stories' => $stories))?>
View/Template
{% for story in story_list %}
{{ story.title }}
{% endfor %}
Summary
I would like a way to pass a number from the template to the controller so that i can limit the about of rows returned from the DB
Logically speaking, it would be impossible for the view to pass something to controller since the view is being processed at the end of the stack, after everything else.
You can however, pass a function into the view. You would want to create some sort of getViewStories function that you can access from your twig template. Since you have this already in your controller:
<?php
$stories = news_stories::getStories("sports",5); //getStories(section,limit);
?>
<?=$twig->render("storyList.html", array('stories' => $stories))?>
All you would need to do is change it around a bit, like this:
<?php
$function = new Twig_SimpleFunction('getViewStories', function (section, limit) {
return news_stories::getStories(section,limit);
});
$twig->addFunction($function);
?>
<?=$twig->render("storyList.html")?>
Now, from inside your template, you can call this function, like so:
{% set story_list = getViewStories('sports',5) %}
{% for story in story_list %}
{{ story.title }}
{% endfor %}
And change the getViewStories parameters around in each template.
And while you can use the slice filter, I would recommend against it in your case, as it makes for unnecessarily long database calls. This is the most optimized method (that I'm aware of).
You want to use the slice filter I think this should work for you
http://twig.sensiolabs.org/doc/filters/slice.html
{% for story in story_list|slice(1,5) %}
{{ story.title }}
{% endfor %}
should only return the elements 1 - > 5 of the loop then break loop. You can also do it like this
{% for story in story_list|[start:5] %}
{{ story.title }}
{% endfor %}
Disclaimer: I've never actually used twig though this was just a quick browse through its docs
You can embedded controllers ( or render other urls ) from inside a twig template. This means you could have a main layout template for your site, and keep your storyList.html template very plain - just to iterate over the stories and any markup they might need.
In your main layout you would render the action for the news stories:
<div id="stories">
{% render url('...') with { section: 'sports', limit: 5}, {'standalone': 'js'} %}
</div>
This way requires hindclude.js included on your page. Check these docs. If you are also using symfony ( you mention MVC but not the framework ) - even better. Scroll up a bit and look at Embedded controllers.
Otherwise, I believe this way essentially uses ajax.

How to get at runtime the route name in Symfony2 when using the yaml routes description?

Here you can find my n-th question on Symfony2.
I'm working with a pagination bundle that uses the route name provided in the routing.yml file.
From my perspective, this approach is not flexible and lead to a dirty code, since if I change the name of the route, then I have to look at all the Twig templates or PHP files to update the route name. This is ok for small Web applications, but will provide such a bug for larger applications and also need an high burden for the developer.
So, I was wondering to pass a string variable x to the Pager object provided by the above mentioned bundle. The string x should be initialized within the controller and has to provide the desired route name as given in the routing.yml file.
Let me give an example. The routing file is the following:
//routing.yml
AcmeTestBundle_listall:
pattern: /test/page/{page}
defaults: { _controller: AcmeTestBundle:List:listall, page: 1 }
requirements:
page: \d+
Then the related controller is:
//use something....
class ListController extends Controller
{
public function exampleAction($page)
{
$array = range(1, 100);
$adapter = new ArrayAdapter($array);
$pager = new Pager($adapter, array('page' => $page, 'limit' => 25));
return array('pager' => $pager);
}
}
Then, in the twig template, the $pager receives the route name that refer to the above bundle
{% if pager.isPaginable %}
{{ paginate(pager, 'AcmeTestBundle_listall') }}
{% endif %}
{% for item in pager.getResults %}
<p>{{ item }}</p>
{% endfor %}
Any idea of how to get the 'AcmeTestBundle_listall' string value at runtime just inside the controller?
You can use the app global variable that is available in twig to get the current route from the request.
{% if pager.isPaginable %}
{{ paginate(pager, app.request.get('_route') }}
{% endif %}
More about app here and here.

Symfony 2 - Accessing Hierarchical Roles in a twig template

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!!

Categories