How to integrate Symfony 6 form with proper style into EasyAdmin 4? - easyadmin

I have a logic that wouldn't be easy to implement in EasyAdmin so I decided that I implement it in Symfony 6 then integrate it into EA. The integration worked like a charm but I can't figure out which form_theme should I use to look like the other EA forms.
I have created a form type which doesn't belong to any entity since multiple entities will be generated after the validation based on the input data.
This is the controller
<?php
namespace App\Controller\Admin;
use App\Form\Type\NewTextType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class TextController extends AbstractController
{
#[Route('/admin/text/new', name: 'new_text')]
public function new(): Response
{
$defaultData = [];
$form = $this->createForm(NewTextType::class, $defaultData);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
// process the data and persist them as different entities
// redirect to the empty form and do it again
}
return $this->renderForm('admin/text/new.html.twig', [
'form' => $form,
]);
}
}
and the template
{% extends '#EasyAdmin/page/content.html.twig' %}
{% form_theme form 'foundation_5_layout.html.twig' %}
{% block content_title %}
<h1 class="title">Add new Text</h1>
{% endblock %}
{% block main %}
{{ form(form) }}
{% endblock %}
Unfortunately it looks like a crap.
It looks better if I replace foundation_5_layout with {% form_theme form 'bootstrap_5_layout.html.twig' %} but then the appearance setting is not applied even though it is presented in the BODY tag:
data-ea-dark-scheme-is-enabled="true"
What do I miss here?
I use Symfony 6.1.2 and EasyAdmin 4.3.2

Finally I have found the right template which supports the Light/Dark appearance:
{% form_theme form '#EasyAdmin/crud/form_theme.html.twig' %}
The whole template
{% extends '#EasyAdmin/page/content.html.twig' %}
{% form_theme form '#EasyAdmin/crud/form_theme.html.twig' %}
{% block content_title %}
<h1 class="title">Add new Text</h1>
{% endblock %}
{% block main %}
{{ form(form) }}
{% endblock %}

Related

How to add entity extension in Shopware 6 App?

I want to query the categories of the product in the product listing of a Shopware app, so that I can query the customFields of all categories. Is this even possible with an app?
I have already tried via a navigation-page-loaded. And when I override the box-standard.html.twig and access the product, I can't access the categories there.
For each product i want the categories extensions
As the categories association is not loaded for products in the listing you have to fetch the categories using an app script.
As already noted, add the script to the navigation-page-loaded hook, i.e. in Resources/scripts/navigation-page-loaded/category-loader.twig:
{% set products = [] %}
{% if hook.page.cmsPage.type === 'product_list' %}
{% foreach hook.page.cmsPage.sections as section %}
{% foreach section.blocks as sectionBlock %}
{% if sectionBlock.type !== 'product-listing' %}
{% continue %}
{% endif %}
{% foreach sectionBlock.slots as slot %}
{% if slot.type !== 'product-listing' %}
{% continue %}
{% endif %}
{% foreach slot.data.listing.entities as product %}
{% set products = products|merge([product]) %}
{% endforeach %}
{% endforeach %}
{% endforeach %}
{% endforeach %}
{% endif %}
{% set categoryIds = products|reduce((carry, v) => carry|merge(v.categoryIds), []) %}
{% if categoryIds %}
{% set categories = services.repository.search('category', {'ids': categoryIds}) %}
{% foreach products as product %}
{% do product.addArrayExtension('myCategories', {
'categories': categories.entities.getList(product.categoryIds),
}) %}
{% endforeach %}
{% endif %}
Where we first extract all the products, than load the categories of all products at once, and than assign the categories back to the products.
Note that for reading the category entity you need the correct permission, i.e. in the manifest.xml add:
<permissions>
<read>category</read>
<read>category_translation</read>
</permissions>
Now you should be able to access in the box-standard.html.twig template the categories using product.extensions.myCategories.categories.
If I understand your issue correctly, you are trying to access a product's category's custom fields in some piece of storefront logic (like a Twig template).
The way I see it, you would have to add a script that would enable you to query the repository for the categories and their custom fields. The issue is, a SalesChannelProductEntity will not contain the information about all its categories (only the SeoCategory), so you might need to first query the product_category repository.
Generally, it is going to be complicated but it should be doable.

