Which approach is better and faster - Symfony2, Doctrine2 - symfony

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

Related

how do I get the raw field values in a twig?

In Drupal 9, I have created a field[description] which is a List in a content type[country]. In the description list, I added two values like name, capital as dropdown. Here is the condition:
if(description==name) { //country name should be displayed) }
if(description==capital) { //the capital name should be displayed }
How do I do this using twig?
I haven't tried coz I had no idea. Can anyone give me solution for this?
Here is the twig code.
{% if country.field_description.value == 'name' %}
{{ country.title.value }}
{% elseif country.field_description.value == 'capital' %}
{{ country.field_capital.value }}
{% endif %}

Looping through values in twig and replacing empty values

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!

Phalcon count in volt

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

Symfony 2 twig split function is not working as expected

I am bored searching for an answer so this is my firs question here.
In Symfony 2 , in my twig template i iterate over an array of objects:
{% for client in clients %}
i have the variable client.curs = to the string "Cursul 1 = 4.1234"
i want to split this string so i use
{% set cursarr = client.curs|split(' = ') %}
now, if i dump my array i get
array (size=2) 0 => string 'Cursul 1' (length=8) 1 => string '4.1234' (length=6)
wow! cool! just what i wanted. i continue my work, i just need the second part of
the array (4.1234) so i do this:
{{ cursarr[1] }}
ooops! Key "1" for array with keys "0" does not exist.
Ok! i am an idiot so i try:
{{ cursarr.1 }} same error here. Hmmmm! WTF?!
i try {{ cursarr[0] }} pops out 'Cursul 1' WTF?!
{{ cursarr.0 }} also working
i don't get it, what am i doing wrong? Why is life so complicated?
is it because it is late and i am tired? Need help!
{% endfor %}
I manage to make it work this morning with a clear mind :)
{% set cursarr = client.cursuri|split(' = ') %}
{% set cSpecial = '' %}
{% for curs in cursarr %}
{% set cSpecial = curs %}
{% endfor %}
I have to iterate the array in order to get the second value;
I didn't realized it is a multidimensional mdfkr.
Hope this will help someone in need.

Grouping Collection in FormBuilder Symfony2

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

Resources