I'm using ACF's flexcontent, however to allow the user to add different types of content in the same row, I've nested more flexcontent elements inside it.
The goal for the end user to be able to add either text or image, and drag them to decide the order.
I have other types of flexcontent for images, video, text etc. These are working fine.
The structure of my ACF fields is
--text
--image etc (all other flexcontent blocks)
--content_mixed = flexcontent
----column = flexcontent
--------text= flexcontent
----------------text (text field)
--------image=flexcontent
----------------imagegroup (group)
--------------------------------image
--------------------------------caption
My code is:
{% set flexcontent == post.get_field('flexcontent')%}
{% if flexcontent %}
{% for item in flexcontent %}
{% if item.acf_fc_layout == 'text' %}
{% elseif item.acf_fc_layout == 'images' %}
//images
{% elseif item.acf_fc_layout == 'content_mixed' %}
// text + image, this shows up
{% for subitem in post.subitem.get_field('column') %}
{% if subsubitem.acf_fc_layout == 'text' %}
//text
{% endif %}
{% if subsubitem.acf_fc_layout == 'imagegroup' %}
// image
//image caption etc
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
I think I'm in the wrong iteration but having trouble finding how to get to the right one. I can't see any of the content I've added to the post.
the print_r of {{ item.column }} (inside the first flexcontent field, "content_mixed") is
Array
(
[0] => Array
(
[acf_fc_layout] => text
[text] =>
Lorem Ipsum doret sit amet
)
[1] => Array
(
[acf_fc_layout] => image
[imagegroup] => Array
(
[image] => Array (
//all image info
)
Your print_r is telling you that item is an array of arrays. So you can just loop over that instead:
{% for subitem in item.column %}
{% if subsubitem.acf_fc_layout == 'text' %}
//text
{% endif %}
// etc.
{% endfor %}
As pointed out in the comments, your chained post.subitem.get_field() call will not do what you expect.
BTW, you don't need the if flexcontent check before your loop. Twig is very tolerant of empty values and will just ignore attempts to loop over null, false, etc. Less nesting is much easier to read.
Related
I'm trying to show products card inside blog posts / articles of shopify.
I've edited main-article.liquid so that when the content of an article is rendered, it checks the content for a particular string and then splits the code.
if a split has occurred than it isolates the handle of the product.
then it will need to render the product based on the product handle as a card-product.
card-product is the element in which a product is show in a collection ( as in a catalog list )
though I cannot render the product but just a dummy element as in this
I've followed 2 tutorials to get this far:
happypoint
thefunction
the first one is using a snippet that is not present in my theme ( dawn ) but also adding it just to se some results brings out same missing content problem but in a different design.
{%- when 'content'-%}
<div class="article-template__content page-width page-width--narrow rte" itemprop="articleBody" {{ block.shopify_attributes }}>
{% assign divider = '[PROD]' %}
{% assign dividerClose = '[/PROD]' %}
{% assign text = article.content | split: divider %}
{{ text[0] }}
{% for divider in text offset:1 %}
{% assign x = forloop.index %}
{% assign newtext = text[x] | split: dividerClose %}
{% assign productHandle = newtext[0] %}
{% assign product = all_products[productHandle] %}
<div class="product_item">
{% render 'card-product',
media_aspect_ratio: section.settings.image_ratio,
show_secondary_image: section.settings.show_secondary_image,
show_vendor: section.settings.show_vendor,
show_rating: section.settings.show_rating,
lazy_load: lazy_load,
show_quick_add: section.settings.enable_quick_add,
section_id: section.id
%}
</div>
{{ newtext[1] }}
{% endfor %}
</div>
How can I render the product as a card-product.liquid?
I'd like to show an element if there is no event belonging to the category. Before landing on list of events user has selected a category previously in a search form.
How can I write a condition saying 'if there is no event belonging to the selected category' ?
I tried this but I ended up with this error :
Key "category" for array with keys "0" does not exist.
My code on twig :
{% if events.category.id != category.id %}
Comment participer ?
{% endif %}
From the error text it seems that your events variable is an array of objects, so you can't use .category.id in a condition without a loop. You need a loop to determine if the event is set with the category you want in the array, if not, display your message
{% set event_isset = false %}
{% for event in events %}
{% if event.category.id == category.id %}
{% set event_isset = true %}
{% endif %}
{% endfor %}
{% if not event_isset %}
Comment participer ?
{% endif %}
If your events is an array of objects/arrays each of which has key category then this condition should work:
{% if events|filter(e => e.category == category.id)|length == 0 %}
...
{% endif %}
Docs https://twig.symfony.com/doc/2.x/filters/filter.html
{% form_theme form _self %}
{% block collection_entry_row %}
{% set fields = form.attributes.children[0].children %}
{% for key, field in fields %}
{{ form_widget(field) }}
{{ form_widget(fields[key]) }}
{% endfor %}
{% endblock %}
And I get this error:
Neither the property "attributes" nor one of the methods "attributes()", "getattributes()"/"isattributes()"/"hasattributes()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView".
But this
{% set fields = form.attributes.children[0] %}
{% for key, field in fields %}
{{ dump(field) }}
{% endfor %}
Produces this:
Symfony\Component\Form\FormView {#2150 ▼
+vars: array:33 [▶]
+parent: Symfony\Component\Form\FormView {#2203 ▶}
+children: []
-rendered: false
-methodRendered: false
}
And yet inside the form_start() I can do the following and render one field at a time:
{% set tempInput = form.children['attributes'].children[0].children['attributes'].children[0].children['pin'] %}
{{ form_widget(tempInput) }}
Thoughts?
just had the same questions as you about this subject, and since i didn't understands the way to use the "{% block collection_row %}" i've found this following workaround.
I find out that the problem i had was coming from the 2 last item of the form.children: "submit" and "_token".
Because, of course, they will not have the variable name you are searching in the other "real" forms childs.
So i just added a check to the length of the children array to exclude this "submit" and "_token" items.
(The test might certainly be different for your case).
{% for child in form.children %}
{% if not child.children | length != 4 %} // my array has 4 items, submit and _token have none
{{ form_widget(child.children.variableName) }}
{% endif %}
{% endfor %}
As you can see you can access your collection variable between the if like this:
{{ form_widget(child.children.variableName) }}
Peace.
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!
Lets say I have some data called 'brands ' in an array past into a twig template like this:
{ 0:1,1:2,2:3,3:4,4:5,5:6,6:7,7:8}
Second array called 'designs' like this
{120:11,123:22,189:32,300:34,400:53,500:63,688:37,799:28}
o/p excepted
12,24,35,38,59,70,45,36
the below code shows what i have tried and the o/p expected for 8 times, but i am getting 64 times the output like
{% for key ,value in brands %}
{% for key,design in designs %}
// for example i need to add the value and design for 8 times
{% set total = value + design %}
{% endfor %}
{% endfor %}
You are incorrectly nesting your loops, and adding each element of designs to each element of brands. You only need one loop:
{% for key, value in brands %}
{% set total = value + designs[key] %}
{% endfor %}
Don't forget you can't access total unless you define it before starting the loop:
{% set total = 0 %}
{% for key, value in brands %}
{% set total = value + designs[key] %}
{% endfor %}
If you're looking for the sum of both of the arrays into one total with varying numbers of elements for brands and designs:
{% set total = 0 %}
{% for key, value in brands %}
{% set total = total + value %}
{% endfor %}
{% for key, value in designs %}
{% set total = total + value %}
{% endfor %}
If you're looking to add together each corresponding element between designs and brands without depending on matching keys - it's a much more difficult process when trying to do it in Twig, and you're missing what Twig is meant for entirely. You should handle this data in the controller or build a better data model to pass to Twig. For example:
$brands = array(0=>1,1=>2,2=>3,3=>4,4=>5,5=>6,6=>7,7=>8);
$designs = array(120=>11,123=>22,189=>32,300=>34,400=>53,500=>63,688=>37,799=>28);
$brands_designs = array();
foreach ($brands as $key => $brand) {
$design_key = key($designs);
$brands_designs[$key] = array(
'brand' => $brand,
'brand_key' => $key,
'design' => next($designs),
'design_key' => $design_key
);
}
return $this->render('AcmeBundle:Folder:template.html.twig', array('brands_designs' => $brands_designs));
Then in your Twig template:
{% for key, value in brands_designs %}
{% set total = value.brand + value.design %}
{% endfor %}
But if you insist on matching each element together and adding them individually...
{% for brandKey, brand in brands %}
{% set outerLoopIndex = loop.index %}
{% for designKey, design in designs %}
{% if outerLoopIndex == loop.index %}
{% set total = brand + design %}
{# do stuff with total here, like {{ total }} #}
{% endif %}
{% endfor %}
{% endfor %}
The above isn't tested, I'm a little worried about variable scope in there. It actually iterates a total of 64 times with your sample set but only outputs on the diagonal (when the outer index == inner index, so 1 == 1, 2 == 2, etc.)