Shopware : Impossible to extend through plugin user-detail

I have Shopware 6.5.3. I was trying to extend "sw-users-permissions-user-detail" like this :
import template from './sw-users-permissions-user-detail.html.twig';
Shopware.Component.override('sw-users-permissions-user-detail', {
template
});
And file 'sw-users-permissions-user-detail.html.twig'
{% block sw_settings_user_detail %}
{% parent %}
{% block test %}
<p>Blabla</p>
{% endblock %}
{% endblock %}
It's not working at all, and I don't know why.
Any help ?
NB : It's working when I'm overriding other templates :
Component.override('sw-dashboard-index', {
template
});
If you want to put the original contents to the block, that you are overriding, you should use the 'parent' statement like this:
{{ parent() }}

Dynamic nav-bar elements - passed from Flask to Jinja - inherited layout template

Environment: Python 3.6, Flask 1.02, Jinja2
Objective:
Create a dynamic menu in layout.html (which is extended by content.html)
yet the url_for of the dynamic element is frequently requires a parameter to be passed
Issue statement:
How can I pass the parameters for url_for in Jinja template when rendering the template?
I feel like I would need the syntax of str().format in Jinja..
I tried to:
1. pass each part as a separate value:
menus = [{'url': 'func_name', 'menu_title': 'title', 'param': 'param_name', 'param_val': 'param_value'}]
return render_template('content1.html', menus=menus]
in jinja I tried to call it like: (I also tried it without the plus and double-quotes)
{{ url_for(func_name), param_name+ "=" + param_val }}
During rendering it gives error of
url_for() takes 1 positional argument but 2 were given
2. tried to use the {% set var_name: passed_variable %}
Built on 1st version of menus defined on server side, I tried to set the variables within Jinja, but also failed.
menus = [{'url': 'func_name', 'menu_title': 'title', 'param': 'param_name', 'param_val': 'param_value'}]
return render_template('content1.html', menus=menus]
Jinja
{% for menu in menus %}
{% set url = menu.get('url') %}
{% set param = menu.get('param') %}
{% set value = menu.get('param_val') %}
{% url_for(url, param + "=" + value %}
Yet it also didn't work.
It feels like if I give a param for the url_for syntax (not a hard-wired string) I cannot add the parameters.
3. tried to pass whole content of url_for as a string:
menus={'url_string': " 'func_name', param_name=param_value"}
yet it fails again as url_for syntacs put the whole between apostrophes, which I wouldn't need at the end.
Some references I scanned through.
Flask context-processor
It could work if I would create another template of each nav-bar for each content page - yet with that move i could simply move the navbar into the content page. However that seems dull. Stack Overflow topic
Thus question:
How can I pass the
param_id=paramval['id']
for the url_for syntax during rendering
{{ url_for('edit_question', param_id=paramval['id']) }}
The code/structure stg like below:
layout.html
<html>
<body>
{% for menu in menus %}
{% for key, value in menu.items() %}
<a href="{{ url_for(value) }}" >
{{ key }}
</a>
{% endfor %}
{% endfor %}
{% block content %}
{% endblock %}
</body>
</html>
content1.html
{% extends 'layout.html' %}
{% block content %}
content
{% endblock %}
content2.html
{% extends 'layout.html' %}
{% block content %}
content
{% endblock %}
app.py
#app.route('/')
def index():
menus = [{'menu_title1': 'menu_func_name1'}]
return render_template('content1.html', menus=menus)
#app.route('/menu_details/<int:menu_nr>')
def show_details_of_menu(menu_nr):
menus = [{'menu_title3': 'menu_func_name3', 'menu_param_name': 'menu_param_value'}
return render_template('content2.html', menus=menus)
sorry for the Wall of text..
sigh.. after hours I just found how to construct the syntax. I hope it will help others!
During rendering:
menus = [{'url': 'func_name', 'menu_title': 'title', 'parameters': {'param1': param1_value}}]
return render_template('context.html', menus=menus]
In Jinja, I adjusted the syntax to manage cases where no parameters are needed:
{% for menu in menus %}
{% if menu.get('parameters').items()|length > 0 %}
<a href="{{ url_for(menu.get('url'), **menu.get('parameters')) }}">
{{ menu.get('menu_title') }}
</a>
{% else %}
<a href="{{ url_for(menu.get('url')) }}">
{{ menu.get('menu_title') }}
</a>
{% endif %}
{% endfor %}

Set an object with Twig into a template thats imported via another template?

Im including a link.twig into block.twig, and block.twig into page.twig. in my set options is there a way I can change the link objects name to something like heroLink?
I need to set options within page.twig. link.twig is included into other templates so I dont want to change it (eg changing link.url to heroLink.url).
In my page:
{% set options = {
title: 'my title',
link: {
text: 'Search',
url: "www.google.com"
}
}
%}
{% include "block.twig" with options %}
In block.twig:
<div class="something">
<h2>{{ title }}</h2>
<div class="hero">
{% include "link.twig" with {'style': 'primary'} %}
</div>
</div>
In link.twig:
{{ link.text }}
The reason for this is that block.twig actually has other links. link.twig may be imported multiple times. As the mock object needs to be created in page.twig something like heroLink makes a lot more sense in this context.
In my page:
{% set options = {
action: {
text: 'Action',
url: "action.com"
}
}
%}
{% include "component.twig" with options %}
In component.twig:
{% import "link.twig" as mainLink %}
{{ mainLink.link(action.url, action.text) }}
In link.twig
{% macro link(url, text) %}
{{ text }}
{% endmacro %}

Using twig variable to dynamically call an imported macro sub-function

I am attempting if use a variable to call a specific macro name.
I have a macros file that is being imported
{% import 'form-elements.html.twig' as forms %}
Now in that file there are all the form element macros: text, textarea, select, radio etc.
I have an array variable that gets passed in that has an elements in it:
$elements = array(
array(
'type'=>'text,
'value'=>'some value',
'atts'=>null,
),
array(
'type'=>'text,
'value'=>'some other value',
'atts'=>null,
),
);
{{ elements }}
what im trying to do is generate those elements from the macros. they work just fine when called by name:
{{ forms.text(element.0.name,element.0.value,element.0.atts) }}
However what i want to do is something like this:
{% for element in elements %}
{{ forms[element.type](element.name,element.value,element.atts) }}
{% endfor %}
I have tried the following all resulting in the same error:
{{ forms["'"..element.type.."'"](element.name,element.value,element.atts) }}
{{ forms.(element.type)(element.name,element.value,element.atts) }}
{{ forms.{element.type}(element.name,element.value,element.atts) }}
This unfortunately throws the following error:
Fatal error: Uncaught exception 'LogicException' with message 'Attribute "value" does not exist for Node "Twig_Node_Expression_GetAttr".' in Twig\Environment.php on line 541
Any help or advice on a solution or a better schema to use would be very helpful.
I just thought other people may want the answer to this, as provide by fabpot:
This is indeed something that is not supported: calling a macro with a dynamic name (I have added a proper exception to be clearer about the issue).
If you really want to do that, you can do so with the following code:
{{ attribute(forms, element.type, [element.name,element.value,element.atts]) }}
-fabpot
https://github.com/twigphp/Twig/issues/922#issuecomment-11133299
Dynamic macros may not be supported in Twig.
But there is a simple workaround since you can dynamically include other templates.
Example:
Let's say you have a bunch of content modules or content blocks (or however you wanna call them) for your site. And you have Twig macros responsible of rendering each of these modules.
{# modules.twig #}
{% macro module1(config) %}
<div>module one</div>
{% endmacro %}
{% macro module2(config) %}
<div>module two</div>
{% endmacro %}
{% macro module3(config) %}
<div>module three</div>
{% endmacro %}
Now, what you need to dynamically call these macros is to add an extra template for each, like so:
{# module1.twig #}
{% import "modules.twig" as modules %}
{{ modules.module1(config) }}
{# module2.twig #}
{% import "modules.twig" as modules %}
{{ modules.module2(config) }}
{# module3.twig #}
{% import "modules.twig" as modules %}
{{ modules.module3(config) }}
Finally, in your actual page template you just include the template instead of calling the macro.
{# template.twig #}
{# this is the macro's name to be called #}
{% set macro = 'module2' %}
{# this is just a config object to be passed to the macro #}
{% set config = {} %}
{% include macro ~ '.twig' with { config: config } only %}
Et voilá, (dynamically produced) output will be <div>module two</div>.

Resources