Call a controller function recursive from twig template - symfony

I have this function in a CategoryBundle:CategoryTreeBuilderController:
/**
* Get subcategories based on $parent_id parameter
*
* #Route("/category/tree/{parent_id}", name="category_tree", options={"expose"=true})
* #Method("GET")
*/
public function BuildCategoryTree($parent_id = null) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('CategoryBundle:Category')->findBy(array("parent" => $parent_id));
if (!$entity) {
throw $this->createNotFoundException('Unable to find Category entity.');
}
return $this->render("CategoryBundle:Default:index.html.twig", array('entities' => $entity));
}
I need to call this function recursively from my Twig template so every times the parent has children a kind of indentation occurs. I'm trying to implement a Tree behavior. I check this post but is not helpful at all, can any guide me on the right path?

I think better make a macro in template and iterate over all childs recursively.
Anyway, if you want to embed check this article. You can simple can call controller in template
{% for entity in entities %}
{{ render(controller('AcmeArticleBundle:Article:BuildCategoryTree', {
'parent_id': entity.id
})) }}
{% endfor %}

Related

How to solve passing IDs in URL with Symfony

i'm making an app to make appointments with doctors, and in the template of the patient I need to use the id of the entity doctor in the path because I use it in the code of my controller, and I still can't get the Id of the entity doctor because when i do {{ doctor.id }} nothing appears on my screen, the content is empty.
As a solution I used the email of the doctor in the URL but it is quite ugly.
This is the code of the controller
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, $id)
{
$patient = $this->getUser();
$medecin = $repoMedecin->findOneById($id);
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
here's the code of the element I use to click on it to redirect on the controller previously
<fieldset>
<h4><i class="fa fa-star"></i> Dr. {{ medecin.nom }} {{ medecin.prenom }}</h4>
</fieldset>
I expected to be able to use the id of the doctor in the URL but I think when you're logged as an entity you can't get the ID (not the id used to log in) of other users and the content of the variable is empty. But I would know if there's a solution to use the ID because passing the email into the url isn't the solution I want, and I had the same problem on other piece of code where the of other entity is empty.
Here's the code of the controller that send the datas to the view
/**
* #Route("/patient/medecins-favoris", name="meet_my_doc_afficher_medecin_favoris")
*/
public function afficherMedecinFavoris()
{
$patient = $this->getUser();
$medecins = $this->getUser()->getMedecinsFavoris();
return $this->Render('meet_my_doc/afficherLesMedecinsFavoris.html.twig', ['medecins' => $medecins]);
}
try this
{{path('meet_my_doc_ajouter_medecin_favoris',{(id): medecin.id})}}
I would do something like this, first controller to receive Medicine Object like this, in this case you dont need to use $medecin = $repoMedecin->findOneById($id);
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
in twig in both cases you do something like this
{% for medecin in medecins %}
<fieldset>
<h4>
<i class="fa fa-star"></i>
Dr. {{ medecin.nom }} {{ medecin.prenom }}
</h4>
</fieldset>
{% endfor %}
If your code is how you've pasted it, then I think the issue lies in a typo - in the afficherMedecinFavoris() action you pass medicins to the view, and inside the view you try to access the id of medicin. So either remove the extra s in the afficherMedecinFavoris() or add that extra s to the path id parameter value.
Aside from that, I believe that this should work (under the condition that the identity field for Medecin entity is id):
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager = $this->getDoctrine()->getManager();
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
To know how the Medecin entity is available as an argument for the controller read this
I'm sorry to post the answer just now, but this how it worked for me : medecin inherit from User, so when I'm creating a medecin, it is creating also an User, both with the same Id, and if I want to use the medecin object's Id, I need to edit the getId() and replace the code by parent::getId(), this is the only way I've found to solve this problem.

What is the better solution to get the Entity I want in a biderectional OneToMany relation?

I have one entity Article and an other entity Image with a bidrectional relation OneToMany and ManyToOne :
class Article
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Image", mappedBy="article")
*/
private $images;
}
class Image
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Article", inversedBy="images")
* #ORM\JoinColumn(nullable=true)
*/
private $article;
}
In my controller I use #paramconverter to get the article I want :
/**
* #Route("/blog/{slug}", name="article")
* #ParamConverter("article", class="AppBundle:Article")
*/
public function articleAction(Article $article)
{
return $this->render('default/article.html.twig', array(
'article' => $article,
));
}
Now my problem is that I want to identify the ONLY image with the attributes "main = true" in all the "article.images" I have.
What is the best solution?
In my wiew I can do somehting like this but it's not the best I think :
{% for image in article.images %}
{% if image.main %}
<img src="{{ asset( image.src ) }}" alt="{{ image.alt }}" title="{{ image.title }}">
{% endif %}
{% endfor %}
I'd like to use something like :
{{ article.mainImg }}
How can I achieve this please? And is this the best solution?
Doctrine provides a collection filter mechanism you could use to get the "main image":
public function articleAction(Article $article)
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq("main", true))
->setMaxResults(1);
$mainImg = $article->getImages()->matching($criteria)->first();
return $this->render('default/article.html.twig', array(
'article' => $article,
'mainImg' => $mainImg
));
}
More information on filtering doctrine collections: Filtering collections
I did not test the code myself, but it should convey the idea of how it can be done.

Custom Repository Classes in Symfony2

I am creating a Task Management Application Demo project just to get the hold of symfony2. So far I have finished CRUD operations.
This is my defaultcontroller. I am calling the getNumberOfTasks() here.
namespace TaskManagerBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use TaskManagerBundle\Entity\Projects;
use TaskManagerBundle\Form\ProjectType;
class DefaultController extends Controller
{
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('TestBundle:Projects')
->findAll();
$tasks = $em->getRepository('TestBundle:Projects')
->getNumberOfTasks();
return $this->render('TestBundle:Default:index.html.twig', [
'projects' => $entities,
'tasks' => $tasks,
]
);
}
This is my ProjectRepository, where I have defined the getNumberOfTasks method.
<?php
namespace TaskManagerBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;
use TaskManagerBundle\Entity\Projects;
use TaskManagerBundle\Entity\Tasks;
/**
* ProjectsRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ProjectsRepository extends EntityRepository
{
public $id;
public function getNumberOfTasks()
{
//$projects = new Projects();
$query = $this->getEntityManager()
->createQuery(
'SELECT p FROM TestBundle:Tasks p WHERE p.projects = :project_id'
)
->setParameter('project_id', $this->id )
->getResult();
return count($query);
}
}
This is my index.html.twig
{% for project in projects %}
<tr>
<td>
{% if project.completed == 0 %}
{{ tasks }}
{% else %}
Completed
{% endif %}
</td>
</tr>
{% endfor %}
I am trying to get the number of tasks for each project. How do I do that?
In yii2 I could just do $this->id but here I am not able to do that.
You don't need the "getNumberOfTasks" method at all.
What you should be doing is specifying the associations in your Doctrine Entities.
You can read about it here:
http://doctrine-orm.readthedocs.org/en/latest/reference/association-mapping.html
Based on your naming, I would suspect a OneToMany relationship from Project to Task, and a ManyToOne relationship from Task to Project.
When mapped correctly, in Twig you can then use:
{{ project.tasks|length }}
to get the number of tasks in a project. All you need to do is to pass the Project Entity to Twig and Symfony will handle the rest, including loading in all relationships.
This is the best way of doing what you are trying to do.

How to reload part of twig with ajax

I need to reload part of my html.twig:
in controller:
$entity = $em->getRepository('PublishDemandsBundle:Demands')->find($id);
In twig:
{% for n in entity %} {{ n.Id }} {% endfor %}.
i need how to reload $entity with ajax.Can someone help me and thanks.
You can do this with jQuery. I think the best way to do this (I think) is to have a method in your controller that do nothing but a findAll() on your Demands repo :
public function demandsAction()
{
$entity = $em->getRepository('PublishDemandsBundle:Demands')->findAll();
return $this->render('PublishDemandsBundle:Demands:liste.html.twig', array(
'entity' => $entity
));
}
Make sure this Action can be called by a route, let's say /ajax/demands/
Then, in your twig template, just do :
<div id="demands">
{{ render(controller("PublishDemandsBundle:MainController:demands")) }}
</div>
reload
With a bit of jQuery :
$('#reload').click(function() {
$.get("/ajax/demands", function( data ) {
$('#demands').html( data );
});
I haven't tested this yet, and it might be adapted to your case, but again, I would do it this way.

Load tagging in query with FPNTagBundle

Bundle docs explain how to load tagging of a simple object:
$this->tagManager->loadTagging($article);
But I need to load a list (ArrayCollection from doctrine query) of taggable resources with their tags. And later iterate over a collection in twig and print:
Object: tag1, tag2, tag..n
Old post, but hopefully this answer will help someone, as I ran into the same issue trying to implement the tagging bundle. The problem is that your entity will have a private or protected property for tags, but the way the documentation reads on the bundle, there is no association mapping for this property, and it's not an actual field (column). So trying to access the tags property or use the getTags method on your entity won't work, either in your controller or in Twig. I feel like the documentation on the bundle may be missing some mapping annotations on the tags property, but I haven't been able to narrow down exactly what it should be.
I ended up taking the approach a couple others recommended by looping thru my entities in the controller, and loading the tagging using the tagmanager for each entity. What I also did which turned out to be helpful was to add a setTags method to the entity that accepts an ArrayCollection, that way when looping thru the entities in your controller, you can set the Tags on each one, then access them in twig like you want to do. For example:
Add this setTags method to your entity:
/**
* #param ArrayCollection $tags
* #return $this
*/
public function setTags(ArrayCollection $tags)
{
$this->tags = $tags;
return $this;
}
This will allow you to set the tags property from your controller.
Then in your controller:
/**
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$posts = $em->getRepository('ContentBundle:Post')->findAll();
// here's the goods... loop thru each entity and set the tags
foreach ($posts as $post) {
$post->setTags($this->getTags($post));
}
// replace this example code with whatever you need
return $this->render('AppBundle::index.html.twig',array(
'posts' => $posts
));
}
/**
* #param Post $post
* #return \Doctrine\Common\Collections\ArrayCollection
*/
public function getTags(Post $post) {
$tagManager = $this->get('fpn_tag.tag_manager');
$tagManager->loadTagging($post);
return $post->getTags();
}
The getTags method in this controller simply takes your entity and uses the tagmanager to find and return it's tags. You'll see the loop in the index method that adds the tags to each entity.
Then in Twig you can access your tags on each post in the loop:
{% for post in posts %}
<h2>{{ post.title }}</h2>
{% for tag in post.tags %}
{{ tag.name }}
{% endfor %}
{% endfor %}
You could iterate over the collection in your controller, like this:
foreach($articles as $article){
$this->tagManager->loadTagging($article);
}

Resources