Load tagging in query with FPNTagBundle - symfony

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);
}

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.

Symfony Twig: send a set parameter with a form submit request

I have a form in my twig, i want that by clicking on its submit button, another parameter (that i've set in the twig) get sent to the same action that handles the form :
this is the variable i've set
{% set idprof = profil.id %}
I want to send it with the submit request : ( i know this code is false)
{{ form_widget(form.id),{'idprof': idprof} }}
and the action will look like this :
public function gestProfAction(Request $request, $idprof)
{
}
I'm sorry i know this is a stupid question, but I'm still new in symfony, I couldn't find a solution by myself.
Just pass it in the form start line
{{ form_start(form, {'action': path('idprof', { 'idprof': idprof })}) }}
Remember to add the annotation or yaml for the routing
/**
* #Route("/idprof/{idprof}", name="idprof")
*/
public function gestProfAction(Request $request, $idprof)
{
}
For reference:
http://symfony.com/doc/current/forms.html#rendering-the-form
http://symfony.com/doc/current/form/action_method.html
http://symfony.com/doc/current/templating.html#linking-to-pages

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.

Call a controller function recursive from twig template

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

Can verbatim be used on contents of an include?

I'm sharing templates between client and server and would like to output the raw template inside a script tag which is possible with verbatim.
http://twig.sensiolabs.org/doc/tags/verbatim.html
However it would be nicer if this could be applied as a filter to the include but it doesn't seem possible?
I'm new to twig so excuse me if i've missed obvious functionality.
I ran into the same problem, and this page came up in my search results. In the time since this question was answered, the Twig developers added this functionality into the library. I figured I should add some details for future searchers.
The functionality to include raw text (aka for client-side templates using the same syntax as Twig) is accomplished with the source function.
Ie: {{ source('path/to/template.html.twig') }}
http://twig.sensiolabs.org/doc/functions/source.html
I was looking for something like this too because I'm using Twig.js for some client-side templating along with Symfony. I was trying to share templates between the server-side and client-side code, so I needed content to be parsed in some cases and treated as verbatim in others, which proved to be a bit tricky.
I couldn't find anything built into Twig to help with this, but luckily, it's pretty easy to extend Twig to get what you're looking for. I implemented it as a function, but you may be able to do it as a filter too.
services.yml
statsidekick.twig.include_as_template_extension:
class: StatSidekick\AnalysisBundle\Twig\IncludeAsTemplateExtension
tags:
- { name: twig.extension }
IncludeAsTemplateExtension.php
<?php
namespace StatSidekick\AnalysisBundle\Twig;
use Twig_Environment;
use Twig_Extension;
class IncludeAsTemplateExtension extends Twig_Extension {
/**
* Returns a list of global functions to add to the existing list.
*
* #return array An array of global functions
*/
public function getFunctions() {
return array(
new \Twig_SimpleFunction( 'include_as_template', array( $this, 'includeAsTemplate' ), array( 'needs_environment' => true, 'is_safe' => array( 'html' ) ) )
);
}
function includeAsTemplate( Twig_Environment $env, $location, $id ) {
$contents = $env->getLoader()->getSource( $location );
return "<script data-template-id=\"{$id}\" type=\"text/x-twig-template\">{$contents}</script>";
}
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName() {
return 'include_as_template_extension';
}
}
Usage in Twig file
{{ include_as_template( 'Your:Template:here.html.twig', 'template-id' ) }}
If you have a Twig file like this:
<ul class="message-list">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
The output will be this:
<script data-template-id="template-id" type="text/x-twig-template">
<ul class="message-list">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</script>
The work is derived from Kari Söderholm's answer here. Hope that helps!

Resources