symfony 2.8
Ok, I have a Category entity which looks like this:
class Category
{
// ... //
/**
* #ORM\OneToMany(targetEntity="Classified", mappedBy="category")
*/
private $classified;
// ... //
public function __construct()
{
$this->classified = new ArrayCollection();
}
}
and the Classified entity looks like this:
Class Classified
{
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="classified")
*/
private $category;
}
In my result.html.twig I called to render the controller like this:
{{ render(controller('ClassifiedBundle:Category:displayCategoryPanelList'))}}
which basically calls Category Controller
class CategoryController extends Controller
{
public function displayCategoryPanelListAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$categories = $em->getRepository('ClassifiedBundle:Category')
->findAllOrderedByName();
return $this->render('ClassifiedBundle:Front/Widgets:category-panel-list.html.twig',
array(
'categories' => $categories
));
}
}
which in turn calls the CategoryRepository for query:
class CategoryRepository extends EntityRepository
{
public function findAllOrderedByName()
{
$query = $this->createQueryBuilder('c')
->select('c, cc')
->leftJoin('c.classified', 'cc')
->orderBy('c.name', 'ASC')
->getQuery()
->getResult();
return $query;
}
}
which in the end is rendered to category-panel-list.html.twig:
<ul>
{% for c in categories %}
<li>
<a href="#">
<i class="fa {{ c.icon }}"></i>
{{ c.name }} <span class="category-counter">{# COUNT SHOULD GO HERE#}</span>
</a>
</li>
{% endfor %}
</ul>
Now, all these work fine apart from the counting classified arraycollection part. I have tried {{ c.classified|length }} but this gives a fatal error.
The dump looks good and I can see the entry for the classified collection, but just don't know how to count them. Some categories have empty classified collection.
{{ c.classified|length }} is applied to an array.
Your object c.classified is a PersistCollection, not an array.
You should use {{ c.classified.count }}
You could try just using the doctrine method
{{ c.classified.count }}
Related
I'm trying to get manyToMany relationship with Doctrine and symfony.
I'm new at Doctrine and Symfony. I came from Zend framework.
I've created the 3 tables:
Post, PostCategory and junction table PostToCategory as you can see below.
My goal is to do inner join and to get for every post its categories.
This is what I've done so far:
//CMS/GBundle/Entity/PostContent
class PostContent
{
/**
* #ORM\ManyToMany(targetEntity="CMS\GBundle\Entity\PostCategory", inversedBy="posts")
* #ORM\JoinTable(name="post_to_category")
*/
protected $categories;
public function __construct()
{
$this->categories = new ArrayCollection();
}
//CMS/GBundle/Entity/PostCategory
class PostCategory
{
/**
* #ORM\ManyToMany(targetEntity="CMS\GBundle\Entity\PostContent", mappedBy="categories")
*/
protected $posts;
public function __construct()
{
$this->posts = new ArrayCollection();
}
I would like now to create function that returns me joined data Post->PostCategories.
Where should I create function ? in PostToCategory Repository ? or somewhere else ? How should my query look like ?
I've tried a lot of options and I passed all the possible questions on Stack but I could not get it done..
Thanks in advance!
Update:
This is what i get when i do findAll method on PostContent repository.
The preferred way to make a relationship is to create your entities like you did, then run a console command to extend your entity with some getters and setters:
$ bin/console doctrine:generate:entities AppBundle
and then let doctrine create your tables:
$ bin/console doctrine:schema:update --force
Now that is everything ready you have to fill some data in your database tables.
$category = new Category();
$categroy->setName('PHP Programming');
$em->persist($category);
$post = new Post();
$post->setTitle('My first Blogpost');
$post->addCategory($category);
$em->persist($post);
$em->flush();
After that can get it out. I just give you an example
public function indexAction($id)
{
$em = $this->getDoctrine()->getManager();
$category= $em->getRepository('AppBundle:PostCategory')->find($id);
// test
foreach($category->getPosts() as $post)
{
echo $post->getTitle() . '<br>';
}
}
To load all the posts immediately instead of lazy loading i suggest to write your own query in the CategoryRepository class and change the find() method-name for your own method-name.
You can create repository class for your entity, for example:
CMS/GBundle/Repository/PostContentRepository.php
<?php
namespace CMS\GBundle\Repository;
use Doctrine\ORM\EntityRepository;
class PostContentRepository extends EntityRepository
{
public function getPostsWithCategories()
{
$qb = $this->createQueryBuilder('post_content')
->select(['post_content', 'post_categories'])
->join('post_content.categories', 'post_categories')
;
return $qb->getQuery()->getResult();
}
}
CMS/GBundle/Entity/PostContent.php
<?php
namespace CMS\GBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity(repositoryClass="CMS\GBundle\Repository\PostContentRepository")
*/
class PostContent
{
/* Your entity properties and methods */
}
And then you can use it in anywhere. For example, in the controller:
<?php
namespace CMS\GBundle\Controller;
use ;
class SomeController extends Controller
{
public function someAction()
{
$posts = $this->getDoctrine()
->getManager()
->getRepository('CMSGBundle:PostContent')
->getPostsWithCategories()
;
/* Your logic */
}
}
As Frankbeen advised me i created my custom query:
public function getPostCategories() {
$data = $this->createQueryBuilder('c')
->select('c.id, pc.name')
->innerJoin('CMSGBundle:PostToCategory', 'ptc', 'WITH', 'ptc.postId = c.id')
->innerJoin('CMSGBundle:PostCategory', 'pc', 'WITH', 'ptc.categoryId = pc.id')
->getQuery()
->getArrayResult();
return $data;
}
In Method above I'm fetching just post.id that is related to post_to_category_post_id category name
In controller I generate two queries one to fetch all Posts second to fetch data using getPostCategories method.
Controller:
$em = $this->getDoctrine()->getManager();
$posts = $em->getRepository('CMSGBundle:PostContent')->findAll();
$postCategories = $em->getRepository('CMSGBundle:PostContent')->getPostCategories();
View:
{% for post in posts %}
{% for category in categories %}
{% if category.id == post.id %}
{{ category.name }}
{% endif %}
{% endfor %}
{% endfor %}
I know that this is not the best solution and please if anyone can suggest me with minifing same code or how to write it better I will be grateful!
In my application's admin panel, I am showing a list of users who are currently marked as "Active" in the database.
<ul class="list-group">
{% for activeuser in activeusers %}
<li class="list-group-item">
{{ activeuser.username|e }}
<input type="checkbox" name="my-checkbox" class="ckbx" checked>
</li>
{% endfor %}
</ul>
As you can see, each active user list item has a placeholder checkbox for now which is checked when the user is, you guessed it, active.
I would like to be able to simply uncheck the checkbox, and then run an AJAX call to update the database to mark the user as inactive. My first instinct was to create a form for each user object in my controller, but it seems like that would get incredibly messy. Also, I can't simply pass in a
'form' => $form->createView()
from my controller as there presumably has to be one form for each user. Any of the documentation I have read on the subject doesn't seem to provide any help for this particular problem.
UPDATE
I created a function within my controller to create a generic user update form:
/**
* Creates a form to create a User entity.
*
* #param User $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
public function createUserForm(User $entity){
$form = $this->createForm(new UserType(), $entity, array(
'action' => $this->generateUrl('user_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
return $form;
}
The form is generated by the UserType class
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('isActive', 'checkbox');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_user';
}
}
Now inside of the main action (called dashboardAction), I get a list of users and for each of user, generate a form for it. Don't forget to run createView() on each form generation!
public function dashboardAction()
{
$userService = new UserService($this->getDoctrine()->getManager());
$activeUsers = $userService->listUsers('active');
$inactiveUsers = $userService->listUsers('inactive');
$formify_activeUsers = array();
foreach($activeUsers as $activeUser){
$formify_activeUsers[] = $this->createUserForm($activeUser)->createView();
};
return $this->render('AppBundle:Admin:dashboard.html.twig',
array('page_title' => 'Administration Panel',
'activeusers' => $formify_activeUsers,
)
);
}
Then the twig code looks like this:
<ul class="list-group">
{% for activeuser in activeusers %}
<li class="list-group-item">
{{ form_start(activeuser) }}
{{ form_widget(activeuser) }}
{{ form_end(activeuser) }}
</li>
{% endfor %}
</ul>
If what you really want is to activate/desactivate an user why put the overhead of the forms in this situation.
You could simply create an action:
/**
* #Route("/admin/isactive", name="isactive")
* #Method("POST")
*/
public function deactivateUserAction($id){
$em = $this->getDoctrine();
$user= $em
->getRepository('AppBundle\Entity\User')
->find($id);
if (!$user) {
throw $this->createNotFoundException(
'No userfound for id '.$id
);
}
$user->setActive(false);
$em->getManager()->flush($user);
return new JsonResponse();
}
On your view:
<ul class="list-group">
{% for activeUser in activeusers %}
<li class="list-group-item">
<input class="user-status" type="checkbox" value="{{activeUser.id}}">{{activeUser.name}}
</li>
{% endfor %}
</ul>
Attach a on click event into your checkboxs.
$('.user-status).on('click', function(e){
$.post("{{ path('isactive') }}", {userId: $(this).val()})
.done(function(data){
alert("Data loaded: " + data);
});
});
In my template, I want to call a function that will display the total counts of of Employee in Company.Employee is related to Department, Department related to Company in one to many relationships.
{% for com in company %}
{{ com.name }}
{{ com.description }}
{{ com.getNumberOfEmp|length }} //this a function must display counts of employee
{% endfor %}
In controller
$em = $this->getDoctrine()->getManager();
$company = $em->getRepository('Bundle:Company')->findAll();
Where should I put the getNumberOfEmp method?
In Symfony 1.4, I easily achieved this by putting the getNumberOfEmp in the company.class that will call the company.table.class
Another question is, how to properly use Doctrine or DQL to query multiple Join?
I tried this function but I don't know if it is the proper way
companyrepository.php
public function getNumberOfEmp()
{
return $this
->createQueryBuilder()
->select('e.firstname')
->from('Emp e')
->leftJoin('e.Department d')
->leftJoin('d.Company c')
->where('i.id =:$id)
->setParameter('id',$this->id)// I am confused about this since i want to display all names of the company
->getQuery()
->getResult()
;
}
In Symfony 1.4 I use it this way
//company.class.php
public function getNumberOfEmp()
{
$emp = Doctrine_Core::getTable('Company')->createQuery('c')
->select('v.firstname')
->from('Employeers e')
->leftJoin('e.Department d')
->leftJoin('d.Company c')
->where('c.id=?',$this->id);
return $emp->execute();
}
And easily call it in php template
<?php foreach ($company as $com): ?>
<?php echo $com->name ?>/display name of company
<?php echo $com->description ?>//description
<?php echo count($com.getNumberOfEmp) ?>//dispalys number of employees
<?php endforeach ?>
Any Ideas?
Just a create a twig extension, and use with it with an argument; something like:
The extension class:
<?php
namespace WHERE\YOU_WANT\TO\CREATE_IT;
class TestExtension extends \Twig_Extension
{
protected $em;
public function __construct($em)
{
$this->em = $em;
}
public function getFunctions()
{
return array(
//this is the name of the function you will use in twig
new \Twig_SimpleFunction('number_employees', array($this, 'a'))
);
}
public function getName()
{
return 'nbr_employees';
}
public function a($id)
{
$qb=$this->em->createQueryBuilder();
$qb->select('count(n.id)')
->from('XYZYOurBundle:Employee','n')
->where('n.company = :x)
->setParameter('x',$id);
$count = $qb->getQuery()->getSingleScalarResult();
return $count;
}
}
Define your extension in service.yml and inject the entity manager:
numberemployees:
class: THE\EXTENSION\NAMESPACE\TestExtension
tags:
- { name: twig.extension }
arguments:
em: "#doctrine.orm.entity_manager"
and finally you can use it in your template like :
{% for com in company %}
{{ com.name }}
{{ com.description }}
{{ number_employees(com.id) }}
{% endfor %}
Having the following error in my doctrine query when trying to find a specific tag selected by the user.
[Semantical Error] line 0, col 78 near 'tag WHERE blog.tags': Error: Class Acme\DemoBundle\Entity\Blog has no association named tags
Can someone tell what's wrong with the query? (trying to query for a tag selected in the side bar that brings up all posts related to the tag)
Repository
public function getPostsByTags($tags)
{
$qb = $this->createQueryBuilder('b');
$qb->select('b')
->join('b.tags', 'tag')
->where('b.tags LIKE ?', '%'.$tags.'%');
return $qb->getQuery()->getResult();
}
Blog Entity
/**
* #var string
*
* #ORM\Column(name="tags", type="text")
*/
private $tags;
/**
* Set tags
*
* #param string $tags
* #return Blog
*/
public function setTags($tags)
{
$this->tags = $tags;
return $this;
}
/**
* Get tags
*
* #return string
*/
public function getTags()
{
return $this->tags;
}
Controller
/**
* #Route("/tag/{tag}", name="AcmeDemoBundle_tag")
* #Template("AcmeDemoBundle:Page:tag.html.twig")
*/
public function tagAction($tag = null)
{
$em = $this->getDoctrine()->getManager();
$tags = $em->getRepository('AcmeDemoBundle:Blog')
->getPostsByTags($tag);
if (!$tags) {
throw $this->createNotFoundException('Unable to find blog posts');
}
return array(
'tags' => $tags,
);
}
Sidebar Twig
<p class="tags">
{% for tag, weight in tags %}
<span class="weight-{{ weight }}">{{ tag }}</span>
{% else %}
<p>There are no tags</p>
{% endfor %}
</p>
Tag results twig
{% block body %}
{% for tag in tags %}
<article class="result">
<div class="date"><time datetime="{{ tag.created|date('c') }}">{{ tag.created|date('l, F j, Y') }}</time></div>
<header>
<h2>{{ tag.title }}</h2>
</header>
<img src="{{ asset(['images/', tag.image]|join) }}" />
<div class="snippet">
<p>{{ tag.blog|truncate(250, true) }}</p>
<p class="continue">More...</p>
</div>
<footer class="meta">
<p>Comments: -</p>
<p>Posted by <span class="highlight">{{tag.author}}</span> at {{ tag.created|date('h:iA') }}</p>
<p>Tags: <span class="highlight">{{ tag.tags }}</span></p>
</footer>
</article>
{% else %}
<p>There are no blog entries for Health&Fitness blog</p>
{% endfor %}
{% endblock %}
Updated solution: repository query (no blogs found)
public function getPostsByTags($tags)
{
$query = $this->createQueryBuilder('b')
->where('b.tags = :tags')
->setParameter('tags', $tags);
return $query->getQuery()->getResult();
}
Updated solution: controller using query (no blogs found)
public function tagAction(tags=null)
{
$em = $this->getDoctrine()->getManager();
$repository = $em->getRepository('AcmeDemoBundle:Blog');
$tags = $repository->createQueryBuilder('b')
->where('b.tags = :tags')
->setParameter('tags', $tags)
->getQuery()
->getResult();
return array(
'tags' => $tags,
);
}
Change your getPostsByTags function to:
$repository = $this->getDoctrine()
->getRepository('AcmeDemoBundle:Blog');
$query = $repository->createQueryBuilder('b')
->where('b.tags = :tags')
->setParameter('tags', $tags)
->getQuery();
return $query->getResult();
This is the query that worked. Hope this helps others.
public function getPostsByTags($tag)
{
$query = $this->createQueryBuilder('b')
->where('b.tags like :tag')
->setParameter('tag', '%'.$tag.'%');
return $query->getQuery()->getResult();
}
You can take a look at the answer i give to a similar problem (3rd solution) : Symfony2 - Need help setting up a doctrine query for finding tags
public function getBlogsWithTag($tagRequested)
{
$blogs = $this->findAll();
$blogsWithTag = array();
$tags = array();
foreach ($blogs as $blog)
{
$tags = explode(",", $blog->getTags());
foreach ($tags as &$tag)
{
$tag = trim($tag);
}
if(in_array($tagRequested, $tags)) {
array_push($blogsWithTag, $blog);
}
}
return $blogsWithTag;
}
I've a mixed array like this one (mobile numbers and entities):
$targets = array();
$targets[] = '+32647651212';
$targets[] = new Customer();
In my Twig template i have to call getMobile() if target is a Customer or just print the number if it's actually a number (string).
Is there something like instanceof operator in Twig?
<ul>
{% for target in targets %}
<li>{{ target instance of MyEntity ? target.getMobile : target }}</li>
{% else %}
<li>Nothing found.</li>
</ul>
In \Twig_Extension you can add tests
public function getTests()
{
return [
'instanceof' => new \Twig_Function_Method($this, 'isInstanceof')
];
}
/**
* #param $var
* #param $instance
* #return bool
*/
public function isInstanceof($var, $instance) {
return $var instanceof $instance;
}
And then use like
{% if value is instanceof('DateTime') %}
UPDATE 10-2021
Please be aware this answer has been written for symfony 3, and twig 2.
If you use a more recent version, please refer to the answer of #Garri Figueroa on this post.
As you can see in the twig documentation the class \Twig_Extension, \Twig_SimpleTest are now deprecated.
If you use a more recent version of symfony (I recommend it), please use the new class AbstractExtension, TwigFunction, etc
https://symfony.com/doc/5.3/templating/twig_extension.html
OLD VERSION : symfony 3.4
Here a nice way to do instanceof operator in twig with Extension :
1) Create your extention file where you want
(ex: src/OC/YourBundle/Twig/InstanceOfExtension.php )
With \Twig_Extension you can do many things, filter, fonction, but now we will create a Test.
So we implement function getTests(), and inside it we create a new \Twig_SimpleTest
The 1st arugment is the name of test you create, and the seconde a callable.
(can be a function() {}).
<?php
namespace OC\YourBundle\Twig;
class InstanceOfExtension extends \Twig_Extension {
public function getTests() {
return array(
new \Twig_SimpleTest('instanceof', array($this, 'isInstanceOf')),
);
}
public function isInstanceOf($var, $instance) {
$reflexionClass = new \ReflectionClass($instance);
return $reflexionClass->isInstance($var);
}
}
2) Register it in services.yml
(ex: src/OC/YourBundle/Resources/config/services.yml)
services:
[...may you have other services ...]
app.twig_extension:
class: OC\YourBundle\Twig\InstanceOfExtension
public: false
tags:
- { name: twig.extension }
3) Then use it in twig like this
{{ myvar is instanceof('\\OC\\YourBundle\\Entity\\YourEntityOrWhatEver') }}
Source from Adrien Brault => https://gist.github.com/adrienbrault/7045544
My solution for Symfony 4.3
1) Create the AppExtension class in src/Twig folder. (The class is automatically detected).
2) Extend the AbstractExtension class:
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigTest;
class AppExtension extends AbstractExtension
{
public function getTests()
{
return [
new TwigTest('instanceof', [$this, 'isInstanceof'])
];
}
/**
* #param $var
* #param $instance
* #return bool
*/
public function isInstanceof($var, $instance) {
return $var instanceof $instance;
}
}
3) Then use same code of valdas.mistolis answer:
{% if value is instanceof('DateTime') %}
4) Thanks valdas.mistolis and symfony documentation i got my own solution:
Twig Extension templating
Since PHP 5.5.0 you can compare class names next way:
{{ constant('class', exception) is constant('\\Symfony\\Component\\HttpKernel\\Exception\\HttpException') }}
This snippet can help in particular cases when you need strict comparison of class names. If you need to check implementation of interface or to check inheritance would be better to create twig extension described above.
Another solution :
class A {
...
public function isInstanceOfB() {
return $this instanceof B;
}
}
class B extends A {}
class C extends A {}
then in your twig :
{{ a.isInstanceOfB ? ... something for B instance ... : ... something for C instance ... }}
OR
{% if a.isInstanceOfB %}
... do something for B instance ...
{% else %}
... do something for C instance ...
{% endif %}
Another example when iterating through Symfony forms:
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigTest;
use App\Document\Embeded\Image;
use App\Document\Embeded\Gallery;
use App\Document\Embeded\Article;
class AppExtension extends AbstractExtension
{
public function getTests()
{
return [
new TwigTest('image', [$this, 'isImage']),
new TwigTest('gallery', [$this, 'isGallery']),
new TwigTest('article', [$this, 'isArticle']),
];
}
public function isImage($var) {
return $var instanceof Image;
}
public function isGallery($var) {
return $var instanceof Gallery;
}
public function isArticle($var) {
return $var instanceof Article;
}
}
Twig
{% if form.vars.data is gallery %}
This is a Gallery
{% elseif form.vars.data is article %}
This is an Article
{% endif %}
Other solution without ReflectionClass with a twig filter :
First you need a TwigExtension class. Then, add a function as twig filter or twig function,
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class TwigExtension extends AbstractExtension
/**
* #return array|\Twig_Filter[]
*/
public function getFilters()
{
return [
new TwigFilter('is_instance_of', [$this, 'isInstanceOf'])
];
}
/**
* #param $object
* #param $class
* #return bool
*/
public function isInstanceOf($object, $class): bool
{
return is_a($object, $class, true);
}
}
And in twig template :
{% if true is same as (object|is_instance_of('ClassName')) %}
// do some stuff
{% endif %}
Check http://php.net/manual/fr/function.is-a.php
Use default filter in Twig like this:
{{ target.mobile|default(target) }}
Quite old, but I can't see here one more good possibility to achive this:
Enity:
public function __toString()
{
return 'NameOfYourEntity';
}
Twig:
{{ entity }}