<?php
/**
* User: delboy1978uk
* Date: 07/12/2016
* Time: 01:54
*/
namespace Del\Form\Renderer;
use Del\Form\Collection\FieldCollection;
use Del\Form\AbstractForm;
use Del\Form\Field\FieldInterface;
use Del\Form\FormInterface;
use Del\Form\Renderer\Error\DefaultErrorRender;
use Del\Form\Renderer\Error\ErrorRendererInterface;
use Del\Form\Traits\HasDomTrait;
use DOMDocument;
use DOMElement;
abstract class AbstractFormRenderer implements FormRendererInterface
{
use HasDomTrait;
/** @var DOMElement $form */
protected $form;
/** @var bool $displayErrors */
protected $displayErrors;
/** @var ErrorRendererInterface $errorRenderer */
protected $errorRenderer;
/** @var DOMElement $label The label element */
protected $label;
/** @var DOMElement $element the field element */
protected $element;
/** @var null|\DOMElement $errors The error block html */
protected $errors;
/** @var DOMElement $block The containing html block */
protected $block;
/** @var DOMElement $dynamicContainerBlock */
protected $dynamicContainerBlock;
/** @var FieldInterface $field The current field being processed */
protected $field;
/** @var bool $includeDynamicFormJavascript */
private $includeDynamicFormJavascript = false;
/** @var string $dynamicFormParentName */
private $dynamicFormParentName = '';
/** @var bool $dynamicFormVisible */
private $dynamicFormVisible = false;
public function __construct()
{
$this->resetDom();
}
/**
* resets dom
*/
private function resetDom()
{
$this->setDom(new DOMDocument());
$this->form = $this->getDom()->createElement('form');
$this->errorRenderer = new DefaultErrorRender($this->dom);
}
/**
* @param FormInterface $form
* @param bool $displayErrors
* @return string
*/
public function render(FormInterface $form, $displayErrors = true)
{
$this->displayErrors = $displayErrors;
$this->setFormAttributes($form);
$fields = $form->getFields();
$this->processFields($fields);
$this->getDom()->appendChild($this->form);
$html = $this->getDom()->saveHTML();
$this->resetDom();
$html .= $this->addDynamicFormJavascript();
return $html;
}
/**
* @param FormInterface $form
*/
private function setFormAttributes(FormInterface $form)
{
$attributes = $form->getAttributes();
foreach ($attributes as $key => $value) {
$this->form->setAttribute($key, $value);
}
// set Id as name or method as post if not set
$method = $this->getMethod($form);
$id = $this->getId($form);
$this->form->setAttribute('id', $id);
$this->form->setAttribute('method', $method);
}
/**
* @param FormInterface $form
* @return string
*/
private function getMethod(FormInterface $form)
{
return $form->getMethod() ?: AbstractForm::METHOD_POST;
}
/**
* @param FormInterface $form
* @return string
*/
private function getId(FormInterface $form)
{
return $form->getId() ?: $this->form->getAttribute('name');
}
/**
* @param FieldCollection $fields
* @param string $dynamicTriggerValue
*/
private function processFields(FieldCollection $fields, $dynamicTriggerValue = null)
{
$count = $fields->count();
$x = 1;
$fields->rewind();
while ($fields->valid()) {
$this->field = $fields->current();
$finaliseDynamicBlock = ($x == $count) ? true : false;
$this->renderField($dynamicTriggerValue, $finaliseDynamicBlock);
$x++;
$fields->next();
}
$fields->rewind();
}
/**
* @param string $dynamicTriggerValue
* @param bool $finaliseDynamicBlock
*/
public function renderField($dynamicTriggerValue = null, $finaliseDynamicBlock = false)
{
$this->createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue);
$this->block = $this->createElement('div');
$this->label = $this->renderFieldLabel();
$this->element = $this->field->getRenderer()->render($this->dom, $this->field);
$this->errors = $this->field->isValid() ? null : $this->renderError();
$this->block = $this->renderFieldBlock();
is_null($dynamicTriggerValue)
? $this->form->appendChild($this->block)
: $this->dynamicContainerBlock->appendChild($this->block);
$this->dynamicFormCheck();
$this->finaliseDynamicBlockIfNeeded($finaliseDynamicBlock);
}
/**
* This creates a containing div for dynamic fields which appear only on another fields value
* @param null|string $dynamicTriggerValue
*/
private function createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue)
{
if (!isset($this->dynamicContainerBlock) && $dynamicTriggerValue !== null) {
$this->dynamicContainerBlock = $this->createElement('div');
$this->dynamicContainerBlock->setAttribute('data-dynamic-form', $this->dynamicFormParentName);
$this->dynamicContainerBlock->setAttribute('data-dynamic-form-trigger-value', $dynamicTriggerValue);
$this->dynamicContainerBlock->setAttribute('class', 'dynamic-form-block trigger'.$this->dynamicFormParentName);
$this->dynamicContainerBlock->setAttribute('id', $this->dynamicFormParentName.$dynamicTriggerValue);
$this->dynamicFormVisible === false ? $this->dynamicContainerBlock->setAttribute('style', 'display: none;') : null;
}
}
/**
* Checks current field being processed for dynamic sub forms
*/
private function dynamicFormCheck()
{
if ($this->field->hasDynamicForms()) {
$this->dynamicFormParentName = $this->field->getName();
$value = $this->field->getValue();
$forms = $this->field->getDynamicForms();
$this->includeDynamicFormJavascript = true;
foreach ($forms as $dynamicTriggerValue => $form) {
$this->dynamicFormVisible = ($value == $dynamicTriggerValue);
$dynamicFields = $form->getFields();
$this->processFields($dynamicFields, $dynamicTriggerValue);
}
unset($this->dynamicFormParentName);
}
}
/**
* @param bool $finaliseDynamicBlock
*/
private function finaliseDynamicBlockIfNeeded($finaliseDynamicBlock)
{
if (isset($this->dynamicContainerBlock) && $finaliseDynamicBlock === true) {
$this->form->appendChild($this->dynamicContainerBlock);
unset($this->dynamicContainerBlock);
}
}
/**
* @return DOMElement|null
*/
public function renderError()
{
$errorBlock = null;
if ($this->errorRenderer->shouldRender($this->field) && $this->displayErrors === true) {
$this->block->setAttribute('class', 'has-error ');
$errorBlock = $this->errorRenderer->render($this->field);
}
return $errorBlock;
}
/**
* @return \DOMElement
*/
protected function createLabelElement()
{
$label = $this->createElement('label');
$label->setAttribute('for', $this->field->getId());
if ($this->field->isRequired()) {
$label = $this->addRequiredAsterisk($label);
}
return $label;
}
/**
* @param DomElement $label
* @return DomElement
*/
public function addRequiredAsterisk(DomElement $label)
{
$span = $this->createElement('span');
$span->setAttribute('class', 'text-danger');
$text = $this->createText('* ');
$span->appendChild($text);
$label->appendChild($span);
return $label;
}
/**
* @return string
*/
private function addDynamicFormJavascript()
{
if ($this->includeDynamicFormJavascript === true) {
return "<script type=\"text/javascript\">
$(document).ready(function(){
$('.dynamic-form-block').each(function(){
var Id = $(this).prop('id');
var parentField = $(this).attr('data-dynamic-form');
var parentValue = $(this).attr('data-dynamic-form-trigger-value');
$('input[name=\"'+parentField+'\"]').change(function(){
var val = $(this).val();
if (val == parentValue) {
$('.trigger'+parentField).each(function(){
$(this).attr('style', 'display: none;');
});
$('#'+Id).attr('style', 'display: block;');
}
});
});
});
</script>
";
}
return '';
}
}
|