I have a query in DQL that in response generates a multidimensional array in this way:
How do I show this on screen with twig?
This is my solution.
Thanks to the help of DarkBee
I saw this solution https://stackoverflow.com/a/34402216/2400373 which adapts to my problem.
It presents other problems here I explain
My DQL is this:
$dql="SELECT c,o
FROM BackendBundle:Orders o
JOIN o.users u
JOIN BackendBundle:Customer c
WITH u.email = c.billEmail
where o.orderid='$var'";
After it's necessary to add a twig extension:
//src/AppBundle/Twig/AppExtension.php
<?php
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
class AppExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('cast_to_array', array($this, 'objectFilter')),
);
}
public function objectFilter($stdClassObject) {
// Just typecast it to an array
$response = (array)$stdClassObject;
return $response;
}
}
After in twig:
{% for key, value in ordenes|cast_to_array %}
<td id="col" class="hidden-xs">{{ value }}</td>
{% endfor %}
Other problems in my Entities I need add __toString ... for example:
public function __toString()
{
return (string)$this->getBillEmail();
}
With this already works. thank you help
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 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 %}
I need to render data with unknown type with filters that specific on each data type:
the rendered structures looks like:
array(
"value" => "value-to-render",
"filter" => "filter-to-apply",
)
{% for item in items %}
{{ item.value|item.filter|raw}}
{% endfor %}
So My Question is: How can I get twig to use item.filter as a filter on the value?
You have to write your filter, which will call filters by passing name to it.
How to initially write you Extension you can read here.
Assuming that you have created you extension, you have define your custom function, (e.g., customFilter).
//YourTwigFilterExtension.php
public function getFunctions()
{
return array(
...
'custom_filter' => new \Twig_Function_Method($this, 'customFilter'),
);
}
Then, you have to define this function
public function customFilter($context, $filterName)
{
// handle parameters here, by calling the
// appropriate filter and pass $context there
}
After this manipulations you'll be able to call in Twig:
{% for item in items %}
{{ custom_filter(item.value, item.filter)|raw }}
{% endfor %}
Or, if you've defined your filter as filter (not as function):
{% for item in items %}
{{ item.value|custom_filter(item.filter)|raw }}
{% endfor %}
This Twig extension did the trick for me:
<?php
namespace YourNamespace\YourBundle\Twig;
use \Twig_Extension;
use \Twig_SimpleFilter;
use \Twig_Environment;
class ApplyFilterExtension extends Twig_Extension
{
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName()
{
return 'apply_filter_twig_extension';
}
public function getFilters()
{
return array(
new Twig_SimpleFilter('apply_filter', array($this, 'applyFilter'), [
'needs_environment' => true,
]
));
}
public function applyFilter(Twig_Environment $env, $value, $filterName)
{
$twigFilter = $env->getFilter($filterName);
if (!$twigFilter) {
return $value;
}
return call_user_func($twigFilter->getCallable(), $value);
}
}
And then in your template:
{% for item in items %}
{{ item.value|apply_filter(item.filter)|raw}}
{% endfor %}
This question is directly linked to one of my questions :
Show variable inside variable
The answer is that you need a kind of "eval" method that doen't exist yet(but soon). BUT you also can create your own function as #thecatontheflat mention it.
I just created a Symfony Bundle for that:
Take a look here: https://github.com/marcj/twig-apply_filter-bundle
Here's an extension to dossorio's answer that allows chaining multiple filters as well as passing extra parameters to filters when needed:
class applyFiltersExtension extends Twig_Extension
{
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName()
{
return 'apply_filters_twig_extension';
}
public function getFilters()
{
return [
new Twig_SimpleFilter('apply_filters', [$this, 'applyFilters'], [
'needs_environment' => true,
]
)];
}
public function applyFilters(Twig_Environment $env, $value, array $filters = null)
{
if (empty($filters)) {
return $value;
}
foreach ($filters as $filter) {
if (is_array($filter)) {
$filter_name = array_shift($filter);
$params = array_merge([$env, $value], $filter);
} else {
$filter_name = $filter;
$params = [$env, $value];
}
$twigFilter = $env->getFilter($filter_name);
if (empty($twigFilter)) {
continue;
}
$value = call_user_func_array($twigFilter->getCallable(), $params);
}
return $value;
}
}
.
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 }}
I'm creating my Twig Extension to extend the actual "FormExtension".
Reason for that is that I need to create new functions without overwriting the current ones and making this available across my entire project.
So building and extension seemed to be the right way to go.
Building the extension is not a problem, my problem is how to render block from there?
What I understood till here, is that I need to create a Twig_Environment where I have to load my actual twig template (containing my blocks).
From there I should be able to render those block using "$mytemplate->displayBlock()".
Sample code:
public function renderWidgetinline(FormView $view, array $variables = array())
{
$loader = new \Twig_Loader_Filesystem(__DIR__.'/../Resources/views/Form');
$twig = new \Twig_Environment($loader);
$this->template = $twig->loadTemplate("form_layout.html.twig");
ob_start();
$this->template->displayBlock(???WHAT-PARAMS???);
$html = ob_get_clean();
return $html;
}
I found those information by looking at the Symfony base FormExtension.php file.
My questions are:
How does displayBlock() works, where can I found the defintion of that function?
Is what I described above the right way to go?
How should I proceed to have access to that new TWIG template together with the base form_div_layout.html template? Can I somehow get the current environment without having to recreated one and load my additional template there?
Thanks!
Have you tried to use renderBlock instead?
The first parameter you need is the name of the block, and the second should be an associative array of values passed to the block.
So what you would have in the case of a service that is rendering a block is the following:
The Service Class:
<?php
namespace Acme\BlockBundle\Blocks;
use Doctrine\Common\Persistence\ObjectManager;
Class Block {
private $om;
private $environment;
private $template;
public function __construct( ObjectManager $om, Twig $environment )
{
$this->om = $om;
$this->environment = $environment;
}
public function render( $template, $data )
{
$this->template = $this->environment->loadTemplate( $template );
// maybe query the DB via doctrine, that is why I have included $om
// in the service arguments
// example:
$entities = $om->getRepository( 'AcmePizzaBundle:Pizza' )->getMeatyOnes()
return $this->template->renderBlock( 'acme_block', array(
'data' => $entities,
));
}
}
The Twig Extension Class
<?php
namespace Acme\BlockBundle\Twig\Extension;
use Twig_Extension;
use Twig_Function_Method;
class BlockExtension extends Twig_Extension
{
protected $container;
public function __construct( $container )
{
$this->container = $container;
}
public function getName()
{
return 'block_extension';
}
public function getFunctions()
{
return array(
'render_block' => new Twig_Function_Method( $this, 'renderBlock', array(
'is_safe' => array( 'html' ),
)),
);
}
public function renderBlock( $template, $data )
{
return $this->container->get( 'acme.block' )->render( $template, $data );
}
}
The services.yml
parameters:
acme.blocks.block.class: Acme\BlocksBundle\Blocks\Block
acme.twig.block_extension.class: Acme\BlocksBundle\Twig\Extension\BlockExtension
services:
acme.blocks.block:
class: '%acme.blocks.block.class%'
arguments:
- '#doctrine.orm.entity_manager'
- '#twig'
acme.twig.block_extension:
class: %acme.twig.block_extension.class%
arguments:
- '#service_container'
tags:
- { name: twig.extension }
don't forget your template:
{% block acme_block %}
{% spaceless %}
{# do something with your data here #}
{% endspaceless %}
{% endblock acme_block %}
Then when you want to display it, you just need to call the twig function you have just created:
{{ render_block( '::block_template.html.twig', someDataOneThePage ) }}
By no mean this is a complete solution, but I have used something similar and it proved to be working.
HTH
Tam
[edit: April 2016 - for reference: this solution was working on a Symfony 2.4 project]