96 lines
3.7 KiB
Markdown
96 lines
3.7 KiB
Markdown
---
|
|
id: KB-PLUGIN-005
|
|
title: ITILCategoryFieldStrategy LAST_VALID_ANSWER ignora subclasses de QuestionTypeItemDropdown
|
|
domain: plugin-dev
|
|
tags:
|
|
- glpi
|
|
- glpi11
|
|
- form-builder
|
|
- form-destination
|
|
- itil-category
|
|
- answer-pipeline
|
|
- question-type
|
|
status: active
|
|
severity: critical
|
|
created_at: 2026-05-02
|
|
updated_at: 2026-05-02
|
|
applies_to:
|
|
- GLPI 11.x form builder — destino de formulário (FormDestinationTicket/Change/Problem)
|
|
- plugins com QuestionType que estendem QuestionTypeItemDropdown para ITILCategory
|
|
related_records:
|
|
- KB-PLUGIN-002
|
|
- KB-PLUGIN-006
|
|
---
|
|
|
|
# Contexto
|
|
Ao criar um QuestionType de plugin que retorna uma ITILCategory como resposta,
|
|
o GLPI não aplica automaticamente essa categoria ao ticket criado pelo formulário.
|
|
|
|
# Sintoma observável
|
|
- O usuário seleciona uma categoria no formulário.
|
|
- O ticket é criado sem `itilcategories_id` (campo vazio/zero).
|
|
- Nenhum erro é exibido — a categoria simplesmente é ignorada.
|
|
|
|
# Causa raiz
|
|
`ITILCategoryFieldStrategy::LAST_VALID_ANSWER` filtra respostas por tipo assim:
|
|
|
|
```php
|
|
$valid_answers = $answers_set->getAnswersByType(QuestionTypeItemDropdown::class);
|
|
```
|
|
|
|
`AnswersSet::getAnswersByType()` usa **comparação exata** (`==`), não `is_a()`:
|
|
|
|
```php
|
|
fn(Answer $answer) => $answer->getRawType() == $type
|
|
```
|
|
|
|
O `raw_question_type` de um `QuestionTypeSplititil` é armazenado como
|
|
`'GlpiPlugin\Splititil\QuestionTypeSplititil'`, que não é igual a
|
|
`'Glpi\Form\QuestionType\QuestionTypeItemDropdown'`.
|
|
Resultado: a resposta é invisível para a estratégia padrão.
|
|
|
|
# Solução — AbstractConfigField silencioso
|
|
|
|
Registrar um campo de destino de plugin que roda **após** `ITILCategoryField` e
|
|
sobrescreve `itilcategories_id` quando encontra uma resposta da nossa classe:
|
|
|
|
```php
|
|
// Em setup.php (dentro de plugin_init_*)
|
|
FormDestinationManager::getInstance()->registerPluginCommonITILConfigField(
|
|
AbstractCommonITILFormDestination::class, // aplica a Ticket, Change e Problem
|
|
new \GlpiPlugin\Splititil\SplititilITILCategoryApplicator()
|
|
);
|
|
```
|
|
|
|
O `SplititilITILCategoryApplicator` estende `AbstractConfigField` com:
|
|
- `renderConfigForm()` retorna `''` → invisível na UI de configuração
|
|
- `applyConfiguratedValueToInputUsingAnswers()` varre as respostas buscando pelo
|
|
tipo exato da nossa classe e seta `itilcategories_id` no input do ticket
|
|
|
|
# Por que esse campo roda após ITILCategoryField
|
|
Em `createDestinationItems()`, os campos `Entity`, `Template` e `ITILCategoryField`
|
|
são aplicados antecipadamente e adicionados a `$already_applied_fields`.
|
|
Os campos de plugin são aplicados no `foreach` subsequente e **não** são filtrados
|
|
(porque têm uma classe diferente de `ITILCategoryField`), sobrescrevendo o valor anterior.
|
|
|
|
# Mínimo para implementar AbstractConfigField de plugin
|
|
Além dos defaults de `AbstractConfigField`, implementar obrigatoriamente:
|
|
`getLabel()`, `renderConfigForm()`, `getConfigClass()`, `getDefaultConfig()`,
|
|
`getWeight()`, `getCategory()`
|
|
+ uma classe config mínima implementando `JsonFieldInterface`
|
|
(`jsonDeserialize()` + `jsonSerialize()`).
|
|
|
|
# Validação padrão
|
|
1. Submeter formulário com questão de categoria preenchida.
|
|
2. Abrir o ticket criado e verificar que `itilcategories_id` está preenchido.
|
|
3. Confirmar no banco: `SELECT itilcategories_id FROM glpi_tickets ORDER BY id DESC LIMIT 1;`
|
|
|
|
# Regra para agentes
|
|
Qualquer QuestionType de plugin que retorne ITILCategory DEVE implementar um
|
|
`AbstractConfigField` silencioso registrado via `registerPluginCommonITILConfigField()`.
|
|
Não existe forma nativa de fazer o `LAST_VALID_ANSWER` reconhecer subclasses
|
|
sem modificar o core do GLPI.
|
|
|
|
# Classificação
|
|
- Tipo: Limitação arquitetural do GLPI 11 + solução via ponto de extensão oficial.
|
|
- Reutilização: obrigatória em qualquer plugin de categoria ITIL no form builder.
|