Files
web-site/Internal/package-hypertext/FormElementsTrait.php
T
2026-07-01 16:44:17 +03:00

712 lines
16 KiB
PHP

<?php namespace ZN\Hypertext;
/**
* ZN PHP Web Framework
*
* "Simplicity is the ultimate sophistication." ~ Da Vinci
*
* @package ZN
* @license MIT [http://opensource.org/licenses/MIT]
* @author Ozan UYKUN [ozan@znframework.com]
*/
use ZN\IS;
use ZN\Base;
use ZN\Lang;
use ZN\Request;
use ZN\Singleton;
use ZN\Protection\Json;
trait FormElementsTrait
{
/**
* Keeps Enctypes
*
* @var array
*/
protected $enctypes =
[
'multipart' => 'multipart/form-data',
'application' => 'application/x-www-form-urlencoded',
'text' => 'text/plain'
];
/**
* Keeps Postback Data
*
* @var array
*/
protected $postback = [];
/**
* Keeps Validate Rules
*
* @var array
*/
protected $validate = [];
/**
* Keeps validation method messages
*
* @var string
*/
protected $vMethodMessages = NULL;
/**
* Keeps javascript validation function
*
* @var array
*/
protected $getJavascriptValidationFunction;
/**
* Email control
*
* @return string
*/
public function vEmail()
{
return $this->onInvalidEventPattern('^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$', 'email');
}
/**
* URL control
*
* @return string
*/
public function vUrl()
{
return $this->onInvalidEventPattern('^(\w+:)?//.*', 'url');
}
/**
* Numeric control
*
* @return string
*/
public function vNumeric()
{
return $this->onInvalidEventPattern('^[0-9]+$', 'numeric');
}
/**
* Alpha control
*
* @return string
*/
public function vAlpha()
{
return $this->onInvalidEventPattern('^[a-zA-Z]+$', 'alpha');
}
/**
* Alnum control
*
* @return string
*/
public function vAlnum()
{
return $this->onInvalidEventPattern('^([a-zA-Z]|[0-9])+$', 'alnum');
}
/**
* Required control
*
* @return string
*/
public function vRequired()
{
return $this->onInvalidEventPattern('^.+$', 'required');
}
/**
* Message control
*
* @return string
*
* @codeCoverageIgnore
*/
public function vMessage(string $message)
{
$this->vMethodMessages = $message;
return $this;
}
/**
* Between control
*
* @param int $min = 0
* @param int $max = 0
*
* @return string
*/
public function vBetween(int $min = 0, int $max = 0)
{
return $this->setJavascriptValidation
(
'ZNValidationBetween',
['betweenBoth' => [':p1' => $min, ':p2' => $max]],
['betweenBoth' => [$min, $max]],
[$min, $max]
);
}
/**
* Captcha control
*
* @return string
*/
public function vCaptcha()
{
return $this->setJavascriptValidation('ZNValidationCaptcha', 'captcha');
}
/**
* Captcha control
*
* @return string
*/
public function vAnswer()
{
return $this->setJavascriptValidation('ZNValidationAnswer', 'answer');
}
/**
* Match control
*
* @return string
*/
public function vMatch(string $selector)
{
return $this->setJavascriptValidation('ZNValidationMatch', 'match', ['match' => $selector], [Base::presuffix($selector, '\'')]);
}
/**
* Match password control
*
* @return string
*/
public function vMatchPassword(string $selector)
{
return $this->setJavascriptValidation('ZNValidationMatch', 'matchPassword', ['matchPassword' => $selector], [Base::presuffix($selector, '\'')]);
}
/**
* Phone control
*
* @return string
*/
public function vPhone(?string $pattern = NULL)
{
return $this->setJavascriptValidation('ZNValidationPhone', 'phone', ['phone' => $pattern], [Base::presuffix($pattern, '\'')]);
}
/**
* Pattern control
*
* @param string $pattern
*
* @return string
*/
public function vPattern(string $pattern)
{
return $this->onInvalidEventPatternWithoutValidate($pattern, 'pattern');
}
/**
* Identity control
*
* @return string
*/
public function vIdentity()
{
return $this->setJavascriptValidation('ZNValidationIdentity', 'identity');
}
/**
* Numeric control
*
* @param int $min = 0
* @param int $max = NULL
*
* @return string
*/
public function vLimit(int $min = 0, ?int $max = NULL)
{
$key['minchar'] = [':p1' => $min];
$typ['minchar'] = $min;
if( $max !== NULL )
{
$key['maxchar'] = [':p1' => $max];
$typ['maxchar'] = $max;
}
return $this->onInvalidEventPattern('.{' . $min . ',' . $max . '}', $key, [], $typ);
}
/**
* Minchar control
*
* @param int $min = 0
* @param int $max = NULL
*
* @return string
*/
public function vMinchar(int $min = 0)
{
return $this->minlength($min)->onInvalidEventAttributeValidate('minchar', [':p1' => $min], ['minchar' => $min]);
}
/**
* Maxchar control
*
* @param int $max = 0
*
* @return string
*/
public function vMaxchar(int $max = 0)
{
return $this->maxlength($max)->onInvalidEventAttributeValidate('maxchar', [':p1' => $max], ['maxchar' => $max]);
}
/**
* Defines validate rules.
*
* @param mixed ...$validate
*
* @return self
*/
public function validate(...$validate)
{
if( $this->validate === [] )
{
$this->validate = $validate;
}
else
{
$this->validate = array_merge($this->validate, $validate);
}
return $this;
}
/**
* Sets postback
*
* @param bool $postback = true
* @param string $type = 'post' - options[post|get]
*
* @return self
*/
public function postBack(bool $postback = true, string $type = 'post')
{
$this->postback['bool'] = $postback;
$this->postback['type'] = $type;
return $this;
}
/**
* Controls CSRF
*
* @return self
*/
public function csrf()
{
$this->settings['token'] = true;
return $this;
}
/**
* Exluding data
*
* @param mixed $exclude
*
* @return self
*/
public function excluding($exclude)
{
$this->settings['exclude'] = (array) $exclude;
return $this;
}
/**
* Including data
*
* @param mixed $include
*
* @return self
*/
public function including($include)
{
$this->settings['include'] = (array) $include;
return $this;
}
/**
* Sets process type.
*
* @param string $type - [insert|update]
*
* @return self
*/
public function process(string $type)
{
$this->settings['process'] = $type;
return $this;
}
/**
* Duplicate check with insert process
*
* @return self
*/
public function duplicateCheck()
{
$this->settings['duplicateCheck'] = true;
return $this;
}
/**
* Database Where Clause
*
* @param mixed $column
* @param string $value = NULL
* @param string $logical = 'and'
*
* @return self
*/
public function where($column, ?string $value = NULL, string $logical = 'and')
{
$this->settings['where'] = true;
$this->settings['whereValue'] = $value;
$this->settings['whereColumn'] = $column;
Singleton::class('ZN\Database\DB')->where($column, $value, $logical);
return $this;
}
/**
* Defines SQL Query
*
* @param string $query
*
* @return self
*/
public function query(string $query)
{
$this->settings['query'] = $query;
return $this;
}
/**
* Sets table
*
* @param string $table
*
* @return self
*/
public function table(string $table)
{
$this->settings['table'] = $table;
return $this;
}
/**
* Order
*
* @param string $type = 'desc'
* @param string $flags = 'regular'
*
* @return self
*/
public function order(string $type = 'desc', string $flags = 'regular')
{
$this->settings['order']['type'] = $type;
$this->settings['order']['flags'] = $flags;
return $this;
}
/**
* Sets attributes
*
* @param array $attr = []
*
* @return self
*/
public function attr(array $attr = [])
{
$settings = [];
if( isset($this->settings['attr']) && is_array($this->settings['attr']) )
{
$settings = $this->settings['attr'];
}
$this->settings['attr'] = array_merge($settings, $attr);
return $this;
}
/**
* Sets Form Action
*
* @param string $url = NULL
*
* @return self
*/
public function action(?string $url = NULL)
{
$this->settings['attr']['action'] = IS::url($url) ? $url : Request::getSiteURL($url);
return $this;
}
/**
* Sets Form Enctype
*
* @param string $enctype
*
* @return self
*/
public function enctype(string $enctype)
{
if( isset($this->enctypes[$enctype]) )
{
$enctype = $this->enctypes[$enctype];
}
$this->_element(__FUNCTION__, $enctype);
return $this;
}
/**
* Sets select options
*
* @param mixed $key
* @param string $value = NULL
*
* @return self
*/
public function option($key, ?string $value = NULL)
{
if( is_array($key) )
{
$this->settings['option'] = $key;
}
else
{
$this->settings['option'][$key] = $value;
}
return $this;
}
/**
* Protected set javascript validation
*/
protected function setJavascriptValidation($name, $lang, $rule = NULL, $param = [])
{
$this->getJavascriptValidationFunction[$name] = $function = $name . md5($name);
$this->validate[] = $rule ?: $lang;
return $this->onkeyup($function . '(this, ' . Base::suffix(implode(', ', $param), ', ') . '\''.$this->setCustomValidity($lang).'\')')
->required();
}
/**
* Protected on invalid event pattern
*/
protected function onInvalidEventPattern($pattern, $key, $check = [], $type = NULL)
{
$this->validate[] = $type ?: $key;
return $this->onInvalidEventPatternWithoutValidate($pattern, $key, $check);
}
/**
* Protected on invalid event pattern without validate
*/
protected function onInvalidEventPatternWithoutValidate($pattern, $key, $check = [])
{
return $this->required()->pattern($pattern)->onInvalidEventCustomValidity($key, $check);
}
/**
* Protected on invalid event attribute validate
*/
protected function onInvalidEventAttributeValidate($key, $check = [], $rule = NULL)
{
$this->validate[] = $rule ?: $key;
return $this->required()->onInvalidEventCustomValidity($key, $check);
}
/**
* Protected on invalid event custom validity
*/
protected function onInvalidEventCustomValidity($key, $check = [])
{
$this->vMethodMessages .= $this->setCustomValidity($key, $check) . ' ';
return $this;
}
/**
* Protected get validate method messages
*/
protected function getVMethodMessages()
{
if( $this->vMethodMessages !== NULL )
{
$this->oninvalid('setCustomValidity(\'' . rtrim($this->vMethodMessages) . '\')')->oninput('setCustomValidity(\'\')')->validate(...$this->validate);
$this->vMethodMessages = NULL;
}
}
/**
* Protected set custom validity
*/
protected function setCustomValidity($key, $check = [])
{
$message = '';
if( is_scalar($key) )
{
$message = $this->getValidationLangValue($key, $check);
}
else foreach( $key as $k => $c )
{
$message .= $this->getValidationLangValue($k, $c) . ' ';
}
return rtrim($message);
}
/**
* Protected get validation lang value
*/
protected function getValidationLangValue($key, $check)
{
$check[':name'] = 'Input';
return Lang::default('ZN\Validation\ValidationDefaultLanguage')::select('ViewObjects', 'validation:'.$key, $check) ?: $key;
}
/**
* Protected Postback
*/
protected function _postback($name, &$default, $type = NULL)
{
if( isset($this->postback['bool']) && $this->postback['bool'] === true )
{
$method = ! empty($this->method) ? $this->method : $this->postback['type'];
$this->postback = [];
if( $type === 'checkbox' || $type === 'radio' )
{
// @codeCoverageIgnoreStart
if( $method::$name() === $default )
{
$this->checked();
}
else if( $method::all() )
{
unset($this->settings['attr']['checked']);
}
// @codeCoverageIgnoreEnd
}
else
{
$default = Singleton::class('ZN\Validation\Data')->postBack($name, $method) ?: $default;
}
}
}
/**
* Protected Validate
*/
protected function _validate($name, $attrName)
{
if( ! empty($this->validate) )
{
$this->validateUsageThisForm = true;
$session = Singleton::class('ZN\Storage\Session');
$validate[$name] = $this->validate;
$validate[$name]['value'] = $this->settings['attr']['alias'] ?? $attrName;
$rules = array_merge($session->select('FormValidationRules' . $this->getValidationFormName) ?: [], $validate);
$session->insert('FormValidationMethod' . $this->getValidationFormName, $this->method);
$session->insert('FormValidationRules' . $this->getValidationFormName, $rules);
$this->validate = [];
}
}
/**
* Protected Get Row
*/
protected function _getrow($type, $value, &$attributes)
{
if( $row = ($this->settings['getrow'] ?? NULL) )
{
$rowval = $row->{$attributes['name']} ?? NULL;
if( $type === 'textarea' || $type === 'select' )
{
return $value ?: $rowval; // @codeCoverageIgnore
}
$attributes['value'] = $value ?: $rowval;
// For radio
if( $type === 'radio' && $value == $rowval )
{
$attributes['checked'] = 'checked'; // @codeCoverageIgnore
}
// For checkbox
if( $type === 'checkbox' )
{
// @codeCoverageIgnoreStart
if( Json::check($rowval) )
{
$rowval = json_decode($rowval, true);
if( in_array($value, $rowval) )
{
$attributes['checked'] = 'checked';
}
}
else
{
if( ! empty($rowval) )
{
$attributes['checked'] = 'checked';
}
}
// @codeCoverageIgnoreEnd
}
}
return $value;
}
}