Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • DefaultFormRenderer
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Forms\Rendering;
  9: 
 10: use Nette;
 11: use Nette\Utils\Html;
 12: use Nette\Utils\IHtmlString;
 13: 
 14: 
 15: /**
 16:  * Converts a Form into the HTML output.
 17:  */
 18: class DefaultFormRenderer implements Nette\Forms\IFormRenderer
 19: {
 20:     use Nette\SmartObject;
 21: 
 22:     /**
 23:      *  /--- form.container
 24:      *
 25:      *    /--- error.container
 26:      *      .... error.item [.class]
 27:      *    \---
 28:      *
 29:      *    /--- hidden.container
 30:      *      .... HIDDEN CONTROLS
 31:      *    \---
 32:      *
 33:      *    /--- group.container
 34:      *      .... group.label
 35:      *      .... group.description
 36:      *
 37:      *      /--- controls.container
 38:      *
 39:      *        /--- pair.container [.required .optional .odd]
 40:      *
 41:      *          /--- label.container
 42:      *            .... LABEL
 43:      *            .... label.suffix
 44:      *            .... label.requiredsuffix
 45:      *          \---
 46:      *
 47:      *          /--- control.container [.odd]
 48:      *            .... CONTROL [.required .text .password .file .submit .button]
 49:      *            .... control.requiredsuffix
 50:      *            .... control.description
 51:      *            .... control.errorcontainer + control.erroritem
 52:      *          \---
 53:      *        \---
 54:      *      \---
 55:      *    \---
 56:      *  \--
 57:      * @var array of HTML tags */
 58:     public $wrappers = [
 59:         'form' => [
 60:             'container' => null,
 61:         ],
 62: 
 63:         'error' => [
 64:             'container' => 'ul class=error',
 65:             'item' => 'li',
 66:         ],
 67: 
 68:         'group' => [
 69:             'container' => 'fieldset',
 70:             'label' => 'legend',
 71:             'description' => 'p',
 72:         ],
 73: 
 74:         'controls' => [
 75:             'container' => 'table',
 76:         ],
 77: 
 78:         'pair' => [
 79:             'container' => 'tr',
 80:             '.required' => 'required',
 81:             '.optional' => null,
 82:             '.odd' => null,
 83:             '.error' => null,
 84:         ],
 85: 
 86:         'control' => [
 87:             'container' => 'td',
 88:             '.odd' => null,
 89: 
 90:             'description' => 'small',
 91:             'requiredsuffix' => '',
 92:             'errorcontainer' => 'span class=error',
 93:             'erroritem' => '',
 94: 
 95:             '.required' => 'required',
 96:             '.text' => 'text',
 97:             '.password' => 'text',
 98:             '.file' => 'text',
 99:             '.email' => 'text',
100:             '.number' => 'text',
101:             '.submit' => 'button',
102:             '.image' => 'imagebutton',
103:             '.button' => 'button',
104:         ],
105: 
106:         'label' => [
107:             'container' => 'th',
108:             'suffix' => null,
109:             'requiredsuffix' => '',
110:         ],
111: 
112:         'hidden' => [
113:             'container' => null,
114:         ],
115:     ];
116: 
117:     /** @var Nette\Forms\Form */
118:     protected $form;
119: 
120:     /** @var int */
121:     protected $counter;
122: 
123: 
124:     /**
125:      * Provides complete form rendering.
126:      * @param  Nette\Forms\Form
127:      * @param  string 'begin', 'errors', 'ownerrors', 'body', 'end' or empty to render all
128:      * @return string
129:      */
130:     public function render(Nette\Forms\Form $form, $mode = null)
131:     {
132:         if ($this->form !== $form) {
133:             $this->form = $form;
134:         }
135: 
136:         $s = '';
137:         if (!$mode || $mode === 'begin') {
138:             $s .= $this->renderBegin();
139:         }
140:         if (!$mode || strtolower($mode) === 'ownerrors') {
141:             $s .= $this->renderErrors();
142: 
143:         } elseif ($mode === 'errors') {
144:             $s .= $this->renderErrors(null, false);
145:         }
146:         if (!$mode || $mode === 'body') {
147:             $s .= $this->renderBody();
148:         }
149:         if (!$mode || $mode === 'end') {
150:             $s .= $this->renderEnd();
151:         }
152:         return $s;
153:     }
154: 
155: 
156:     /**
157:      * Renders form begin.
158:      * @return string
159:      */
160:     public function renderBegin()
161:     {
162:         $this->counter = 0;
163: 
164:         foreach ($this->form->getControls() as $control) {
165:             $control->setOption('rendered', false);
166:         }
167: 
168:         if ($this->form->isMethod('get')) {
169:             $el = clone $this->form->getElementPrototype();
170:             $query = parse_url($el->action, PHP_URL_QUERY);
171:             $el->action = str_replace("?$query", '', $el->action);
172:             $s = '';
173:             foreach (preg_split('#[;&]#', $query, -1, PREG_SPLIT_NO_EMPTY) as $param) {
174:                 $parts = explode('=', $param, 2);
175:                 $name = urldecode($parts[0]);
176:                 if (!isset($this->form[$name])) {
177:                     $s .= Html::el('input', ['type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])]);
178:                 }
179:             }
180:             return $el->startTag() . ($s ? "\n\t" . $this->getWrapper('hidden container')->setHtml($s) : '');
181: 
182:         } else {
183:             return $this->form->getElementPrototype()->startTag();
184:         }
185:     }
186: 
187: 
188:     /**
189:      * Renders form end.
190:      * @return string
191:      */
192:     public function renderEnd()
193:     {
194:         $s = '';
195:         foreach ($this->form->getControls() as $control) {
196:             if ($control->getOption('type') === 'hidden' && !$control->getOption('rendered')) {
197:                 $s .= $control->getControl();
198:             }
199:         }
200:         if (iterator_count($this->form->getComponents(true, Nette\Forms\Controls\TextInput::class)) < 2) {
201:             $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
202:         }
203:         if ($s) {
204:             $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
205:         }
206: 
207:         return $s . $this->form->getElementPrototype()->endTag() . "\n";
208:     }
209: 
210: 
211:     /**
212:      * Renders validation errors (per form or per control).
213:      * @return string
214:      */
215:     public function renderErrors(Nette\Forms\IControl $control = null, $own = true)
216:     {
217:         $errors = $control
218:             ? $control->getErrors()
219:             : ($own ? $this->form->getOwnErrors() : $this->form->getErrors());
220:         if (!$errors) {
221:             return '';
222:         }
223:         $container = $this->getWrapper($control ? 'control errorcontainer' : 'error container');
224:         $item = $this->getWrapper($control ? 'control erroritem' : 'error item');
225: 
226:         foreach ($errors as $error) {
227:             $item = clone $item;
228:             if ($error instanceof IHtmlString) {
229:                 $item->addHtml($error);
230:             } else {
231:                 $item->setText($error);
232:             }
233:             $container->addHtml($item);
234:         }
235:         return "\n" . $container->render($control ? 1 : 0);
236:     }
237: 
238: 
239:     /**
240:      * Renders form body.
241:      * @return string
242:      */
243:     public function renderBody()
244:     {
245:         $s = $remains = '';
246: 
247:         $defaultContainer = $this->getWrapper('group container');
248:         $translator = $this->form->getTranslator();
249: 
250:         foreach ($this->form->getGroups() as $group) {
251:             if (!$group->getControls() || !$group->getOption('visual')) {
252:                 continue;
253:             }
254: 
255:             $container = $group->getOption('container', $defaultContainer);
256:             $container = $container instanceof Html ? clone $container : Html::el($container);
257: 
258:             $id = $group->getOption('id');
259:             if ($id) {
260:                 $container->id = $id;
261:             }
262: 
263:             $s .= "\n" . $container->startTag();
264: 
265:             $text = $group->getOption('label');
266:             if ($text instanceof IHtmlString) {
267:                 $s .= $this->getWrapper('group label')->addHtml($text);
268: 
269:             } elseif ($text != null) { // intentionally ==
270:                 if ($translator !== null) {
271:                     $text = $translator->translate($text);
272:                 }
273:                 $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
274:             }
275: 
276:             $text = $group->getOption('description');
277:             if ($text instanceof IHtmlString) {
278:                 $s .= $text;
279: 
280:             } elseif ($text != null) { // intentionally ==
281:                 if ($translator !== null) {
282:                     $text = $translator->translate($text);
283:                 }
284:                 $s .= $this->getWrapper('group description')->setText($text) . "\n";
285:             }
286: 
287:             $s .= $this->renderControls($group);
288: 
289:             $remains = $container->endTag() . "\n" . $remains;
290:             if (!$group->getOption('embedNext')) {
291:                 $s .= $remains;
292:                 $remains = '';
293:             }
294:         }
295: 
296:         $s .= $remains . $this->renderControls($this->form);
297: 
298:         $container = $this->getWrapper('form container');
299:         $container->setHtml($s);
300:         return $container->render(0);
301:     }
302: 
303: 
304:     /**
305:      * Renders group of controls.
306:      * @param  Nette\Forms\Container|Nette\Forms\ControlGroup
307:      * @return string
308:      */
309:     public function renderControls($parent)
310:     {
311:         if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
312:             throw new Nette\InvalidArgumentException('Argument must be Nette\Forms\Container or Nette\Forms\ControlGroup instance.');
313:         }
314: 
315:         $container = $this->getWrapper('controls container');
316: 
317:         $buttons = null;
318:         foreach ($parent->getControls() as $control) {
319:             if ($control->getOption('rendered') || $control->getOption('type') === 'hidden' || $control->getForm(false) !== $this->form) {
320:                 // skip
321: 
322:             } elseif ($control->getOption('type') === 'button') {
323:                 $buttons[] = $control;
324: 
325:             } else {
326:                 if ($buttons) {
327:                     $container->addHtml($this->renderPairMulti($buttons));
328:                     $buttons = null;
329:                 }
330:                 $container->addHtml($this->renderPair($control));
331:             }
332:         }
333: 
334:         if ($buttons) {
335:             $container->addHtml($this->renderPairMulti($buttons));
336:         }
337: 
338:         $s = '';
339:         if (count($container)) {
340:             $s .= "\n" . $container . "\n";
341:         }
342: 
343:         return $s;
344:     }
345: 
346: 
347:     /**
348:      * Renders single visual row.
349:      * @return string
350:      */
351:     public function renderPair(Nette\Forms\IControl $control)
352:     {
353:         $pair = $this->getWrapper('pair container');
354:         $pair->addHtml($this->renderLabel($control));
355:         $pair->addHtml($this->renderControl($control));
356:         $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), true);
357:         $pair->class($control->hasErrors() ? $this->getValue('pair .error') : null, true);
358:         $pair->class($control->getOption('class'), true);
359:         if (++$this->counter % 2) {
360:             $pair->class($this->getValue('pair .odd'), true);
361:         }
362:         $pair->id = $control->getOption('id');
363:         return $pair->render(0);
364:     }
365: 
366: 
367:     /**
368:      * Renders single visual row of multiple controls.
369:      * @param  Nette\Forms\IControl[]
370:      * @return string
371:      */
372:     public function renderPairMulti(array $controls)
373:     {
374:         $s = [];
375:         foreach ($controls as $control) {
376:             if (!$control instanceof Nette\Forms\IControl) {
377:                 throw new Nette\InvalidArgumentException('Argument must be array of Nette\Forms\IControl instances.');
378:             }
379:             $description = $control->getOption('description');
380:             if ($description instanceof IHtmlString) {
381:                 $description = ' ' . $description;
382: 
383:             } elseif ($description != null) { // intentionally ==
384:                 if ($control instanceof Nette\Forms\Controls\BaseControl) {
385:                     $description = $control->translate($description);
386:                 }
387:                 $description = ' ' . $this->getWrapper('control description')->setText($description);
388: 
389:             } else {
390:                 $description = '';
391:             }
392: 
393:             $control->setOption('rendered', true);
394:             $el = $control->getControl();
395:             if ($el instanceof Html && $el->getName() === 'input') {
396:                 $el->class($this->getValue("control .$el->type"), true);
397:             }
398:             $s[] = $el . $description;
399:         }
400:         $pair = $this->getWrapper('pair container');
401:         $pair->addHtml($this->renderLabel($control));
402:         $pair->addHtml($this->getWrapper('control container')->setHtml(implode(' ', $s)));
403:         return $pair->render(0);
404:     }
405: 
406: 
407:     /**
408:      * Renders 'label' part of visual row of controls.
409:      * @return Html
410:      */
411:     public function renderLabel(Nette\Forms\IControl $control)
412:     {
413:         $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
414:         $label = $control->getLabel();
415:         if ($label instanceof Html) {
416:             $label->addHtml($suffix);
417:             if ($control->isRequired()) {
418:                 $label->class($this->getValue('control .required'), true);
419:             }
420:         } elseif ($label != null) { // @intentionally ==
421:             $label .= $suffix;
422:         }
423:         return $this->getWrapper('label container')->setHtml($label);
424:     }
425: 
426: 
427:     /**
428:      * Renders 'control' part of visual row of controls.
429:      * @return Html
430:      */
431:     public function renderControl(Nette\Forms\IControl $control)
432:     {
433:         $body = $this->getWrapper('control container');
434:         if ($this->counter % 2) {
435:             $body->class($this->getValue('control .odd'), true);
436:         }
437: 
438:         $description = $control->getOption('description');
439:         if ($description instanceof IHtmlString) {
440:             $description = ' ' . $description;
441: 
442:         } elseif ($description != null) { // intentionally ==
443:             if ($control instanceof Nette\Forms\Controls\BaseControl) {
444:                 $description = $control->translate($description);
445:             }
446:             $description = ' ' . $this->getWrapper('control description')->setText($description);
447: 
448:         } else {
449:             $description = '';
450:         }
451: 
452:         if ($control->isRequired()) {
453:             $description = $this->getValue('control requiredsuffix') . $description;
454:         }
455: 
456:         $control->setOption('rendered', true);
457:         $el = $control->getControl();
458:         if ($el instanceof Html && $el->getName() === 'input') {
459:             $el->class($this->getValue("control .$el->type"), true);
460:         }
461:         return $body->setHtml($el . $description . $this->renderErrors($control));
462:     }
463: 
464: 
465:     /**
466:      * @param  string
467:      * @return Html
468:      */
469:     protected function getWrapper($name)
470:     {
471:         $data = $this->getValue($name);
472:         return $data instanceof Html ? clone $data : Html::el($data);
473:     }
474: 
475: 
476:     /**
477:      * @param  string
478:      * @return mixed
479:      */
480:     protected function getValue($name)
481:     {
482:         $name = explode(' ', $name);
483:         $data = &$this->wrappers[$name[0]][$name[1]];
484:         return $data;
485:     }
486: }
487: 
Nette 2.4-20170829 API API documentation generated by ApiGen 2.8.0