Loop over OneToMany - ManyToOne association - symfony

I have the tables as follows:
structure -> OneToMany -> media
media -> ManyToOne -> typeMedia
Browsing structure must enter the media for every type.
For example:
Photo:
img, img, img, img ..
Video:
video, video, video ...
...
I tried in every way, a for loop inside another for loop, but typeMedia (photo, video ..) are duplicated forever..
Here the code, but it is wrong
{% for media in structure.media %}
{% for type in media.typeMedia.media %}
<h3>{{ type.typeMedia.name }}</h3>
<hr/>
<img src="{{ type.webPath | imagine_filter('thumb', true) }}" />
{% endfor %}
{% endfor %}
How can I fix?
I need to change the database structure?

Firstly, I am guessing that in your twig code type.webPath that should be media.webPath? As it doesn't make sense for webPath to be an attribute of the media type?
The database structure is fine. However, you cannot directly follow the relationship typeMedia.media to get the results for a single structure. The typeMedia.media relationship will always give you all media of that type (for all structures), regardless of how you navigated to the typeMedia. Furthermore, as long as your outer loop is iterating over a set of media, it is only possible to output by mediaType from twig if that set of media is already ordered by mediaType. This is not the case by default when using structure.media.
There are various ways you could do this. One way would be to add a method to your Structure entity class to get the media by type. The advantage of this approach is that it enables you to get the media by type for a structure from anywhere in your code - repository, service, controller or twig. For example:
In your Structure entity class:
public function getMediaByTypeName()
{
$mediaByType = array();
foreach ($this->media as $aMedia)
{
$typeName = $aMedia->getTypeMedia()->getName();
if (!array_key_exists($typeName, $mediaByType)) $mediaByType[$typeName] = array();
$mediaByType[$typeName][] = $aMedia;
}
return $mediaByType;
}
In your twig:
{% for typeName, media in structure.mediaByTypeName %}
<h3>{{ typeName }}</h3>
<hr/>
{% for aMedia in media %}
<img src="{{ aMedia.webPath | imagine_filter('thumb', true) }}" />
{% endfor %}
{% endfor %}

Related

Iterate lists/content in block template twig Drupal 8

How would I be able to supersede the hierarchical dependencies in Drupal 8's twig engine to be able to loop within the i.e Lists/Views which is assigned to a block. So we would have a template: block--views-block--[machine-name]-1.html.twig You will be required to have the variable {{ content }}
Which then recursively buries itself down to field templates. Its completely killing me that one would need so many levels to produce on block of content.
I would like to iterate within the top custom block template the list.
Attempted
{% for key, value in _context %}
<li>{{ key }}</li>
{% endfor %}
To evaluate what is available to iterate down into the object but with no luck. I did though find a nice overriding object structure to reach the field attributes but that was within the field level
item.content['#item'].entity.uri.value
Thanks
i use this to "generate" a picture from my
node--news--full.html.twig
<div class="col-md-3">
{{ content.field_newsbild }}
</div>
the twig debug suggests some filenames. i took this:
field--node--field-newsbild--news.html.twig
and in there i wrote:
{% for item in items %}
<img alt="" src="{{ file_url(item.content['#item'].entity.uri.value) }}" class="img-responsive" {{ attributes }} >
{% endfor %}
hope i'll help a bit.

An elegant way to get Jekyll collection item by name?

In Jekyll 2.5.3, I use albums collection (because I need not only data stored, but also pages generated).
When Jekyll Data Files are used, you can get a data for particular item as simple as: site.data.albums[foo]
But with collections, things are much worse. All those ways I've tried just do nothing:
site.albums[foo]
site.collections.albums[foo]
site.collections.albums.docs[foo]
site.collections.albums.files[foo]
So I need to:
Loop through all collection items
For each of them get a bare name
Compare this name with some target name
If the name matches, finally assign collection item data to some variable to use
Any better suggestions?
I have just done this today, you are correct with your assertions. Here's how I did it:
<!-- this is in a partial with a 'name' parameter -->
{% for item in site.albums %}
{% assign name = item.path | split:"/" | last | split:"." | first %}
{% if name == include.name %}
{% assign collectionItem = item %}
{% endif %}
{% endfor %}
Usage
{{ collectionItem.title }}
{{ collectionItem.url }}
{{ collectionItem.path }}
It can even be used to populate values in other partials, like so:
{% include card.html title=workItem.title bg=workItem.card.bg href=workItem.url %}
As of Jekyll 3.2, you can use the filter where_exp to filter a collection by any of its properties. So if I have the following item in an albums collection:
---
title: My Amazing Album
---
...
I can grab the first match for an item by that name:
{% assign album = site.albums
| where_exp:"album", "album.title == 'My Amazing Album'"
| first %}
Note that I use the first filter because where_exp returns an array of matched items.
And then use it any way I like:
<h1>{{ album.title }}</h1>
I can't vouch for the build performance of this method, but I have to imagine it's better than a Liquid loop.

