1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19:
20: class DefaultFormRenderer extends Object implements IFormRenderer
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: public $wrappers = array(
60: 'form' => array(
61: 'container' => NULL,
62: 'errors' => TRUE,
63: ),
64:
65: 'error' => array(
66: 'container' => 'ul class=error',
67: 'item' => 'li',
68: ),
69:
70: 'group' => array(
71: 'container' => 'fieldset',
72: 'label' => 'legend',
73: 'description' => 'p',
74: ),
75:
76: 'controls' => array(
77: 'container' => 'table',
78: ),
79:
80: 'pair' => array(
81: 'container' => 'tr',
82: '.required' => 'required',
83: '.optional' => NULL,
84: '.odd' => NULL,
85: ),
86:
87: 'control' => array(
88: 'container' => 'td',
89: '.odd' => NULL,
90:
91: 'errors' => FALSE,
92: 'description' => 'small',
93: 'requiredsuffix' => '',
94:
95: '.required' => 'required',
96: '.text' => 'text',
97: '.password' => 'text',
98: '.file' => 'text',
99: '.submit' => 'button',
100: '.image' => 'imagebutton',
101: '.button' => 'button',
102: ),
103:
104: 'label' => array(
105: 'container' => 'th',
106: 'suffix' => NULL,
107: 'requiredsuffix' => '',
108: ),
109:
110: 'hidden' => array(
111: 'container' => 'div',
112: ),
113: );
114:
115:
116: protected $form;
117:
118:
119: protected $counter;
120:
121:
122:
123: 124: 125: 126: 127: 128:
129: public function render(Form $form, $mode = NULL)
130: {
131: if ($this->form !== $form) {
132: $this->form = $form;
133: $this->init();
134: }
135:
136: $s = '';
137: if (!$mode || $mode === 'begin') {
138: $s .= $this->renderBegin();
139: }
140: if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
141: $s .= $this->renderErrors();
142: }
143: if (!$mode || $mode === 'body') {
144: $s .= $this->renderBody();
145: }
146: if (!$mode || $mode === 'end') {
147: $s .= $this->renderEnd();
148: }
149: return $s;
150: }
151:
152:
153:
154:
155: public function setClientScript()
156: {
157: trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
158: return $this;
159: }
160:
161:
162:
163: 164: 165: 166:
167: protected function init()
168: {
169: 170: $wrapper = & $this->wrappers['control'];
171: foreach ($this->form->getControls() as $control) {
172: if ($control->isRequired() && isset($wrapper['.required'])) {
173: $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
174: }
175:
176: $el = $control->getControlPrototype();
177: if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
178: $el->class($wrapper['.' . $el->type], TRUE);
179: }
180: }
181: }
182:
183:
184:
185: 186: 187: 188:
189: public function renderBegin()
190: {
191: $this->counter = 0;
192:
193: foreach ($this->form->getControls() as $control) {
194: $control->setOption('rendered', FALSE);
195: }
196:
197: if (strcasecmp($this->form->getMethod(), 'get') === 0) {
198: $el = clone $this->form->getElementPrototype();
199: $uri = explode('?', (string) $el->action, 2);
200: $el->action = $uri[0];
201: $s = '';
202: if (isset($uri[1])) {
203: foreach (preg_split('#[;&]#', $uri[1]) as $param) {
204: $parts = explode('=', $param, 2);
205: $name = urldecode($parts[0]);
206: if (!isset($this->form[$name])) {
207: $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
208: }
209: }
210: $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
211: }
212: return $el->startTag() . $s;
213:
214:
215: } else {
216: return $this->form->getElementPrototype()->startTag();
217: }
218: }
219:
220:
221:
222: 223: 224: 225:
226: public function renderEnd()
227: {
228: $s = '';
229: foreach ($this->form->getControls() as $control) {
230: if ($control instanceof HiddenField && !$control->getOption('rendered')) {
231: $s .= (string) $control->getControl();
232: }
233: }
234: if ($s) {
235: $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
236: }
237:
238: return $s . $this->form->getElementPrototype()->endTag() . "\n";
239: }
240:
241:
242:
243: 244: 245: 246: 247:
248: public function renderErrors(IFormControl $control = NULL)
249: {
250: $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
251: if (count($errors)) {
252: $ul = $this->getWrapper('error container');
253: $li = $this->getWrapper('error item');
254:
255: foreach ($errors as $error) {
256: $item = clone $li;
257: if ($error instanceof Html) {
258: $item->add($error);
259: } else {
260: $item->setText($error);
261: }
262: $ul->add($item);
263: }
264: return "\n" . $ul->render(0);
265: }
266: }
267:
268:
269:
270: 271: 272: 273:
274: public function renderBody()
275: {
276: $s = $remains = '';
277:
278: $defaultContainer = $this->getWrapper('group container');
279: $translator = $this->form->getTranslator();
280:
281: foreach ($this->form->getGroups() as $group) {
282: if (!$group->getControls() || !$group->getOption('visual')) continue;
283:
284: $container = $group->getOption('container', $defaultContainer);
285: $container = $container instanceof Html ? clone $container : Html::el($container);
286:
287: $s .= "\n" . $container->startTag();
288:
289: $text = $group->getOption('label');
290: if ($text instanceof Html) {
291: $s .= $text;
292:
293: } elseif (is_string($text)) {
294: if ($translator !== NULL) {
295: $text = $translator->translate($text);
296: }
297: $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
298: }
299:
300: $text = $group->getOption('description');
301: if ($text instanceof Html) {
302: $s .= $text;
303:
304: } elseif (is_string($text)) {
305: if ($translator !== NULL) {
306: $text = $translator->translate($text);
307: }
308: $s .= $this->getWrapper('group description')->setText($text) . "\n";
309: }
310:
311: $s .= $this->renderControls($group);
312:
313: $remains = $container->endTag() . "\n" . $remains;
314: if (!$group->getOption('embedNext')) {
315: $s .= $remains;
316: $remains = '';
317: }
318: }
319:
320: $s .= $remains . $this->renderControls($this->form);
321:
322: $container = $this->getWrapper('form container');
323: $container->setHtml($s);
324: return $container->render(0);
325: }
326:
327:
328:
329: 330: 331: 332: 333:
334: public function renderControls($parent)
335: {
336: if (!($parent instanceof FormContainer || $parent instanceof FormGroup)) {
337: throw new InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
338: }
339:
340: $container = $this->getWrapper('controls container');
341:
342: $buttons = NULL;
343: foreach ($parent->getControls() as $control) {
344: if ($control->getOption('rendered') || $control instanceof HiddenField || $control->getForm(FALSE) !== $this->form) {
345: 346:
347: } elseif ($control instanceof Button) {
348: $buttons[] = $control;
349:
350: } else {
351: if ($buttons) {
352: $container->add($this->renderPairMulti($buttons));
353: $buttons = NULL;
354: }
355: $container->add($this->renderPair($control));
356: }
357: }
358:
359: if ($buttons) {
360: $container->add($this->renderPairMulti($buttons));
361: }
362:
363: $s = '';
364: if (count($container)) {
365: $s .= "\n" . $container . "\n";
366: }
367:
368: return $s;
369: }
370:
371:
372:
373: 374: 375: 376: 377:
378: public function renderPair(IFormControl $control)
379: {
380: $pair = $this->getWrapper('pair container');
381: $pair->add($this->renderLabel($control));
382: $pair->add($this->renderControl($control));
383: $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
384: $pair->class($control->getOption('class'), TRUE);
385: if (++$this->counter % 2) $pair->class($this->getValue('pair .odd'), TRUE);
386: $pair->id = $control->getOption('id');
387: return $pair->render(0);
388: }
389:
390:
391:
392: 393: 394: 395: 396:
397: public function renderPairMulti(array $controls)
398: {
399: $s = array();
400: foreach ($controls as $control) {
401: if (!$control instanceof IFormControl) {
402: throw new InvalidArgumentException("Argument must be array of IFormControl instances.");
403: }
404: $s[] = (string) $control->getControl();
405: }
406: $pair = $this->getWrapper('pair container');
407: $pair->add($this->renderLabel($control));
408: $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
409: return $pair->render(0);
410: }
411:
412:
413:
414: 415: 416: 417: 418:
419: public function renderLabel(IFormControl $control)
420: {
421: $head = $this->getWrapper('label container');
422:
423: if ($control instanceof Checkbox || $control instanceof Button) {
424: return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : '');
425:
426: } else {
427: $label = $control->getLabel();
428: $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
429: if ($label instanceof Html) {
430: $label->setHtml($label->getHtml() . $suffix);
431: $suffix = '';
432: }
433: return $head->setHtml((string) $label . $suffix);
434: }
435: }
436:
437:
438:
439: 440: 441: 442: 443:
444: public function renderControl(IFormControl $control)
445: {
446: $body = $this->getWrapper('control container');
447: if ($this->counter % 2) $body->class($this->getValue('control .odd'), TRUE);
448:
449: $description = $control->getOption('description');
450: if ($description instanceof Html) {
451: $description = ' ' . $control->getOption('description');
452:
453: } elseif (is_string($description)) {
454: $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
455:
456: } else {
457: $description = '';
458: }
459:
460: if ($control->isRequired()) {
461: $description = $this->getValue('control requiredsuffix') . $description;
462: }
463:
464: if ($this->getValue('control errors')) {
465: $description .= $this->renderErrors($control);
466: }
467:
468: if ($control instanceof Checkbox || $control instanceof Button) {
469: return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
470:
471: } else {
472: return $body->setHtml((string) $control->getControl() . $description);
473: }
474: }
475:
476:
477:
478: 479: 480: 481:
482: protected function getWrapper($name)
483: {
484: $data = $this->getValue($name);
485: return $data instanceof Html ? clone $data : Html::el($data);
486: }
487:
488:
489:
490: 491: 492: 493:
494: protected function getValue($name)
495: {
496: $name = explode(' ', $name);
497: $data = & $this->wrappers[$name[0]][$name[1]];
498: return $data;
499: }
500:
501: }
502: