I have such problem with symfony2.
I have base.html.twig and this base is the template for others twigs.
I want to render controller, which contains form with locale-type input field.
The form will be used to change language of site.
My LanguageController:
<?php
namespace Soczewki\PlatformaBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LanguageController extends Controller {
public function createAction(Request $Request)
{
$form = $this->createFormBuilder()
->setMethod('POST')
->setAction(null)
->add('locale', 'locale', array(
'label' => ' ',
'choices' => array('pl' => 'Polski', 'de' => 'Deutsch', 'en' => 'English'),
'data' => $this->getRequest()->getLocale(),
'required' => true))
->add('submit', 'submit')
->getForm();
$form->handleRequest($Request);
if($form->isValid()) {
$this->getRequest()->setLocale('en');
}
return $this->render("SoczewkiPlatformaBundle::myForm.html.twig",
array(
'form' => $form->createView(),
'req' => $r
));
}
}
The whole base.html.twig:
<html>
<head>
<title>{% block pageTitle %}{% endblock %}</title>
{#js#}
<script src="{{ asset('bundles/soczewkiplatforma/js/jquery.js') }}"></script>
<script src="{{ asset('bundles/soczewkiplatforma/js/bootstrap.js') }}"></script>
{#/js#}
{% block stylesheets %}
<link href="{{ asset('bundles/soczewkiplatforma/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ asset('bundles/soczewkiplatforma/css/my-style.css') }}" rel="stylesheet">
{% endblock stylesheets %}
</head>
<body>
{% if app.security.getToken().getUser().getAuthKey() is defined %}
{% if app.security.getToken().getUser().getAuthKey() is not empty %}
{% set diff = ( app.security.getToken().getUser().getEndDate()|date('U') - "now"|date('U') ) / 3600 %}
<div style="width: 300px; height: 140px; position: absolute; bottom: 90px; right: 50px;" class="alert alert-danger alert-error">
<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<strong>Uwaga!</strong> Twoje konto nadal nie zostało aktywowane!<br />
Pozostało Ci: <span class="badge">{{ diff[0:2] }} godzin {{ ( diff[3:] * 60 )[0:2] }} minut </span>
<br /><br />
<button type="button" class="btn btn-danger">Wpisz kod</button>
<button type="button" class="btn btn-default" data-dismiss="alert">Zamknij</button>
</div>
{% endif %}
{% set hasParams = app.request.get('id') %}
{% if hasParams is empty %}
{% set currentPath = url( app.request.attributes.get( '_route', app.request.attributes.get('_route_params'))) %}
{% else %}
{% set currentPath = url( app.request.attributes.get( '_route', app.request.attributes.get('_route_params')),{'id': 0} ) %}
{% endif %}
{% set dirs = currentPath|split('/') %}
{% set flag = "" %}
{% set url = "" %}
<ol class="breadcrumb">
<li>Główna</li>
{% for key, dir in dirs %}
{% if url is not empty %}
{% set url = url ~ '/' ~ dir %}
{% else %}
{% set url = url ~ dir %}
{% endif %}
{% if flag == true %}
{% if '=' in dir %}
{% set _temp = dir|split('=') %}
{% else %}
{% set _temp = dir|split(' ') %}
{% endif %}
{% if key + 1 == dirs|length %}
<li class="active"> {{ _temp[0]|capitalize|replace('-',' ') }} </li>
{% else %}
<li> {{ _temp[0]|capitalize|replace('-',' ') }} </li>
{% endif %}
{% endif %}
{% if dir == 'app_dev.php' %}
{% set flag = true %}
{% endif %}
{% endfor %}
<li class="dropdown" style="float: right !important;">
Zalogowano jako: {{ app.security.getToken().getUser().getName() }} <span class="caret"></span>
<ul class="dropdown-menu" role="menu">
<li>Moje konteło</li>
<li>Wyloguj</li>
</ul>
</li>
</ol>
{% else %}
<ol class="breadcrumb">
<li>Główna</li>
<!--- logowanie --->
<li class="dropdown" style="float: right !important;">
Zaloguj <span class="caret"></span>
<div class="dropdown-menu" style="padding: 15px; padding-bottom: 0px; left: -200px;">
<form action="{{ path('login_check') }}" method="post">
<label for="username">Email/NIP:</label>
<input type="text" id="username" name="_username" />
<br />
<label for="password">Hasełko:</label>
<input type="password" id="password" name="_password" />
<br /><br />
<button type="submit">Loguj do Afganistanu</button>
</form>
</div>
</li>
<!--- log ---->
</ol>
{% endif %}
{{ render(controller('SoczewkiPlatformaBundle:Language:create')) }}
{% block pageContainer %}
{% endblock %}
</body>
</html>
Nothing shows and I get too any error.
Why is it like that?
// codes updated
myForm.html.twig:
{{ dump(req) }}
{{ form(form) }}
and LocaleListener.php:
<?php
namespace Soczewki\PlatformaBundle\Translations;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale = 'pl')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get('_locale')) {
$request->getSession()->set('_locale', $locale);
} else {
// if no explicit locale has been set on this request, use one from the session
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 17)),
);
}
}
You have created a situation where you are calling the same method an infinite amount of times until the page just dies.
You original base is being rendered which is in the end rendering the SoczewkiPlatformaBundle::base.html.twig template. Within this template a render() is calling an action that is then rendering the SoczewkiPlatformaBundle::base.html.twig template, which then calls the render(), which then render SoczewkiPlatformaBundle::base.html.twig, and on and on...
You need to specify a template for the form to be rendered in rather than the base and then call that in your changeAction.
For example...
Soczewki\PlatformaBundle\Controller\LanguageController
public function changeAction(Request $Request)
{
// ..
return $this->render("SoczewkiPlatformaBundle:Locale:change_form.html.twig",
array(
'form' => isset($form) ? $form->createView() : null,
));
}
SoczewkiPlatformaBundle:Locale:change_form.html.twig
{{ form_start(form, {'method': 'POST', 'action': 'your_locale_change_route' }) }}
{{ form_row(form.locale) }}
{{ form_end(form) }}
Your next problem would be that your changeAction isn't actually performing an action (apart from creating a form), although you may just be wanting that page to show before you get to that part.
You need to add this code where you want that the form appear
{{ form(form) }}
Edited after the comments below;
Create a file named myForm.html.twig and put in SoczewkiPlatformaBundle;
Now write this code inside:
{{ form(form) }}
now your controller should be:
public function changeAction(Request $Request)
{
$form = $this->createFormBuilder()
->add('locale', 'locale', array(
'label' => 'user.locale',
'required' => true,
'choices' => array('en' => 'English', 'de' => 'Deutsch', 'pl' => 'Polish')
))
->getForm();
$form->handleRequest($Request);
return $this->render("SoczewkiPlatformaBundle::myForm.html.twig",
array(
'form' => $form->createView(),
));
}
Related
I try to insert images with relations OneToMany, but I get an error.
Expected argument of type "App\Entity\CategoryImage", "array" given at property path "images".
I have entities Category and CategoryImage:
/**
* #ORM\Entity(repositoryClass="App\Repository\CategoryRepository")
*/
class Category
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\CategoryImage", mappedBy="category", orphanRemoval=true ,cascade={"persist", "remove"})
*/
private $images;
.......
}
/**
* #ORM\Entity(repositoryClass="App\Repository\CategoryImageRepository")
*/
class CategoryImage
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $path;
/** #var \SplFileInfo */
protected $file;
....
}
Two forms:
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$data = $builder->getForm()->getData();
$builder
->add('name')
->add('parent',OptionType::class,[
'class'=>Category::class,
'selectType'=>'flatexcept',
'required'=>false,
])
->add('images',CollectionType::class,[
'entry_type' => ImageTypeMultiple::class,
'entry_options' => ['label' => false],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
]);
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Category::class,
]);
}
}
class ImageTypeMultiple extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options )
{
$builder->add('file',FileType::class,[
'label'=>false,
'required'=>false,
'constraints' => [
new Image([
'maxSize' => '5M'
])
]
]);
}
public function getBlockPrefix()
{
return 'image_multiple';
}
}
My custom ImageTypeMultiple form_theme widget:
{% block image_multiple_widget -%}
{% spaceless %}
{% if form.vars.value.path|default(null) is null %}
<label for="{{ form.file.vars.id }}" class="image-upload btn btn-secondary btn-block"><i class="fa-cloud fa icon"></i> {{ 'select'|trans }}</label>
{% else %}
<img src="{{ form.vars.value.path|imagine_filter('sylius_small') }}" />
<label for="{{ form.file.vars.id }}" class="image-upload btn btn-secondary btn-block"><i class="fa-cloud fa icon"></i> {{ 'replace'|trans }}</label>
{% endif %}
<div class="d-none">
{{ form_widget(form.file) }}
</div>
<div class="element">
{{- form_errors(form.file) -}}
</div>
{% endspaceless %}
{% endblock %}
{% block collection_widget -%}
{% import _self as self %}
{% set attr = attr|merge({'class': attr.class|default ~ ' controls collection-widget'}) %}
{% spaceless %}
<div data-form-type="collection" {{ block('widget_container_attributes') }}
{% if prototype is defined and allow_add %}
data-prototype='{{ self.collection_item(prototype, allow_delete, 'Delete', '__name__')|e }}'
{%- endif -%}
>
{{ form_errors(form) }}
{% if prototypes|default is iterable %}
{% for key, subPrototype in prototypes %}
<input type="hidden" data-form-prototype="{{ key }}" value="{{ self.collection_item(subPrototype, allow_delete, button_delete_label, '__name__')|e }}" />
{% endfor %}
{% endif %}
<div data-form-collection="list" class="row">
{% for child in form %}
{{ self.collection_item(child, allow_delete, 'Delete', loop.index0) }}
{% endfor %}
</div>
{% if prototype is defined and allow_add %}
<a href="#" class="btn btn-primary" data-form-collection="add">
Add
</a>
{% endif %}
</div>
{% endspaceless %}
{%- endblock collection_widget %}
{% macro collection_item(form, allow_delete, button_delete_label, index) %}
{% spaceless %}
<div data-form-collection="item" data-form-collection-index="{{ index }}" class='col-lg-2 col-md-4 col-sm-6 imagetype_widget ml-3 mb-3' >
{{ form_widget(form) }}
{% if allow_delete %}
<a href="#" data-form-collection="delete" class="btn btn-warning btn-block">
<i class="fa fa-trash "></i>
Delete
</a>
{% endif %}
</div>
{% endspaceless %}
{% endmacro %}
My controller:
/**
* #Route("/{id}/edit", name="category_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Category $category): Response
{
$form = $this->createForm(CategoryType::class, $category);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
...}
}
I can open my edit page without error:
but after I select an image under images field and submit, it shows this error:
It shows
Expected argument of type "App\Entity\CategoryImage", "array" given at property path "images".
on the line $form->handleRequest($request) in the controller ,something goes wrong, I have been dealing with this problem for days, but cannot solve it.
I'm trying to override some fields of bootstrap_4_layout.html.twig, but i found some problems with radio_widget block. I need to change the div element to label, but when I ovverride the block, the radio element is duplicated on view. The div in question is that with class custom-control
{% block radio_widget %}
{% set parent_label_class = parent_label_class|default(label_attr.class|default('')) %}
{% if 'radio-custom' in parent_label_class %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' custom-control-input')|trim}) %}
<div class="custom-control custom-radio{{ 'radio-inline' in parent_label_class ? ' custom-control-inline' }}">
{{ form_label(form, null, { widget: parent() }) }}
</div>
{% else %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) %}
<div class="form-check{{ 'radio-inline' in parent_label_class ? ' form-check-inline' }}">
{{ form_label(form, null, { widget: parent() }) }}
</div>
{% endif %}
{% endblock radio_widget %}
Analizyng the dom with the console, I see that are created two element with custom-control class. Why?
I found the solution. I override the block radio_widget and changed the div element with label:
{% block radio_widget %}
{% set parent_label_class = parent_label_class|default(label_attr.class|default('')) %}
{% if 'radio-custom' in parent_label_class %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' custom-control-input')|trim}) %}
<label class="custom-control custom-radio{{ 'radio-inline' in parent_label_class ? ' custom-control-inline' }}">
{{ form_label(form, null, { widget: parent() }) }}
</label>
{% else %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) %}
<div class="form-check{{ 'radio-inline' in parent_label_class ? ' form-check-inline' }}">
{{ form_label(form, null, { widget: parent() }) }}
</div>
{% endif %}
{% endblock radio_widget %}
Then I override also the block checkbox_radio_label. Inside it I substituted the base code:
// ...
{{ widget|raw }}
// ...
With:
{% set attr = attr|merge({class: (attr.class|default('') ~ ' custom-control-input')|trim}) %}
<input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
For reference about bootstrap 4 twig blocks see this link:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig
everybody i'm trying to add a captcha to my form with Gregwar/CaptchaBundle but i face a probleme :
Variable "captcha_code" does not exist when i pay this part of my code :
========================================================================
<div class="col-sm-8 col-sm-offset-2">
{% block captcha_widget %}
{% spaceless %}
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
{{ form_row(form.captcha) }}
{% endspaceless %}
{% endblock %}
</div>
Variable form inside of widget block already contains the captcha field (not the whole form), so the correct way is to use form_widget(form) instead of form_row(form.captcha) :
{% block captcha_widget %}
{% spaceless %}
<img src="{{ captcha_code }}" title="captcha" width="{{ captcha_width }}" height="{{ captcha_height }}" />
{{ form_widget(form) }}
{% endspaceless %}
{% endblock %}
thank you but i figured out it by myselfe the best way to solve my problem was to
configure captcha field in my form builder
$builder->add('captcha', 'Gregwar\CaptchaBundle\Type\CaptchaType',array(
'width' => 200,
'height' => 50,
'length' => 6,
'quality' => 90,
'distortion' => true,
'background_color' => [115, 194, 251],
'max_front_lines' => 0,
'max_behind_lines' => 0,
'attr' => array('class' => 'form-control',
'rows'=> "6"
)
));
and in my twig template only call this small part of code :
<div class="col-sm-8 col-sm-offset-2">
{% block captcha_widget %}
{% spaceless %}
{{ form_row(form.captcha) }}
{% endspaceless %}
{% endblock %}
</div>
To validate a form I am using a standard:
{{ form_widget(form.save, {'attr': {'class': 'btn btn-sm btn-danger'}, 'label': 'Submit form'}) }}
I want to insert a fontawsome icon in the button. I tried:
{{ form_widget(form.save, {'attr': {'class': 'btn btn-sm btn-danger'}, 'label': '<i class="fa fa-envelope-o"></i> Submit form'}) }}
But it is not working; obviously
Any idea how to that?
I would define a new form template in the same view (or in a template if you need to reuse the code). More details here
{% extends '::base.html.twig' %}
{% form_theme form _self %}
{%- block submit_widget -%}
{%- set type = type|default('submit') -%}
{%- if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
<i class="fa fa-envelope-o"></i>
{{ label|trans({}, translation_domain) }}
</button>
{%- endblock submit_widget -%}
{% block content %}
{# ... render the form #}
{{ form_row(form.age) }}
{% endblock %}
EDIT
You can also extend ButtonType to allow icon_before and icon_after in order to add icons easily in form definition :
$form->add('submitReportV2Show', SubmitType::class, array(
'label' => 'My test',
'icon_before' => 'fa-refresh',
'icon_after' => 'fa-refresh',
'attr' => array('class' => 'btn btn-sm btn-success'
)));
Create a new class src/bundle/Form/Extension:
namespace YourBundle\ToolBoxBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class IconButtonExtension extends AbstractTypeExtension
{
public function getExtendedType()
{
return ButtonType::class;
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['icon_before'] = $options['icon_before'] ?? '';
$view->vars['icon_after'] = $options['icon_after'] ?? '';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'icon_before' => null,
'icon_after' => null
]);
}
}
Declare it in service src/bundle/Resources/config/service.yml
bundle.tools.form.type_extension.icon_button:
class: YourBundle\ToolBoxBundle\Form\Extension\IconButtonExtension
tags:
- { name: 'form.type_extension', extended_type: 'Symfony\Component\Form\Extension\Core\Type\ButtonType' }
app/Resources/views/Form/fields.html.twig
{%- block button_widget -%}
{%- if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- elseif label is same as(false) -%}
{% set translation_domain = false %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
{% if icon_before is defined and icon_before is not null %}
<i class="fa {{ icon_before }}"></i>
{% endif %}
{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{% if icon_after is defined and icon_after is not null %}
<i class="fa {{ icon_after }}"></i>
{% endif %}
</button>
{%- endblock button_widget -%}
sdespont's answer is the correct answer and worthy of being selected. I have, however, extended the functionality of it to include adding the custom fa class icon as well as whether the icon is placed to the left or right of the button text.
Since this functionality accepts variables the best thing to do is to create a template to be reused instead of customising just the view.
Form template: app/Resources/views/form/submit.html.twig
{# app/Resources/views/form/submit.html.twig #}
{% block submit_widget %}
{% set type = type|default('submit') %}
{% if label is empty %}
{% if label_format is not empty %}
{% set label = label_format|replace({
'%name%' : name,
'%id%' : id,
}) %}
{% else %}
{% set label = name|humanize %}
{% endif %}
{% endif %}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
{% if fa is defined %}
{% if left is defined and left %}
<i class="fa {{ fa }}"></i>
{% endif %}
{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{% if right is defined and right %}
<i class="fa {{ fa }}"></i>
{% endif %}
{% else %}
{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{% endif %}
</button>
{% endblock submit_widget %}
Controller:
$form = $this->createFormBuilder($user)
...
->add('submit', SubmitType::class, array(
'attr'=> array('class'=>'myclass')
))
->getForm();
Twig template:
{{ form_widget(form.submit, {'fa' : 'fa-long-arrow-right','right' : true}) }}
You can set any old fa icon and even sizing like so: fa-long-arrow-right fa-2x
The easiest, you can put your button with html and form vars:
<button type="submit" name="{{ form.send.vars.full_name }}" id="{{ form.send.vars.id }}" class="btn btn-sm btn-danger"><i class="fa fa-envelope-o"></i></button><
You can just add a new custom service css class per icon
/*
* css selector for a class attribute that starts with "btn-fa-" or has " btn-fa-" in it:
*/
[class^="btn-fa-"]:before,
[class*=" btn-fa-"]:before
{
font-family: "Font Awesome 5 Free";
font-weight: bold;
margin: 0 6px 0 2px;
}
/*
* And then only 1 setting per font awesome class
*/
.btn-fa-plus:before {
content: '\f067';
}
And add the class to the ButtonType
->add('Add an item', ButtonType::class, [
'attr' => [
'class' => 'btn btn-primary btn-fa-plus',
]
])
In YourFormType.class -> buildForm
->add('submit', SubmitType::class, [
'attr' => [
'class' => 'main-btn primary-btn',
],
'label' => '<i class="fas fa-search"></i> Search',
'label_html' => true,
])
I got stuck while trying to customize the rendering of a specific field in my form.
It looks like this:
$builder->add('players', 'entity', array(
'class' => 'Acme\Bundle\Entity\Player',
'expanded' => true,
'multiple' => true,
'required' => false,
));
The form itself is beeing rendered with a simple:
{% block form_content %}
{% form_theme form 'AcmeBundle:Form:fields_child.html.twig' %}
{{ form_widget(form) }}
{% endblock %}
Now inside fields_child.html.twig i'm extending from another form template but there is nothing special there.
My HTML looks like this:
Players:
- [checkbox-input] 1
Where 1 equals the id of the only player in the database. However instead of rendering the ID im trying to render his picture and full name after the checkbox.
I have tried many combinations of the form theming to override it but failed each time.
Could someone post the twig block to render what i want here?
Thanks
You have to create custom form field type together with custom widget template for it.
http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html
I recently ran into this problem (was a little bit different situation. I needed to show products as table with checkboxes..), Form's child data always returned null value that's why I ended up with this (dirty:)) solution:
Controller action:
...
$productRepository = $entityManager->getRepository('VendorMyBundle:Product');
$products = [];
$formChildren = $productListForm->createView()->children;
foreach ($formChildren['products'] as $formProduct) {
$formProductId = $formProduct->vars['value'];
$productEntity = $productRepository->find($formProductId);
$products[$formProductId] = $productEntity;
}
...
return $this->render('TEMPLATE', [
'productListForm' => $productListForm->createView(),
'products' => $products,
]);
Template:
...
{% for productForm in productListForm.products %}
{% set id = productForm.vars.value %}
<tr>
<td class="check">
{{ form_widget(productForm) }}
</td>
<td class="photo">
{% if products[id].getImages().isEmpty() == false %}
{% set productImage = products[id].getImages().first() %}
<img src="{{ productImage.getWebPath() | imagine_filter('st_product_cabinet_thumbnail') }}" />
{% else %}
<span class="no-image">No image</span>
{% endif %}
</td>
<td class="title">
{{ products[id].getName() }}
</td>
<td class="status">
{{ products[id].getStatusName(products[id].getStatus()) }}
</td>
<td class="price">
<ul>
{% for productPrice in products[id].getPrices() %}
<li>{{ productPrice.getValue() ~ ' ' ~ productPrice.getCurrencyCode() }}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
...