How can I change the order of displaying the collection, and group them by a category that is in the FooBar entity.
$builder
->add('foobar', 'collection', array(
'type' => new FooBarType()
));
The query_builder only works for Entities, not Collections. So it is not about ordering the result, but about Grouping based on the category of the FooBarType, and show them in that order.
Like:
[COLLECTION]
Category One name : [list with that category]
Category Two name : [list with that category]
The view is now showing the collection, with the category name in front:
{% for db in edit_form.foobar %}
<li>[Category: {{ db.foobar.vars.data.name }}]
{{ form_widget(db.value) }}</li>
{% endfor %}
I want to group this by category. So they are sorted by Category name.
This will help:
{% set category = '' %}
{% for db in edit_form.foobar %}
<li>
{% set currentCategory = db.foobar.vars.data.category.name %}
{% if category != currentCategory %}
<p>{{ currentCategory }}</p>
{% set category = currentCategory %}
{% endif %}
{{ form_widget(db.value) }}</li>
{% endfor %}
And order the Category in your Entity like Joao said.
In your entity, on your oneToMany or ManyToMany annotation you can add this:
/**
* #ManyToMany(targetEntity="FooBar")
* #OrderBy({"name" = "ASC"})
*/
private $foobar;
With this, when you loop foobar on a template, they are ordered and sorted by what you defined.
source:
http://docs.doctrine-project.org/en/2.0.x/reference/annotations-reference.html#annref-orderby
Related
I want to customize my EntityType's choice labels highly, like creating a table with multiple attributes of my model.
So I want to access my choices class attributes. The choices are my entities of class MyClass through the EntityType. How can I do so in twig?
Currently I do it like this:
1. in my FormClass I json_encode all fields I need in my label
2. in my template I json_decode these information and display according
IN CODE:
1.
$builder
->add('field', EntityType::class, [
'class' => MyClass::class,
'multiple' => false,
'expanded' => true,
],
'choice_label' => function (MyClass $myClass) {
$data = [
'name' => $myClass->getName(),
'description' => $myClass->getDescription(),
];
return json_encode($data);
},
])
2.
{% block my_form_widget %}
...
{# form is my 'field' FormView of the EntityType #}
{% for child in form %}
{# child is a FormView, one choice of my EntityType #}
{# child.vars.data is boolean as its a checkbox #}
{% set data = child.vars.label|json_decode %}
create some complex html here, like tables
...
{% endfor %}
...
{% endblock %}
Working. But is there a better way?
Thanks,
Kim
In a Symfony form (or form field, which is just a form of its own) that is mapped to an entity, you always have access to the underlying data in form.vars.data. So in this case, form.vars.data would either be null or an instance of MyClass.
For ease of use in your template, you might do something like:
{% set my_object = form.field.vars.data %}
{% if my_object %}
{{ my_object.getName() }}
{{ my_object.getDescription() }}
{% endif %}
Thus there is no need to re-encode your entity data for the view layer, since it's always already available.
If you are working with an EntityType and want to access the properties of each choice's entity, they are available as well in the choices array:
{% for choice in form.field.vars.choices %}
{{ choice.data.getName() }}
{{ choice.data.getDescription() }}
{% endfor %}
A nice trick when you're trying to access any form data and aren't sure where to look is to temporarily add a line to your template like:
{{ dump(form.field) }}
This will allow you to look through the available data and see what all is available. Note that it requires the Twig debug extension to be enabled, and XDebug to be enabled in PHP in order to make the output look nice.
Ok I got it, here is an example of how to access the data of your EntityType's choices in twig. You can check the child.parent.vars.choices list.
{% block my_form_widget %}
...
{# form is my 'field' FormView of the EntityType #}
{% for child in form %}
{# child is a FormView, one choice of my EntityType #}
{# child.vars.data is boolean as its a checkbox #}
{% for choice in child.parent.vars.choices if choice.value == child.vars.value %}
{{ choice.data.name }} {# contains MyClass name #}
{{ choice.data.description }} {# contains MyClass description #}
{% endfor %}
...
{% endfor %}
...
{% endblock %}
The title is a bit ambiguous I know, but let me explain what I'm trying to achieve.
I am attempting to generate a CSV based on data pulled from a doctrine query in my Symfony2 CRM. The data retrieved is based on OpenCart Product and Attribute data, as well as some bespoke information which is irrelevant for this issue.
Each product can have up to 5 different attribute values, named A, B, D, L1 and L2. However, some products do not have all of them, only A, B and L1. The CSV requires each attribute value to be in a separate cell - so the headers are as follows:
ATTRIBUTE: A | ATTRIBUTE: B | ATTRIBUTE: D | ATTRIBUTE: L1 |
ATTRIBUTE: L2
And then I loop through in my Twig file as follows:
{% for attribute in row.product.attributes %}
{% if attribute.text is not null %}
{{ attribute.text }},
{% else %}na,{% endif %}
{% endfor %}
If the product has all 5 attributes, the structure of the CSV is fine. However, if the product only has 3 attributes, it means that all of the subsequent values are pulled back a cell, meaning that the other data is under the wrong headings. I tried checking for values first:
{% for attribute in row.product.attributes %}
{% if attribute.attributeName.name == "A" %}
{% if attribute.text is not null %}
{{ attribute.text }},
{% else %}na,{% endif %}
{% endif %}
{% endfor %}
And I did this for each possible attribute name, but unfortuantely this does not work since if the name does not exist, it just skips it anyway. I'm having trouble trying to think of a way to loop through these attributes and entering a n/a if it's non existent - I'm sure there is a way but I don't know what it is.
For reference, here is the controller code that's generating the data for the CSV:
public function adminCsvAction($filter) {
$repository = $this->getDoctrine()->getRepository('AppBundle:Project');
$stages_repository = $this->getDoctrine()->getRepository('AppBundle:Stage');
$users_repository = $this->getDoctrine()->getRepository('AppBundle:User');
$results = $repository->getSearchResults($filter);
$users = $users_repository->findAll();
$stages = $stages_repository->findBy(array('deleted' => 0), array('sortOrder' => 'ASC'));
$filename = "export_".date("Y_m_d_His").".csv";
$response = $this->render('AppBundle:pages:csv.html.twig', array('data' => $results,'users' => $users, 'stages' => $stages));
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
return $response;
}
The Project Entity has various mappings, one of which links to the Product table in OpenCart which means all attributes and linked values are accessible via this.
Any help in this is much appreciated.
I also agree with Cerad from comment section - that is not the job for Twig. In case your really need to do it, I would try roughly something like this:
{% set allAttr = ["A","B","D","L1","L2"] %}
{% for attribute in allAttr %}
{% if row.product.attributes[attribute] is defined %}
{{ row.product.attributes[attribute].text }}
{% endif %}
{% if not loop.last %},{% endif %}
{% endfor %}
I guess the is defined is critical here...
OK I figured it out. Using what Jovan Perovic suggested, I came up with this:
{% set allAttr = ["A","B","D","L1","L2"] %}
{% set prodAtts = [] %}
{% for row in data %}
{% set existingAtts = [] %}
{% for att in allAttr %}
{% if att not in prodAtts %}
{% set prodAtts = prodAtts|merge([att]) %}
{% endif %}
{% endfor %}
{% for rowAtt in row.product.attributes %}
{% set existingAtts = existingAtts|merge({(rowAtt.attributeName.name|trim):(rowAtt.attributeName.name|trim~'_'~rowAtt.text|trim)}) %}
{% endfor %}
{% for prodAtt in prodAtts %}
{% if prodAtt not in existingAtts|keys %}
{% set existingAtts = existingAtts|merge({(prodAtt):(prodAtt~'_na')}) %}
{% endif %}
{% endfor %}
{% set orderedAtts = existingAtts|sort %}
....
Then the loop for each row thereafter. I used the attribute name with an underscore in order to be able to sort it correct (as it only sorts by value not key) then used preg_replace to remove it along with any instance of the name so I just ended up with the value.
Bit of a lengthy - and probably over thought - solution but it does work!
I have a counting problem in phalcon volt. I have a table named category and there I have two columns id and cname, and also have a table blog and there is a column category. I want to show how many post have in each category.
When I insert a post into blog table, in category column I'm inserting its category id. First of I just retrieve list of all category like this:
[controller]
$categories = Category::find();
$this->view->setVar('category', $categories);
$cx = Blogs::find();
$this->view->setVar('cates',$cx);
[Volt]
{% for categories in category %}
<a href="blog/category/{{categories.cname}}" class="tags">{{ categories.cname }}
<span>[
{% for cx in cates %}
{%if cx.category === categories.id %}
<?php echo(count($cx->category)); ?>
{% endif %}
{% endfor %}
]</span></a>
{% endfor %}
Its render like "1 1 1" or "1 1" or "1" but it should render like "3" or "2" or "1" whats my wrong?
I also tried like this but did not get the expected output:
{% for categories in category %}
<a href="blog/category/{{categories.cname}}" class="tags">{{ categories.cname }}
<span>[
{% for cx in cates %}
{%if cx.category === categories.id %}
{% if loop.first %} {{ loop.length }} {% endif %}
{% endif %}
{% endfor %}
]</span></a>
{% endfor %}
Have you defined your relationships between your models in Phalcon?
If so, you can use the built in commands to query the total amount of posts for each category
Example from the documentation:
You can also use “count” prefix to return an integer denoting the count of the related records:
$robot = Robots::findFirst(2);
echo "The robot has ", $robot->countRobotsParts(), " parts\n";
I don't have much experience with Volt templating, but I guess it will be something like:
{% for categories in category %}
<a href="blog/category/{{categories.cname}}" class="tags">{{ categories.cname }}
<span>[
{{ categories.countBlogs }}
]</span></a>
{% endfor %}
Refer to: https://docs.phalconphp.com/en/latest/reference/models.html#taking-advantage-of-relationships
UPDATE - model relations
[model: Category]
public function initialize()
{
// id => primary key name of the Category table
// Blogs => name of the table you want to create a relationship with
// category => name of the foreign key column in your relationship table
$this->hasMany('id', 'Blogs', 'category');
}
[model: Blogs]
public function initialize()
{
// category => blog column name which refers to the ID in the Category table
// Category => name of the Category table
// id => name of the primary key column in the Category table
$this->belongsTo('category', 'Category', 'id');
}
No Sir, its not working. But i just solved my problem like this :
[controller]
$categories = Category::find();
$this->view->setVar('category', $categories);
[volt]
{% for categories in category %}
<a href="blog/category/{{categories.cname}}" class="tags">{{ categories.cname }}
<span>[
<?php
$catcount = $this->modelsManager->executeQuery("SELECT Blogs.category FROM Blogs WHERE Blogs.category = $categories->id");echo(count($catcount));
?>
]</span></a>
{% endfor %}
Now its working as expected. and here i dont make any relations ion model.Is it ok Sir. Please! Thnx
I have some related entities, let's say for convenience Product and Category. They are related OneToMany and ManyToOne. (each product has one category and one category has many products)
My goal is to display in a template all categories with all their products, but in alphabetical order. For example like this:
Furniture
Bed
Sofa
TV
Meals
Banana
Fish
Lemon
Office
Paper
Pen
I want both categories and products to be sorted.
So:
Approach 1
Write queries with doctrine to get all categories orderedBy name ASC, and to get all products orderedBy name ASC and then in the template:
{% for c in all_categories %}
{{ c.name }}
{% for p in all_products %}
{% if p.category == c %}
{{ p.name }}
{% endif %}
{% endfor %}
{% endfor %}
Approach 2
Get only the categories no matter how they are ordered. Change fetch to eager. Then write a sort filter as Twig Extension like this:
public function sortByName($a, $b)
{
if($a->getName() === $b->getName()) {
return 0;
}
if($a->getName() < $b->getName()) {
return -1;
}
return 1;
}
and using an iterator and uasort() with this function and then in the template:
{% for c in all_categories|sortbyname %}
{{ c.name }}
{% for p in c.products|sortbyname %}
{{ p.name }}
{% endfor %}
{% endfor %}
What I can see is that in 1 the if check is bad because many checks will be redundant. And getting all products when I already have all categories is also redundant. But I think that sorting with Doctrine should be faster than that with the twig extension. So I can't tell which one should I use. If it matters, in my case, I have 3 entities, like this: Each shop has categories and each category has products.
Can you please help me? Thanks very much in advance! :)
This is more an algo choice rather than a symfony/doctrine issue.
My approach is to get all products sorted by (category-name, product-name), and then loop over them to store them in an array indexed by your category. It can be used if you have many products, and enough memory to load all objects at one, but will be faster ( #nbproducts, and not #nbproducts*#nbcategories ), and will use only one ORM request, that will use indexes to sort data.
Php code would look like this :
$sorted_products = array();
foreach ($products as $product) {
$category = $product->getCategory();
if (!array_key_exists($category->getId(), $sorted_products)) {
$sorted_products[$category->getId()] = array('category' => $category, 'products' => array());
}
$sorted_products['products'][] = $product;
}
And then you just have to do 2 foreach in your twig :
{% for category_infos in sorted_products %}
{{ category_infos.category.name }}
{% for product in category_infos.products %}
{{ product.name }}
{% endfor %}
{% endfor %}
Im new to symfony and what im trying is next:
Select everything from the table and count the number of rows.
Access and display this informations in a twig template.
This is part from my code in the controler dedicated to fetch data from datatabase :
.
.
.
$em=$this->getDoctrine()->getManager();
$query=$em->createQuery('SELECT b,COUNT(b.id) FROM AcmeWebBundle:baza b ORDER BY b.id DESC');
$users = $query->getResult();
if (!$em) {
throw $this->createNotFoundException('Something went wrong!');
}
return $this->render('AcmeWebBundle:Default:index.html.twig',array('users'=>$users));
}
in the table named baza i have the fields: id,username,date..etc
And part from the twig file named index.html.twig
{% extends 'AcmeWebBundle:Default:master.html.twig' %}
{% block body %}
<h1> something</h1><br></br>
{% for user in users %}
{{ ...how to access to the number of rows and other data...}}
{% endfor %}
{% endblock %}
Queries:
$query = $this->createQueryBuilder()
->from('AcmeWebBundle:baza', 'b')
->getQuery();
$user = $query->select('b')
->getQuery()
->getResult();
$total = $query->select('COUNT(b)')
->getQuery()
->getSingleScalarResult();
.....
return $this->render('AcmeWebBundle:Default:index.html.twig',array('users' => $users, 'count' => $total));
Template:
{% extends 'AcmeWebBundle:Default:master.html.twig' %}
{% block body %}
<h1> something</h1><br></br>
{% for user in users %}
{{ user.id }}
{{ user.name }}
{% endfor %}
{% endblock %}
Total users: {{ count }}
Where id and name fields in your DB.
You could try the following:
{{ user.id }}
{{ user.username }}
{{ user.date }}
...etc...