save sonata media path into twig variable

I want to store full path of image (sonata media bundle) into variable in twig. Is that possible?
If I write:
{% set pic = path item.image, 'big' %}
it throws me an error: Unexpected token "name" of value "item" ("end of statement block" expected) ...
If I write:
{% set pic = item.image %}
then it works, but it stores only name of the file, not full path.
Why don't you do like this ?
{% set rendered %}{% path item.image, 'big' %}{% endset %}
....
Here is my path {{ rendered }}
There is not such a function available (there is a path() function to generate routes). You have to create your own twig extension with this custom function. Read all about that in the documentation.
Perhaps you can solve with:
<img src="{% path media, 'small' %}" data-href="{% path media, 'big' %}">

SonataAdmin: replace ID in breadcrumbs

How can I replace Object's ID in SonataAdmin breadcrumbs by some other text?
If I set __toString() in my document, it works only for editing. When I attempt to create new record, there is something like MyDocument:0000000000e09f5c000000006a48ef49 in the last breadcumb.
I'm searching for a method which allows me to set some text as the last breadcump if Document::toString() returns null.
This behaviour is implemented directly in the entity:
public function __toString()
{
return $this->getFoo() ? : '-';
}
Bundles are using variants of this, including return (string)$this->getFoo(); or $this->getFoo() ? : 'n/a'; etc.
Related question: toString method for SonataAdminBundle Listing in Symfony2
BTW something cool to know, you can completely customize the breadcrumb via a Twig template:
{% block sonata_breadcrumb %}
{% set _breadcrumb %}
<li>Home</li>
<li>Library</li>
<li class="active">Data</li>
{% endset %}
{{ parent() }}
{% endblock %}

Extending twig for generate html code

I have to generate something like star rating and I have to generate some html for styling ect.
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star"><i></i></div>
<div class="star"><i></i></div>
I want to render using a twig function passing active stars parameters.
{{ stars(4) }}
Is correct use twig functions for generate html code?
Or maybe should I use {% include ... %}
No need in overengineering for such simple task.
If you generate your array in Controller, then it could look like this:
$stars = array(
true,
true,
true,
false,
false,
);
Then you could render your HTML in Twig:
{% for star in stars %}
<div class="star{{ star ? ' on' }}"<i>{{ star ? '*' }}</i></div>
{% endfor %}
In case if you would like to operate with Twig only, I recommend you to use macro:
{% macro stars(stars, total) %}
{% for item in 1..total %}
{{ item }}<br>
{% if item <= stars %}
<div class="star on"><i>*</i></div>
{% else %}
<div class="star"><i></i></div>
{% endif %}
{% endfor %}
{% endmacro %}
If you've defined your macro in the same template, you should call it via _self, if in another file - just like a function, but not forget to import your file into needed twig. See chapter about macros (linked above).
Following call will produce HTML structure that you described in your question:
{{ _self.stars(3,5) }}
See the Extending Twig section of its docs. According to the table in the first section on that page, using functions for content generation is natural. I create a lot of Twig functions and I suggest you create one to solve your problem.
BTW, your function can render a separate template with HTML code — do not generate the HTML code right in your Twig function's PHP code. To render a separate template from your Twig function, inject the service_container service into it, get the templating service and call the render() method on it:
return $this->container->get('templating')->render($pathToYourCustomTemplate);
Usually, it's best to inject the needed services individually, but if you inject the templating service instead of service_container, you'll get a cyclic dependencies problem. That's why injecting the whole container into Twig extensions is a reasonable exception.

Resources