diff --git a/README.md b/README.md index ebcc0c7..9781785 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,12 @@ This extension adds following methods to `Nette\Forms\Container` and thus to all [ AjaxSelectExtension::CONFIG_INVALID_VALUE_MODE => AjaxSelectExtension::INVALID_VALUE_MODE_*, AjaxSelectExtension::CONFIG_OR_BY_ID_FILTER => TRUE, + AjaxSelectExtension::CONFIG_TRANSLATOR => TRUE, ] ``` +`AjaxSelectExtension::CONFIG_TRANSLATOR`: Sets the automatic translation of select values on/off. Default is `TRUE`. + ### Dynamic Select This control allows passing unknown value to `$control->value` field. Doing so will invoke control's `$itemFactory` with only one parameter - the invalid value. @@ -418,3 +421,8 @@ public function init($form) { ``` +## ToDo + +### orById filtr pro zanořené selecty + +Pokud máme select uvnitř toMany nebo addDynamic, tak musí být nastaveno `AjaxSelectExtension::CONFIG_OR_BY_ID_FILTER => FALSE`, aby se knihovna nepokoušela přistoupit k atributu dle názvu selectu v hlavní entitě, což by skončilo chybou. Toto rozšíření orById filtru pro zanořené selecty by se dalo naimplementovat, že bychom si v kontejneru selectu zjistili, kam je select zanořený (může být víc než jedna úroveň zanoření) a podle toho bychom místo `$form->getEntity()->get{$atributeName}()` provolali všechny prvky zanoření, tedy `$form->getEntity()->get{$zanořenýPrvek}($indexPrvku)->...->get{$atributeName}()` diff --git a/composer.json b/composer.json index 396505c..80c71a3 100644 --- a/composer.json +++ b/composer.json @@ -4,13 +4,14 @@ "type": "library", "license": ["MIT", "BSD-3-Clause", "GPL-2.0", "GPL-3.0"], "require": { - "php": ">=7.1", - "nette/forms": "^3.0", + "php": ">=8.3", + "adt/nette-forms-components": "^2.3", "nette/di": "^3.0", - "nette/utils": "^3.0", + "nette/utils": "^3.0 | ^4.0", "nette/application": "*", - "adt/base-query": "^1.7" - }, + "adt/doctrine-components": "^3.0", + "nette/forms": "^3.2.9" + }, "autoload": { "psr-4": { "ADT\\": "src/" diff --git a/src/Components/AjaxSelect/DI/AjaxSelectExtension.php b/src/Components/AjaxSelect/DI/AjaxSelectExtension.php index f2f424e..449ceb4 100644 --- a/src/Components/AjaxSelect/DI/AjaxSelectExtension.php +++ b/src/Components/AjaxSelect/DI/AjaxSelectExtension.php @@ -18,6 +18,8 @@ class AjaxSelectExtension extends \Nette\DI\CompilerExtension { const CONFIG_OR_BY_ID_FILTER = 'orByIdFilter'; + const CONFIG_TRANSLATOR = 'translator'; + const ENTITY_FACTORY_TAG = 'ajax-select.entity-factory'; public function loadConfiguration() { @@ -25,6 +27,7 @@ public function loadConfiguration() { static::CONFIG_GET_ITEMS_SIGNAL_NAME => 'getAjaxItems', static::CONFIG_INVALID_VALUE_MODE => static::INVALID_VALUE_MODE_EXCEPTION, static::CONFIG_OR_BY_ID_FILTER => TRUE, + static::CONFIG_TRANSLATOR => TRUE, ]; $builder = $this->getContainerBuilder(); @@ -125,11 +128,16 @@ public static function register(\Nette\DI\Container $container, $globalConfig) { /** @var AjaxSelect\AjaxSelect|AjaxSelect\DynamicSelect|mixed $control */ $control = new $class($label); - $config = static::processConfigOptions($config, $globalConfig, [static::CONFIG_INVALID_VALUE_MODE, static::CONFIG_OR_BY_ID_FILTER]); + $config = static::processConfigOptions($config, $globalConfig, [static::CONFIG_INVALID_VALUE_MODE, static::CONFIG_OR_BY_ID_FILTER, static::CONFIG_TRANSLATOR]); // set invalid value mode $control->setInvalidValueMode($config[static::CONFIG_INVALID_VALUE_MODE]); + // set translator + if (!$config[static::CONFIG_TRANSLATOR]) { + $control->setTranslator(NULL); + } + // inject ajax entity /** @var AjaxSelect\Services\AjaxService $ajaxService */ $ajaxService = $serviceGetter(); @@ -148,11 +156,13 @@ public static function register(\Nette\DI\Container $container, $globalConfig) { // pro dymanic select return function (\Nette\Forms\Container $container, $name, $label = NULL, $items = NULL, $itemFactory = NULL, $config = []) use ($class, $serviceGetter, $globalConfig) { - $config = static::processConfigOptions($config, $globalConfig, [static::CONFIG_INVALID_VALUE_MODE, static::CONFIG_OR_BY_ID_FILTER]); + $config = static::processConfigOptions($config, $globalConfig, [static::CONFIG_INVALID_VALUE_MODE, static::CONFIG_OR_BY_ID_FILTER, static::CONFIG_TRANSLATOR]); // if $items are not array of values, we have received query object if ($items instanceof \ADT\BaseQuery\BaseQuery) { - AjaxSelect\Traits\OrByIdFilterTrait::applyOrByIdFilter($config, $container, $name, $items); + if ($items instanceof AjaxSelect\Interfaces\OrdByIdFilterInterface) { + $items->applyOrByIdFilter($config, $container, $name); + } if ($items->callSelectPairsAuto()) { $items->selectPairs(); @@ -168,6 +178,11 @@ public static function register(\Nette\DI\Container $container, $globalConfig) { // set invalid value mode $control->setInvalidValueMode($config[static::CONFIG_INVALID_VALUE_MODE]); + // set translator + if (!$config[static::CONFIG_TRANSLATOR]) { + $control->setTranslator(NULL); + } + $control->setItemFactory($itemFactory); return $container[$name] = $control; diff --git a/src/Components/AjaxSelect/Entities/AbstractEntity.php b/src/Components/AjaxSelect/Entities/AbstractEntity.php index c6ac2a0..2bd32cd 100644 --- a/src/Components/AjaxSelect/Entities/AbstractEntity.php +++ b/src/Components/AjaxSelect/Entities/AbstractEntity.php @@ -3,17 +3,22 @@ namespace ADT\Components\AjaxSelect\Entities; use ADT\Components\AjaxSelect; +use ADT\DoctrineComponents\QueryObject\QueryObject; use Nette\Application\UI\Presenter; use Nette\SmartObject; -abstract class AbstractEntity { - +abstract class AbstractEntity +{ use SmartObject; - const DATA_ATTRIBUTE_NAME = 'data-ajax-select'; - + const DATA_ATTRIBUTE_NAME = 'data-adt-ajax-select'; const OPTION_QUERY = 'q'; + public abstract function formatValues($values): array; + public abstract function hydrateValues($values, array $formValues = []): array; + + protected abstract function createQueryObject(): QueryObject; + /** @var string */ protected $name; @@ -69,9 +74,13 @@ public function areValidValues(array $values) { $query = $this->createQueryObject() ->byId($values); - $this->filterQueryObject($query); + if (method_exists($this, 'filterQueryObject')) { + $this->filterQueryObject($query); + } - AjaxSelect\Traits\OrByIdFilterTrait::applyOrByIdFilter($this->config, $form, $control->getName(), $query); + if ($query instanceof AjaxSelect\Interfaces\OrdByIdFilterInterface) { + $query->applyOrByIdFilter($this->config, $form, $control->getName()); + } foreach ($query->fetch() as $row) { // pouze selectnuté jsou validní @@ -105,9 +114,7 @@ public function findValues($limit) { ->applyPaging(0, $limit) ->toArray(); - return array_map(function ($row) { - return $row->getId(); - }, $rows); + return $rows; } /** @@ -115,41 +122,26 @@ public function findValues($limit) { * @param array $values * @return array List of items. */ - public abstract function formatValues($values); - - /** - * @internal - * @param array $values - * @return array List of items. - */ - public function formatJsonValues($values) { + public function formatJsonValues($values, array $formValues = []) { $result = [ ]; - $titles = $this->formatValues($values); - + $titles = $this->formatValues($this->hydrateValues($values, $formValues)); + $count = count($titles); + $i = 1; foreach ($titles as $id => $item) { if (!is_array($item)) { $item = [ 'text' => $item, ]; } - $item['id'] = $id; + $item['aria-posinset'] = $i; + $item['aria-setsize'] = $count; $result[] = $item; + $i++; } - return $result; } - /** - * @return \ADT\BaseQuery\BaseQuery freshly created QO without filters - */ - protected abstract function createQueryObject(); - - /** - * @param \ADT\BaseQuery\BaseQuery $query - */ - protected abstract function filterQueryObject($query); - /** * @param string $option * @param mixed $value @@ -208,7 +200,8 @@ public function done() { ? $this->formatJsonValues( is_array($controlValue) ? $controlValue - : [ $controlValue ] + : [ $controlValue ], + $control->getForm()->getValidatedValues('array') ) : [], 'queryParam' => static::OPTION_QUERY, diff --git a/src/Components/AjaxSelect/Interfaces/OrByIdFilterInterface.php b/src/Components/AjaxSelect/Interfaces/OrByIdFilterInterface.php new file mode 100644 index 0000000..cd49ac3 --- /dev/null +++ b/src/Components/AjaxSelect/Interfaces/OrByIdFilterInterface.php @@ -0,0 +1,9 @@ +getAjaxEntity()->formatValues($validValues); + $validItems = $this->getAjaxEntity()->formatValues($this->getAjaxEntity()->hydrateValues($validValues, $this->getForm()->getValidatedValues('array'))); // add to list of valid values $this->setItems($this->getItems() + $validItems); diff --git a/src/Components/AjaxSelect/Traits/InvalidSetValueTrait.php b/src/Components/AjaxSelect/Traits/InvalidSetValueTrait.php index e2f5afe..6f75a61 100644 --- a/src/Components/AjaxSelect/Traits/InvalidSetValueTrait.php +++ b/src/Components/AjaxSelect/Traits/InvalidSetValueTrait.php @@ -35,7 +35,7 @@ public function setInvalidValueMode($mode) { return $this; } - protected abstract function getHttpData($type, ?string $htmlTail = NULL); + protected abstract function getHttpData(int $type, ?string $htmlTail = NULL): mixed; protected abstract function isDisabled(); /** diff --git a/src/Components/AjaxSelect/Traits/OrByIdFilterTrait.php b/src/Components/AjaxSelect/Traits/OrByIdFilterTrait.php index e3821b0..c4b89d4 100644 --- a/src/Components/AjaxSelect/Traits/OrByIdFilterTrait.php +++ b/src/Components/AjaxSelect/Traits/OrByIdFilterTrait.php @@ -4,29 +4,24 @@ use ADT\Components\AjaxSelect; -trait OrByIdFilterTrait { +trait OrByIdFilterTrait +{ + public abstract function orById($id): static; - /** - * @param array $config - * @param \Nette\Forms\Container $form - * @param string $attributeName - * @param \ADT\BaseQuery\BaseQuery $query - */ - public static function applyOrByIdFilter(array $config, \Nette\Forms\Container $form, string $attributeName, \ADT\BaseQuery\BaseQuery $query) + public function applyOrByIdFilter(array $config, \Nette\Forms\Container $form, string $attributeName) { // if orByIdFilter is active and it is entity form, the value, which is set in entity->inputName is included in items. if ($config[AjaxSelect\DI\AjaxSelectExtension::CONFIG_OR_BY_ID_FILTER] && method_exists($form, 'getEntity') && !empty($form->getEntity())) { $defaultValue = $form->getEntity()->{'get' . ucfirst($attributeName)}(); //It can be Kdyby\Doctrine\Collections\Readonly\ReadOnlyCollectionWrapper - if (method_exists($defaultValue, 'toArray')) { + if ($defaultValue && !is_array($defaultValue) && method_exists($defaultValue, 'toArray')) { $defaultValue = $defaultValue->toArray(); } if (!empty($defaultValue)) { - $query->orById($defaultValue); + $this->orById($defaultValue); } } } - }