Symfony view loop parameter
Ik heb in Symfony een view met daarin een loop die een andere (partial) view laadt. Deze partial view maakt gebruik van cycle om odd en even te onderscheiden. Zolang ik de cycle binnen de hoofd Twig-template gebruik, is er niets aan de hand, maar op het moment dat ik deze partial view vanuit m'n controller wil renderen met $this->renderView() krijg ik de volgende foutmelding:
Variable "loop" does not exist in desktop/ride/ride.html.twig at line 1
Weet iemand hoe ik aan renderView() de loop-variabele kan meegeven vanuit een foreach() in mijn controller?
Roel
Dan kun je ook naar CSS oplossing kijken, bijv om een tr op te maken: tr:nth-child(2n+0) {background: #ff0000;}
Als het goed is, render je de view vanuit je Controller? In dat geval kun je namelijk gewoon return $this->render('AcmeBundle:default:index.html.twig', array('entities' => $entities)); doen bijvoorbeeld.
In je view kun je dan het volgende schrijven:
Code (php)
1
2
3
2
3
{% for entity in entities %}
{% include 'AcmeBundle:default/blocks:item.html.twig' %}
{% endfor %}
{% include 'AcmeBundle:default/blocks:item.html.twig' %}
{% endfor %}
Volgens mij wordt het loop object dan meegegeven aan item.html.twig, maar kan zijn dat je hem apart moet opgeven met with{}
Zie ook:
http://twig.sensiolabs.org/doc/tags/include.html
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
{% for ride in rides %}
{{ include('desktop/ride/ride.html.twig', { 'ride': ride }) }}
{% else %}
<tr>
<td colspan="6">Het lijkt erop dat er (nog) geen ritten zijn geregistreerd.</td>
</tr>
{% endfor %}
{{ include('desktop/ride/ride.html.twig', { 'ride': ride }) }}
{% else %}
<tr>
<td colspan="6">Het lijkt erop dat er (nog) geen ritten zijn geregistreerd.</td>
</tr>
{% endfor %}
In m'n controller doe ik het zo:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
foreach ($rides as $ride) {
$output .= $this->renderView(
Toolkit::getDeviceType() . '/ride/ride.html.twig',
array(
'ride' => $ride
)
);
}
$output .= $this->renderView(
Toolkit::getDeviceType() . '/ride/ride.html.twig',
array(
'ride' => $ride
)
);
}
Inmiddels heb ik het werkend met de hierboven getoonde CSS-oplossing. Bedankt!
Fijn dat je een oplossing gevonden hebt :)
Ik wil nog wel even inhaken op de code die je geplaatst hebt.
Ik zie namelijk dat je in je controller een view rendert en vervolgens in een $output variabele stopt. Dat is eigenlijk per definitie geen correct gebruik van views. Die gebruik je uitsluitend om webpagina's of e-mails te renderen; Twig is immers krachtig genoeg om dat zelf allemaal te kunnen doen :)
Dat is ook meteen de reden dat je de loop-variabele niet kunt gebruiken in je view; de renderView functie van Controller geeft die variabele niet standaard mee, maar Twig doet dat wel bij een include (als je de only flag niet gebruikt).
Het is namelijk zo dat ik een tabel heb waarin ik dan de rijen in m'n Twig-template als partial view toon. Omdat ik wil filteren op deze tabel, bouw ik nadat je op 'zoek' klikt in m'n controller een nieuw overzicht op en dan render ik weer die rijen, om ze vervolgens als HTML terug te sturen.
Is dit geen correct gebruik?
Dat kan heel makkelijk via een Form Type. Je kunt bijvoorbeeld een PersistentCollection, gewone Collection of een QueryBuilder (of whatever) aan je Form meegeven waar je op basis van de filterbare velden op filtert.
Hier heb ik zelf weleens iets voor gebouwd. Is wel een beetje een rommelcode, maar dan heb je een idee hoe je zoiets kunt doen.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<?php
namespace Fizz\Klaver\AdminBundle\Form\Entity;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Filter extends AbstractType
{
/**
* @var string
* The name of the entity to be selected from
*/
private $entityName;
/**
* @var EntityManager
* The entity manager
*/
private $entityManager;
/**
* @var array Fields to use as a filter
*/
private $filterFields = array();
private $customFilters = array();
/**
*
* @param $entityName The entity to use for filtering
* @param $filterFields Explaination up above
* @param EntityManager $em Give the entity manager
*/
public function __construct($entityName, $filterFields, EntityManager $em)
{
$this->entityName = $entityName;
$this->entityManager = $em;
$this->filterFields = $filterFields;
}
/**
* Factory for extended filters
* @param $filterName
* @param FormBuilderInterface $builder
* @return mixed
*/
public function factory($filterName, FormBuilderInterface $builder)
{
$filterName = __NAMESPACE__ . '\Filter\\' . ucfirst($filterName) . 'Filter';
return new $filterName($builder);
}
/**
* The form builder automatically builds the form based on the given fields and choices
* @param FormBuilderInterface $builder
* @param array $options
* @throws \Doctrine\ORM\Mapping\MappingException
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$md = $this->entityManager->getClassMetadata($this->entityName);
foreach($this->filterFields as $key => $field)
{
$choices = array();
if(!is_numeric($key) && !$md->hasAssociation($key) && is_array($field) && isset($field['choices']) && is_array($field['choices']))
{
$choices[''] = '';
foreach($field['choices'] as $choice => $alias)
$choices[$choice] = $alias;
}
elseif($md->hasAssociation($key) && $field['method'] == 'choice')
{
$mapping = $md->getAssociationMapping($key);
$entities = $this->entityManager->getRepository($mapping['targetEntity'])->findAll();
$choices[''] = '';
$value = 'get' . ucfirst($field['field']);
foreach($entities as $choice)
{
$getId = 'get' . ucfirst($field['idField']);
$choices[$choice->$getId()] = $choice->$value();
}
}
elseif(isset($field['method']) && $field['method'] != 'choice')
$field = $key;
elseif(!is_string($field))
{
$this->customFilters[$key] = $this->factory($field['filter'], $builder);
$builder->add($this->customFilters[$key]->create());
}
if(count($choices)) {
$builder
->add($builder->create($key, 'choice', array(
'choices' => $choices,
'label' => false,
'expanded' => false,
'multiple' => false,
'required' => false
)));
}
elseif(is_string($field))
$builder->add($builder->create($field, 'text', array('label' => false, 'required' => false)));
}
$builder->add('filter', 'submit', array('label' => 'Filter'));
}
/**
* Adds a where condition to the query considering the possibility of there already being
* added conditions
* @param QueryBuilder $query
* @param $where
* @return QueryBuilder
*/
private function addWhere(QueryBuilder $query, $where)
{
if(strpos($query->getDQL(), ' WHERE ') === false)
return $query->where($where);
else
return $query->andWhere($where);
}
/**
* Filter on all entered criteria.
* If the field name has an association with an internal relationship, it will try to find a match in this relationship using a join
* If the field name exists in the entity, it will try to find a match in the entity using the determined method
* If a custom filter is given using the 'filter' option, it will execute the filter method of this custom filter
* If the conditions exist in a 'choice', it will try to match the given text exactly using the value provided.
* @param QueryBuilder $query
* @param array $data
* @return QueryBuilder
*/
public function filterQuery(QueryBuilder &$query, $data)
{
$fn['like'] = function($query, $fieldName, $field, $value) {
$this->addWhere($query, $fieldName . ' LIKE :' . strtolower($field))
->setParameter(strtolower($field), '%' . addcslashes($value, '%_') . '%');
};
$fn['eq'] = function($query, $fieldName, $field, $value) {
$this->addWhere($query, $fieldName . ' = :' . strtolower($field))
->setParameter(strtolower($field), $value);
};
if(!$data)
return $query;
$md = $this->entityManager->getClassMetadata($this->entityName);
$i = 97;
foreach($data as $field => $value)
{
$func = 'like';
if(!strlen($value))
continue;
$aliases = $query->getRootAliases();
$fieldName = $aliases[0] . '.' . $field;
if($md->hasAssociation($field))
{
while(in_array(chr($i), $aliases))
$i++;
$func = $this->filterFields[$field]['method'] == 'choice' ? 'eq' : 'like';
$this->filterFields[$field] = $this->filterFields[$field]['method'] == 'choice' ? (isset($this->filterFields[$field]['idField']) ? $this->filterFields[$field]['idField'] : 'id') : $this->filterFields[$field]['field'];
$fieldName = chr($i) . '.' . $this->filterFields[$field];
$query->join($aliases[0] . '.' . $field, chr($i));
$i++;
}
elseif(isset($this->filterFields[$field]) && is_array($this->filterFields[$field]))
$func = 'eq';
if(isset($this->customFilters[$field]))
$this->customFilters[$field]->filter($query, $value);
else
$fn[$func]($query, $fieldName, $field, $value);
}
$query = $query->getQuery();
$query->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
return $query;
}
/**
* The name of the form
* @return string
*/
public function getName()
{
return 'filters';
}
}
//[/code] < Lol phphulp sluit de code tag niet af de 1e keer :P
namespace Fizz\Klaver\AdminBundle\Form\Entity;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Filter extends AbstractType
{
/**
* @var string
* The name of the entity to be selected from
*/
private $entityName;
/**
* @var EntityManager
* The entity manager
*/
private $entityManager;
/**
* @var array Fields to use as a filter
*/
private $filterFields = array();
private $customFilters = array();
/**
*
* @param $entityName The entity to use for filtering
* @param $filterFields Explaination up above
* @param EntityManager $em Give the entity manager
*/
public function __construct($entityName, $filterFields, EntityManager $em)
{
$this->entityName = $entityName;
$this->entityManager = $em;
$this->filterFields = $filterFields;
}
/**
* Factory for extended filters
* @param $filterName
* @param FormBuilderInterface $builder
* @return mixed
*/
public function factory($filterName, FormBuilderInterface $builder)
{
$filterName = __NAMESPACE__ . '\Filter\\' . ucfirst($filterName) . 'Filter';
return new $filterName($builder);
}
/**
* The form builder automatically builds the form based on the given fields and choices
* @param FormBuilderInterface $builder
* @param array $options
* @throws \Doctrine\ORM\Mapping\MappingException
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$md = $this->entityManager->getClassMetadata($this->entityName);
foreach($this->filterFields as $key => $field)
{
$choices = array();
if(!is_numeric($key) && !$md->hasAssociation($key) && is_array($field) && isset($field['choices']) && is_array($field['choices']))
{
$choices[''] = '';
foreach($field['choices'] as $choice => $alias)
$choices[$choice] = $alias;
}
elseif($md->hasAssociation($key) && $field['method'] == 'choice')
{
$mapping = $md->getAssociationMapping($key);
$entities = $this->entityManager->getRepository($mapping['targetEntity'])->findAll();
$choices[''] = '';
$value = 'get' . ucfirst($field['field']);
foreach($entities as $choice)
{
$getId = 'get' . ucfirst($field['idField']);
$choices[$choice->$getId()] = $choice->$value();
}
}
elseif(isset($field['method']) && $field['method'] != 'choice')
$field = $key;
elseif(!is_string($field))
{
$this->customFilters[$key] = $this->factory($field['filter'], $builder);
$builder->add($this->customFilters[$key]->create());
}
if(count($choices)) {
$builder
->add($builder->create($key, 'choice', array(
'choices' => $choices,
'label' => false,
'expanded' => false,
'multiple' => false,
'required' => false
)));
}
elseif(is_string($field))
$builder->add($builder->create($field, 'text', array('label' => false, 'required' => false)));
}
$builder->add('filter', 'submit', array('label' => 'Filter'));
}
/**
* Adds a where condition to the query considering the possibility of there already being
* added conditions
* @param QueryBuilder $query
* @param $where
* @return QueryBuilder
*/
private function addWhere(QueryBuilder $query, $where)
{
if(strpos($query->getDQL(), ' WHERE ') === false)
return $query->where($where);
else
return $query->andWhere($where);
}
/**
* Filter on all entered criteria.
* If the field name has an association with an internal relationship, it will try to find a match in this relationship using a join
* If the field name exists in the entity, it will try to find a match in the entity using the determined method
* If a custom filter is given using the 'filter' option, it will execute the filter method of this custom filter
* If the conditions exist in a 'choice', it will try to match the given text exactly using the value provided.
* @param QueryBuilder $query
* @param array $data
* @return QueryBuilder
*/
public function filterQuery(QueryBuilder &$query, $data)
{
$fn['like'] = function($query, $fieldName, $field, $value) {
$this->addWhere($query, $fieldName . ' LIKE :' . strtolower($field))
->setParameter(strtolower($field), '%' . addcslashes($value, '%_') . '%');
};
$fn['eq'] = function($query, $fieldName, $field, $value) {
$this->addWhere($query, $fieldName . ' = :' . strtolower($field))
->setParameter(strtolower($field), $value);
};
if(!$data)
return $query;
$md = $this->entityManager->getClassMetadata($this->entityName);
$i = 97;
foreach($data as $field => $value)
{
$func = 'like';
if(!strlen($value))
continue;
$aliases = $query->getRootAliases();
$fieldName = $aliases[0] . '.' . $field;
if($md->hasAssociation($field))
{
while(in_array(chr($i), $aliases))
$i++;
$func = $this->filterFields[$field]['method'] == 'choice' ? 'eq' : 'like';
$this->filterFields[$field] = $this->filterFields[$field]['method'] == 'choice' ? (isset($this->filterFields[$field]['idField']) ? $this->filterFields[$field]['idField'] : 'id') : $this->filterFields[$field]['field'];
$fieldName = chr($i) . '.' . $this->filterFields[$field];
$query->join($aliases[0] . '.' . $field, chr($i));
$i++;
}
elseif(isset($this->filterFields[$field]) && is_array($this->filterFields[$field]))
$func = 'eq';
if(isset($this->customFilters[$field]))
$this->customFilters[$field]->filter($query, $value);
else
$fn[$func]($query, $fieldName, $field, $value);
}
$query = $query->getQuery();
$query->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
return $query;
}
/**
* The name of the form
* @return string
*/
public function getName()
{
return 'filters';
}
}
//[/code] < Lol phphulp sluit de code tag niet af de 1e keer :P
Op deze manier maak je hem dan aan:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
$this->createForm(
$filterType = new Filter(
$entity,
array(
'title' => 'title',
'category' => array('field' => 'value', 'method' => 'choice', 'idField' => 'id'),
),
$this->getDoctrine()->getManager()
));
/** @var QueryBuilder $qb **/
$filterType->filterQuery($qb, $filterForm->getData());
$filterType = new Filter(
$entity,
array(
'title' => 'title',
'category' => array('field' => 'value', 'method' => 'choice', 'idField' => 'id'),
),
$this->getDoctrine()->getManager()
));
/** @var QueryBuilder $qb **/
$filterType->filterQuery($qb, $filterForm->getData());
Gewijzigd op 14/08/2015 16:41:36 door Richard Snijders