diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:38:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:38:04 +0000 |
commit | 1ff5c35de5dbd70a782875a91dd2232fd01b002b (patch) | |
tree | 77d9ce5e1bf78b3e6ef79f8f6e7861e2ced3c09b /vendor/ipl/web/src/Control/SearchBar | |
parent | Initial commit. (diff) | |
download | icinga-php-library-upstream.tar.xz icinga-php-library-upstream.zip |
Adding upstream version 0.10.1.upstream/0.10.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/ipl/web/src/Control/SearchBar')
7 files changed, 1072 insertions, 0 deletions
diff --git a/vendor/ipl/web/src/Control/SearchBar/SearchException.php b/vendor/ipl/web/src/Control/SearchBar/SearchException.php new file mode 100644 index 0000000..a89c6ce --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/SearchException.php @@ -0,0 +1,9 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use Exception; + +class SearchException extends Exception +{ +} diff --git a/vendor/ipl/web/src/Control/SearchBar/Suggestions.php b/vendor/ipl/web/src/Control/SearchBar/Suggestions.php new file mode 100644 index 0000000..eae4c97 --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/Suggestions.php @@ -0,0 +1,447 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use Countable; +use Generator; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\FormattedString; +use ipl\Html\FormElement\ButtonElement; +use ipl\Html\FormElement\InputElement; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Stdlib\Contract\Paginatable; +use ipl\Stdlib\Filter; +use ipl\Web\Control\SearchEditor; +use ipl\Web\Filter\QueryString; +use IteratorIterator; +use LimitIterator; +use OuterIterator; +use Psr\Http\Message\ServerRequestInterface; +use Traversable; + +use function ipl\I18n\t; + +abstract class Suggestions extends BaseHtmlElement +{ + const DEFAULT_LIMIT = 50; + const SUGGESTION_TITLE_CLASS = 'suggestion-title'; + + protected $tag = 'ul'; + + /** @var string */ + protected $searchTerm; + + /** @var Traversable */ + protected $data; + + /** @var array */ + protected $default; + + /** @var string */ + protected $type; + + /** @var string */ + protected $failureMessage; + + public function setSearchTerm($term) + { + $this->searchTerm = $term; + + return $this; + } + + public function setData($data) + { + $this->data = $data; + + return $this; + } + + public function setDefault($default) + { + $this->default = $default; + + return $this; + } + + public function setType($type) + { + $this->type = $type; + + return $this; + } + + public function setFailureMessage($message) + { + $this->failureMessage = $message; + + return $this; + } + + /** + * Return whether the relation should be shown for the given column + * + * @param string $column + * + * @return bool + */ + protected function shouldShowRelationFor(string $column): bool + { + return false; + } + + /** + * Create a filter to provide as default for column suggestions + * + * @param string $searchTerm + * + * @return Filter\Rule + */ + abstract protected function createQuickSearchFilter($searchTerm); + + /** + * Fetch value suggestions for a particular column + * + * @param string $column + * @param string $searchTerm + * @param Filter\Chain $searchFilter + * + * @return Traversable + */ + abstract protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $searchFilter); + + /** + * Fetch column suggestions + * + * @param string $searchTerm + * + * @return Traversable + */ + abstract protected function fetchColumnSuggestions($searchTerm); + + protected function filterToTerms(Filter\Chain $filter) + { + $logicalSep = [ + 'label' => QueryString::getRuleSymbol($filter), + 'search' => QueryString::getRuleSymbol($filter), + 'class' => 'logical_operator', + 'type' => 'logical_operator' + ]; + + $terms = []; + foreach ($filter as $child) { + if ($child instanceof Filter\Chain) { + $terms[] = [ + 'search' => '(', + 'label' => '(', + 'type' => 'grouping_operator', + 'class' => 'grouping_operator_open' + ]; + $terms = array_merge($terms, $this->filterToTerms($child)); + $terms[] = [ + 'search' => ')', + 'label' => ')', + 'type' => 'grouping_operator', + 'class' => 'grouping_operator_close' + ]; + } else { + /** @var Filter\Condition $child */ + + $terms[] = [ + 'search' => $child->getColumn(), + 'label' => $child->metaData()->get('columnLabel'), + 'type' => 'column' + ]; + $terms[] = [ + 'search' => QueryString::getRuleSymbol($child), + 'label' => QueryString::getRuleSymbol($child), + 'type' => 'operator' + ]; + $terms[] = [ + 'search' => $child->getValue(), + 'label' => $child->getValue(), + 'type' => 'value' + ]; + } + + $terms[] = $logicalSep; + } + + array_pop($terms); + return $terms; + } + + protected function assembleDefault() + { + if ($this->default === null) { + return; + } + + $attributes = [ + 'type' => 'button', + 'tabindex' => -1, + 'data-label' => $this->default['search'], + 'value' => $this->default['search'] + ]; + if (isset($this->default['type'])) { + $attributes['data-type'] = $this->default['type']; + } elseif ($this->type !== null) { + $attributes['data-type'] = $this->type; + } + + $button = new ButtonElement(null, $attributes); + if (isset($this->default['type']) && $this->default['type'] === 'terms') { + $terms = $this->filterToTerms($this->default['terms']); + $list = new HtmlElement('ul', Attributes::create(['class' => 'comma-separated'])); + foreach ($terms as $data) { + if ($data['type'] === 'column') { + $list->addHtml(new HtmlElement( + 'li', + null, + new HtmlElement('em', null, Text::create($data['label'])) + )); + } + } + + $button->setAttribute('data-terms', json_encode($terms)); + $button->addHtml(FormattedString::create( + t('Search for %s in: %s'), + new HtmlElement('em', null, Text::create($this->default['search'])), + $list + )); + } else { + $button->addHtml(FormattedString::create( + t('Search for %s'), + new HtmlElement('em', null, Text::create($this->default['search'])) + )); + } + + $this->prependHtml(new HtmlElement('li', Attributes::create(['class' => 'default']), $button)); + } + + protected function assemble() + { + if ($this->failureMessage !== null) { + $this->addHtml(new HtmlElement( + 'li', + Attributes::create(['class' => 'failure-message']), + new HtmlElement('em', null, Text::create(t('Can\'t search:'))), + Text::create($this->failureMessage) + )); + return; + } + + if ($this->data === null) { + $data = []; + } elseif ($this->data instanceof Paginatable) { + $this->data->limit(self::DEFAULT_LIMIT); + $data = $this->data; + } else { + $data = new LimitIterator(new IteratorIterator($this->data), 0, self::DEFAULT_LIMIT); + } + + foreach ($data as $term => $meta) { + if (is_int($term)) { + $term = $meta; + } + + $attributes = [ + 'type' => 'button', + 'tabindex' => -1, + 'data-search' => $term, + 'data-title' => $term + ]; + if ($this->type !== null) { + $attributes['data-type'] = $this->type; + } + + if (is_array($meta)) { + foreach ($meta as $key => $value) { + if ($key === 'label') { + $label = $value; + } + + $attributes['data-' . $key] = $value; + } + } else { + $label = $meta; + $attributes['data-label'] = $meta; + } + + $button = (new ButtonElement(null, $attributes)) + ->setAttribute('value', $label) + ->addHtml(Text::create($label)); + if ($this->type === 'column' && $this->shouldShowRelationFor($term)) { + $relationPath = substr($term, 0, strrpos($term, '.')); + $button->getAttributes()->add('class', 'has-details'); + $button->addHtml(new HtmlElement( + 'span', + Attributes::create(['class' => 'relation-path']), + Text::create($relationPath) + )); + } + + $this->addHtml(new HtmlElement('li', null, $button)); + } + + if ($this->hasMore($data, self::DEFAULT_LIMIT)) { + $this->getAttributes()->add('class', 'has-more'); + } + + $showDefault = true; + if ($this->searchTerm && $this->count() === 1) { + // The default option is only shown if the user's input does not result in an exact match + $input = $this->getFirst('li')->getFirst('button'); + $showDefault = $input->getContent() != $this->searchTerm + && $input->getAttributes()->get('data-search')->getValue() != $this->searchTerm; + } + + if ($this->type === 'column' && ! $this->isEmpty() && ! $this->getFirst('li')->getAttributes()->has('class')) { + // The column title is only added if there are any suggestions and the first item is not a title already + $this->prependHtml(new HtmlElement( + 'li', + Attributes::create(['class' => static::SUGGESTION_TITLE_CLASS]), + Text::create(t('Columns')) + )); + } + + if ($showDefault) { + $this->assembleDefault(); + } + + if (! $this->searchTerm && $this->isEmpty()) { + $this->addHtml(new HtmlElement( + 'li', + Attributes::create(['class' => 'nothing-to-suggest']), + new HtmlElement('em', null, Text::create(t('Nothing to suggest'))) + )); + } + } + + /** + * Load suggestions as requested by the client + * + * @param ServerRequestInterface $request + * + * @return $this + */ + public function forRequest(ServerRequestInterface $request) + { + if ($request->getMethod() !== 'POST') { + return $this; + } + + $requestData = json_decode($request->getBody()->read(8192), true); + if (empty($requestData)) { + return $this; + } + + $search = $requestData['term']['search']; + $label = $requestData['term']['label']; + $type = $requestData['term']['type']; + + $this->setSearchTerm($search); + $this->setType($type); + + switch ($type) { + case 'value': + if (! $requestData['column'] || $requestData['column'] === SearchEditor::FAKE_COLUMN) { + $this->setFailureMessage(t('Missing column name')); + break; + } + + $searchFilter = QueryString::parse( + isset($requestData['searchFilter']) + ? $requestData['searchFilter'] + : '' + ); + if ($searchFilter instanceof Filter\Condition) { + $searchFilter = Filter::all($searchFilter); + } + + try { + $this->setData($this->fetchValueSuggestions($requestData['column'], $label, $searchFilter)); + } catch (SearchException $e) { + $this->setFailureMessage($e->getMessage()); + } + + if ($search) { + $this->setDefault(['search' => $label]); + } + + break; + case 'column': + $this->setData($this->filterColumnSuggestions($this->fetchColumnSuggestions($label), $label)); + + if ($search && isset($requestData['showQuickSearch']) && $requestData['showQuickSearch']) { + $quickFilter = $this->createQuickSearchFilter($label); + if (! $quickFilter instanceof Filter\Chain || ! $quickFilter->isEmpty()) { + $this->setDefault([ + 'search' => $label, + 'type' => 'terms', + 'terms' => $quickFilter + ]); + } + } + } + + return $this; + } + + protected function hasMore($data, $than) + { + if (is_array($data)) { + return count($data) > $than; + } elseif ($data instanceof Countable) { + return $data->count() > $than; + } elseif ($data instanceof OuterIterator) { + return $this->hasMore($data->getInnerIterator(), $than); + } + + return false; + } + + /** + * Filter the given suggestions by the client's input + * + * @param Traversable $data + * @param string $searchTerm + * + * @return Generator + */ + protected function filterColumnSuggestions($data, $searchTerm) + { + foreach ($data as $key => $value) { + if ($this->matchSuggestion($key, $value, $searchTerm)) { + yield $key => $value; + } + } + } + + /** + * Get whether the given suggestion should be provided to the client + * + * @param string $path + * @param string $label + * @param string $searchTerm + * + * @return bool + */ + protected function matchSuggestion($path, $label, $searchTerm) + { + return fnmatch($searchTerm, $label, FNM_CASEFOLD) || fnmatch($searchTerm, $path, FNM_CASEFOLD); + } + + public function renderUnwrapped() + { + $this->ensureAssembled(); + + if ($this->isEmpty()) { + return ''; + } + + return parent::renderUnwrapped(); + } +} diff --git a/vendor/ipl/web/src/Control/SearchBar/Terms.php b/vendor/ipl/web/src/Control/SearchBar/Terms.php new file mode 100644 index 0000000..c81e336 --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/Terms.php @@ -0,0 +1,255 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlElement; +use ipl\Stdlib\Filter; +use ipl\Web\Filter\QueryString; +use ipl\Web\Widget\Icon; + +class Terms extends BaseHtmlElement +{ + protected $tag = 'div'; + + protected $defaultAttributes = ['class' => 'terms']; + + /** @var callable|Filter\Rule */ + protected $filter; + + /** @var array */ + protected $changes; + + /** @var int */ + private $changeIndexCorrection = 0; + + /** @var int */ + private $currentIndex = 0; + + public function setFilter($filter) + { + $this->filter = $filter; + + return $this; + } + + /** + * Apply term changes + * + * @param array $changes + * + * @return $this + */ + public function applyChanges(array $changes) + { + $this->changes = $changes; + + return $this; + } + + protected function assemble() + { + $filter = $this->filter; + if (is_callable($filter)) { + $filter = $filter(); + } + + if ($filter === null) { + return; + } + + if ($filter instanceof Filter\Chain) { + if ($filter->isEmpty()) { + return; + } + + if ($filter instanceof Filter\None) { + $this->assembleChain($filter, $this, $filter->count() > 1); + } else { + $this->assembleConditions($filter, $this); + } + } else { + /** @var Filter\Condition $filter */ + $this->assembleCondition($filter, $this); + } + } + + protected function assembleConditions(Filter\Chain $filters, BaseHtmlElement $where) + { + foreach ($filters as $i => $filter) { + if ($i > 0) { + $logicalOperator = QueryString::getRuleSymbol($filters); + $this->assembleTerm([ + 'class' => 'logical_operator', + 'type' => 'logical_operator', + 'search' => $logicalOperator, + 'label' => $logicalOperator + ], $where); + } + + if ($filter instanceof Filter\Chain) { + $this->assembleChain($filter, $where, $filter->count() > 1); + } else { + /** @var Filter\Condition $filter */ + $this->assembleCondition($filter, $where); + } + } + } + + protected function assembleChain(Filter\Chain $chain, BaseHtmlElement $where, $wrap = false) + { + if ($wrap) { + $group = new HtmlElement( + 'div', + Attributes::create(['class' => 'filter-chain', 'data-group-type' => 'chain']) + ); + } else { + $group = $where; + } + + if ($chain instanceof Filter\None) { + $this->assembleTerm([ + 'class' => 'logical_operator', + 'type' => 'negation_operator', + 'search' => '!', + 'label' => '!' + ], $where); + } + + if ($wrap) { + $opening = $this->assembleTerm([ + 'class' => 'grouping_operator_open', + 'type' => 'grouping_operator', + 'search' => '(', + 'label' => '(' + ], $group); + } + + $this->assembleConditions($chain, $group); + + if ($wrap) { + $closing = $this->assembleTerm([ + 'class' => 'grouping_operator_close', + 'type' => 'grouping_operator', + 'search' => ')', + 'label' => ')' + ], $group); + + $opening->addAttributes([ + 'data-counterpart' => $closing->getAttributes()->get('data-index')->getValue() + ]); + $closing->addAttributes([ + 'data-counterpart' => $opening->getAttributes()->get('data-index')->getValue() + ]); + + $where->addHtml($group); + } + } + + protected function assembleCondition(Filter\Condition $filter, BaseHtmlElement $where) + { + $column = $filter->getColumn(); + $operator = QueryString::getRuleSymbol($filter); + $value = $filter->getValue(); + $columnLabel = $filter->metaData()->get('columnLabel', $column); + + $group = new HtmlElement( + 'div', + Attributes::create(['class' => 'filter-condition', 'data-group-type' => 'condition']), + new HtmlElement('button', Attributes::create(['type' => 'button']), new Icon('trash')) + ); + + $columnData = [ + 'class' => 'column', + 'type' => 'column', + 'search' => rawurlencode($column), + 'label' => $columnLabel, + 'title' => $column + ]; + if ($filter->metaData()->has('invalidColumnPattern')) { + $columnData['pattern'] = $filter->metaData()->get('invalidColumnPattern'); + if ($filter->metaData()->has('invalidColumnMessage')) { + $columnData['invalidMsg'] = $filter->metaData()->get('invalidColumnMessage'); + } + } + + $this->assembleTerm($columnData, $group); + + if ($value !== true) { + $operatorData = [ + 'class' => 'operator', + 'type' => 'operator', + 'search' => $operator, + 'label' => $operator + ]; + if ($filter->metaData()->has('invalidOperatorPattern')) { + $operatorData['pattern'] = $filter->metaData()->get('invalidOperatorPattern'); + if ($filter->metaData()->has('invalidOperatorMessage')) { + $operatorData['invalidMsg'] = $filter->metaData()->get('invalidOperatorMessage'); + } + } + + $this->assembleTerm($operatorData, $group); + + if (! empty($value) || ! is_string($value) || ctype_digit($value)) { + $valueData = [ + 'class' => 'value', + 'type' => 'value', + 'search' => rawurlencode($value), + 'label' => $value + ]; + if ($filter->metaData()->has('invalidValuePattern')) { + $valueData['pattern'] = $filter->metaData()->get('invalidValuePattern'); + if ($filter->metaData()->has('invalidValueMessage')) { + $valueData['invalidMsg'] = $filter->metaData()->get('invalidValueMessage'); + } + } + + $this->assembleTerm($valueData, $group); + } + } + + $where->addHtml($group); + } + + protected function assembleTerm(array $data, BaseHtmlElement $where) + { + if (isset($this->changes[$this->currentIndex - $this->changeIndexCorrection])) { + $change = $this->changes[$this->currentIndex - $this->changeIndexCorrection]; + if ($change['type'] !== $data['type']) { + // This can happen because the user didn't insert parentheses but the parser did + $this->changeIndexCorrection++; + } else { + $data = array_merge($data, $change); + } + } + + $term = new HtmlElement('label', Attributes::create([ + 'class' => $data['class'], + 'data-index' => $this->currentIndex++, + 'data-type' => $data['type'], + 'data-search' => $data['search'], + 'data-label' => $data['label'] + ]), new HtmlElement('input', Attributes::create([ + 'type' => 'text', + 'value' => $data['label'] + ]))); + + if (isset($data['title'])) { + $term->setAttribute('title', $data['title']); + } + + if (isset($data['pattern'])) { + $term->getFirst('input')->setAttribute('pattern', $data['pattern']); + + if (isset($data['invalidMsg'])) { + $term->getFirst('input')->setAttribute('data-invalid-msg', $data['invalidMsg']); + } + } + + $where->addHtml($term); + + return $term; + } +} diff --git a/vendor/ipl/web/src/Control/SearchBar/ValidatedColumn.php b/vendor/ipl/web/src/Control/SearchBar/ValidatedColumn.php new file mode 100644 index 0000000..5825790 --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/ValidatedColumn.php @@ -0,0 +1,44 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use ipl\Stdlib\Data; +use ipl\Stdlib\Filter\Condition; + +class ValidatedColumn extends ValidatedTerm +{ + /** + * Create a new ValidatedColumn from the given filter condition + * + * @param Condition $condition + * + * @return static + */ + public static function fromFilterCondition(Condition $condition) + { + return new static($condition->getColumn(), $condition->metaData()->get('columnLabel')); + } + + public function toTermData() + { + $termData = parent::toTermData(); + $termData['type'] = 'column'; + + return $termData; + } + + public function toMetaData() + { + $data = new Data(); + if (($label = $this->getLabel()) !== null) { + $data->set('columnLabel', $label); + } + + if (! $this->isValid()) { + $data->set('invalidColumnMessage', $this->getMessage()) + ->set('invalidColumnPattern', $this->getPattern()); + } + + return $data; + } +} diff --git a/vendor/ipl/web/src/Control/SearchBar/ValidatedOperator.php b/vendor/ipl/web/src/Control/SearchBar/ValidatedOperator.php new file mode 100644 index 0000000..2ca6d77 --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/ValidatedOperator.php @@ -0,0 +1,80 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use InvalidArgumentException; +use ipl\Stdlib\Data; +use ipl\Stdlib\Filter; +use LogicException; + +class ValidatedOperator extends ValidatedTerm +{ + /** + * Create a new ValidatedColumn from the given filter condition + * + * @param Filter\Condition $condition + * + * @return static + * + * @throws InvalidArgumentException In case the condition type is unknown + */ + public static function fromFilterCondition(Filter\Condition $condition) + { + switch (true) { + case $condition instanceof Filter\Unlike: + case $condition instanceof Filter\Unequal: + $operator = '!='; + break; + case $condition instanceof Filter\Like: + case $condition instanceof Filter\Equal: + $operator = '='; + break; + case $condition instanceof Filter\GreaterThan: + $operator = '>'; + break; + case $condition instanceof Filter\LessThan: + $operator = '<'; + break; + case $condition instanceof Filter\GreaterThanOrEqual: + $operator = '>='; + break; + case $condition instanceof Filter\LessThanOrEqual: + $operator = '<='; + break; + default: + throw new InvalidArgumentException('Unknown condition type'); + } + + return new static($operator); + } + + public function toTermData() + { + $termData = parent::toTermData(); + $termData['type'] = 'operator'; + + return $termData; + } + + public function toMetaData() + { + $data = new Data(); + + if (! $this->isValid()) { + $data->set('invalidOperatorMessage', $this->getMessage()) + ->set('invalidOperatorPattern', $this->getPattern()); + } + + return $data; + } + + public function setSearchValue($searchValue) + { + throw new LogicException('Operators cannot be changed'); + } + + public function setLabel($label) + { + throw new LogicException('Operators cannot be changed'); + } +} diff --git a/vendor/ipl/web/src/Control/SearchBar/ValidatedTerm.php b/vendor/ipl/web/src/Control/SearchBar/ValidatedTerm.php new file mode 100644 index 0000000..e552552 --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/ValidatedTerm.php @@ -0,0 +1,196 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use ipl\Stdlib\Data; + +abstract class ValidatedTerm +{ + /** @var string The default validation constraint */ + const DEFAULT_PATTERN = '^\s*(?!%s\b).*\s*$'; + + /** @var mixed The search value */ + protected $searchValue; + + /** @var string The label */ + protected $label; + + /** @var string The validation message */ + protected $message; + + /** @var string The validation constraint */ + protected $pattern; + + /** @var bool Whether the term has been adjusted */ + protected $changed = false; + + /** + * Create a new ValidatedTerm + * + * @param mixed $searchValue The search value + * @param ?string $label The label + */ + public function __construct($searchValue, $label = null) + { + $this->searchValue = $searchValue; + $this->label = $label; + } + + /** + * Create a new ValidatedTerm from the given data + * + * @param array $data + * + * @return static + */ + public static function fromTermData(array $data) + { + return new static($data['search'], isset($data['label']) ? $data['label'] : null); + } + + /** + * Check whether the term is valid + * + * @return bool + */ + public function isValid() + { + return $this->message === null; + } + + /** + * Check whether the term has been adjusted + * + * @return bool + */ + public function hasBeenChanged() + { + return $this->changed; + } + + /** + * Get the search value + * + * @return mixed + */ + public function getSearchValue() + { + return $this->searchValue; + } + + /** + * Set the search value + * + * @param mixed $searchValue + * + * @return $this + */ + public function setSearchValue($searchValue) + { + $this->searchValue = $searchValue; + $this->changed = true; + + return $this; + } + + /** + * Get the label + * + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * Set the label + * + * @param string $label + * + * @return $this + */ + public function setLabel($label) + { + $this->label = (string) $label; + $this->changed = true; + + return $this; + } + + /** + * Get the validation message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Set the validation message + * + * @param ?string $message + * + * @return $this + */ + public function setMessage($message) + { + $this->message = $message; + + return $this; + } + + /** + * Get the validation constraint + * + * Returns the default constraint if none is set. + * + * @return string + */ + public function getPattern() + { + if ($this->pattern === null) { + return sprintf(self::DEFAULT_PATTERN, $this->getSearchValue()); + } + + return $this->pattern; + } + + /** + * Set the validation constraint + * + * @param string $pattern + * + * @return $this + */ + public function setPattern($pattern) + { + $this->pattern = (string) $pattern; + + return $this; + } + + /** + * Get this term's data + * + * @return array + */ + public function toTermData() + { + return [ + 'search' => $this->getSearchValue(), + 'label' => $this->getLabel() ?: $this->getSearchValue(), + 'invalidMsg' => $this->getMessage(), + 'pattern' => $this->getPattern() + ]; + } + + /** + * Get this term's metadata + * + * @return Data + */ + abstract public function toMetaData(); +} diff --git a/vendor/ipl/web/src/Control/SearchBar/ValidatedValue.php b/vendor/ipl/web/src/Control/SearchBar/ValidatedValue.php new file mode 100644 index 0000000..423102d --- /dev/null +++ b/vendor/ipl/web/src/Control/SearchBar/ValidatedValue.php @@ -0,0 +1,41 @@ +<?php + +namespace ipl\Web\Control\SearchBar; + +use ipl\Stdlib\Data; +use ipl\Stdlib\Filter\Condition; + +class ValidatedValue extends ValidatedTerm +{ + /** + * Create a new ValidatedColumn from the given filter condition + * + * @param Condition $condition + * + * @return static + */ + public static function fromFilterCondition(Condition $condition) + { + return new static($condition->getValue()); + } + + public function toTermData() + { + $termData = parent::toTermData(); + $termData['type'] = 'value'; + + return $termData; + } + + public function toMetaData() + { + $data = new Data(); + + if (! $this->isValid()) { + $data->set('invalidValueMessage', $this->getMessage()) + ->set('invalidValuePattern', $this->getPattern()); + } + + return $data; + } +} |