Segmentos¶
Segmentos permitem segmentar usuarios via criterios composiveis. Um segmento e uma regra declarativa (ex: "usuarios com perfil X e na coorte Y") que o framework avalia como SQL subquery — sem materializar listas.
O sistema faz parte do Core Capability CC-07 (core.segments) e esta sempre disponivel via core extension.
Exemplo rapido¶
Avaliar um segmento existente e obter a subquery de membros:
use local_middag\facade\segments;
// Obter subquery SQL dos membros do segmento
$subquery = segments::get_subquery(segment_id: 15);
// Usar a subquery em uma query composta
global $DB;
$users = $DB->get_records_sql(
"SELECT u.* FROM {user} u WHERE u.id IN ({$subquery->sql})",
$subquery->params
);
// Ou apenas contar membros (respeitando cache)
$count = segments::evaluate(segment_id: 15);
Conceitos¶
| Conceito | Descricao |
|---|---|
| Segment | Regra declarativa que define um grupo de usuarios |
| Criteria | Condicao individual dentro do segmento (ex: campo de perfil, coorte) |
| Criteria type | Categoria da condicao (ex: profile_field, cohort_membership) |
| Criteria provider | Implementacao que sabe gerar SQL e validar config para um criteria type |
| segment_subquery | Value object retornado pela avaliacao: contem SQL, parametros e contagem |
Segmentos nao retornam listas de IDs. Retornam subqueries SQL para composicao e performance em operacoes em massa.
Referencia da API¶
segments_interface (contract @api)¶
| Metodo | Descricao |
|---|---|
get_subquery(int $segment_id) |
Retorna segment_subquery com SQL, parametros e contagem estimada |
evaluate(int $segment_id) |
Avalia o segmento e retorna contagem de membros (respeita cache) |
get_criteria_types() |
Lista tipos de criterio disponiveis (built-in + extensions) |
get_available_fields() |
Lista campos filtraveis de todos os providers registrados |
Acesso via facade:
use local_middag\facade\segments;
$subquery = segments::get_subquery($id);
$count = segments::evaluate($id);
$types = segments::get_criteria_types();
$fields = segments::get_available_fields();
segment_subquery (value object @api)¶
final readonly class segment_subquery
{
public string $sql; // SQL subquery retornando user IDs
public array $params; // Parametros bound para a subquery
public int $count; // Contagem estimada (pode ser cached)
}
Uso tipico:
$subquery = segments::get_subquery(15);
// Compor com outra query
$sql = "SELECT u.id, u.email
FROM {user} u
WHERE u.id IN ({$subquery->sql})
AND u.suspended = 0";
$users = $DB->get_records_sql($sql, $subquery->params);
Implementar um criteria provider¶
Extensions podem adicionar novos tipos de criterio implementando criteria_provider_interface e registrando o provider durante boot().
Exemplo: criterio por lingua do usuario¶
namespace local_meuplugin\extensions\idiomas;
use local_middag\framework\contract\criteria_provider_interface;
use local_middag\framework\contract\segment_subquery;
class language_criteria_provider implements criteria_provider_interface
{
/**
* Identificador unico deste tipo de criterio.
*/
public function get_type(): string
{
return 'user_language';
}
/**
* Campos filtraveis expostos por este provider.
*/
public function get_fields(): array
{
return [
[
'name' => 'lang',
'label' => get_string('language'),
'category' => 'profile',
'operators' => ['equals', 'not_equals', 'in'],
'is_advanced' => false,
],
];
}
/**
* Gera subquery SQL para a configuracao de criterio.
*/
public function build_sql(array $config): segment_subquery
{
$lang = $config['value'] ?? 'pt_br';
return new segment_subquery(
sql: "SELECT id AS userid FROM {user} WHERE lang = ?",
params: [$lang],
);
}
/**
* Descricao legivel do criterio para exibicao.
*/
public function describe(array $config): string
{
$lang = $config['value'] ?? '?';
return get_string('language') . ' = ' . $lang;
}
/**
* Valida a configuracao antes de persistir.
*/
public function validate_config(array $config): bool
{
return !empty($config['value']) && is_string($config['value']);
}
}
Registro no boot()¶
namespace local_meuplugin\extensions\idiomas;
class idiomas_extension extends \local_middag\base\extension
{
public function boot(): void
{
// Registrar o provider no criteria registry
$registry = $this->container->get(
\local_middag\framework\contract\segments_interface::class
);
// O registro concreto depende da implementacao do registry;
// consulte a documentacao da versao para o metodo exato.
}
}
Referencia do criteria_provider_interface¶
| Metodo | Descricao |
|---|---|
get_type(): string |
Identificador unico do tipo de criterio |
get_fields(): array |
Lista de campos filtraveis com label, categoria, operadores e visibilidade |
build_sql(array $config) |
Gera segment_subquery para a configuracao dada |
describe(array $config) |
Descricao legivel para display e audit |
validate_config(array $config) |
Valida a configuracao antes de persistir (true = valida) |
Estrutura do campo retornado por get_fields()¶
[
'name' => 'lang', // Identificador do campo
'label' => 'Idioma', // Label para exibicao
'category' => 'profile', // Categoria para agrupamento na UI
'operators' => ['equals', 'in'], // Operadores suportados
'is_advanced' => false, // Se e campo avancado (oculto por padrao)
]
Patterns¶
Combinar criterios em um provider¶
Um provider pode expor multiplos campos filtraveis. O segment builder combina criterios de multiplos providers via AND/OR:
public function get_fields(): array
{
return [
['name' => 'lang', 'label' => 'Idioma', 'category' => 'profile', 'operators' => ['equals', 'in'], 'is_advanced' => false],
['name' => 'country', 'label' => 'Pais', 'category' => 'profile', 'operators' => ['equals', 'in'], 'is_advanced' => false],
['name' => 'city', 'label' => 'Cidade', 'category' => 'profile', 'operators' => ['equals', 'contains'], 'is_advanced' => true],
];
}
Usar segmentos em conteudo condicional¶
Combine segmentos com shortcodes ou hooks para entregar conteudo direcionado:
use local_middag\facade\segments;
// Em um hook ou controller
$subquery = segments::get_subquery($segment_id);
global $DB, $USER;
$is_member = $DB->record_exists_sql(
"SELECT 1 FROM ({$subquery->sql}) sub WHERE sub.userid = ?",
array_merge($subquery->params, [$USER->id])
);
if ($is_member) {
// Exibir conteudo exclusivo para este segmento
}
Contar membros para relatorio¶
use local_middag\facade\segments;
$segments_list = [10, 15, 22];
$report = [];
foreach ($segments_list as $id) {
$report[$id] = segments::evaluate($id);
}
// $report = [10 => 342, 15 => 1205, 22 => 87]
Anti-patterns¶
Materializar a lista de IDs¶
Segmentos retornam subqueries por design. Nao converta para array de IDs em operacoes em massa:
// ERRADO: materializa milhares de IDs na memoria
$subquery = segments::get_subquery(15);
$ids = $DB->get_fieldset_sql($subquery->sql, $subquery->params);
// Depois usa IN(...) com milhares de valores
// CORRETO: compor a subquery diretamente
$sql = "SELECT u.* FROM {user} u WHERE u.id IN ({$subquery->sql})";
$users = $DB->get_records_sql($sql, $subquery->params);
Avaliar segmentos em loop por usuario¶
O segmento avalia todos os membros de uma vez. Nao chame evaluate() ou get_subquery() dentro de um loop por usuario:
// ERRADO: N+1 queries
foreach ($users as $user) {
$count = segments::evaluate(15); // mesma query repetida
}
// CORRETO: avaliar uma vez, compor subquery
$subquery = segments::get_subquery(15);
Provider sem validacao¶
Sempre implemente validate_config() com verificacoes reais. Um provider que sempre retorna true permite configuracoes invalidas:
// ERRADO
public function validate_config(array $config): bool
{
return true; // aceita qualquer coisa
}
// CORRETO
public function validate_config(array $config): bool
{
return !empty($config['value']) && is_string($config['value']);
}
Diferenca entre Segments e Conditions¶
| Aspecto | Segments (CC-07) | Conditions (CC-09) |
|---|---|---|
| Pergunta | "Quais usuarios pertencem a este grupo?" | "Em quais contextos esta regra se aplica?" |
| Alvo | Usuarios (user IDs) | Contextos Moodle (categorias, cursos, atividades) |
| Retorno | segment_subquery (SQL de user IDs) |
bool (match) ou list<int> (context IDs) |
| Uso | Audiencia, targeting, conteudo segmentado | Visibilidade, filtro por area do site |
Veja o guia de Conditions para regras baseadas em contexto.
Referencias¶
- ADR-607 -- Modelo de Subdominios Core (CC-07)
- Conditions -- regras de inclusao/exclusao por contexto
- Hooks -- integrar segmentos com o sistema reativo