Durante a análise inicial, identifiquei os seguintes pontos críticos que precisavam ser resolvidos:
- Interface de Usuário: Precisava de uma forma simples para o cliente alterar cores sem conhecimento técnico
- Validação de Entrada: Garantir que apenas cores HEX válidas e store-views existentes fossem aceitas
- Armazenamento de Configuração: Decidir onde e como armazenar a cor configurada por store-view
- Aplicação no Frontend: Como aplicar a cor customizada a todos os botões sem modificar arquivos do tema
- Persistência: Garantir que a configuração seja mantida e aplicada corretamente
Decidi criar um módulo Magento 2 seguindo as melhores práticas do framework, dividindo as responsabilidades em componentes claros:
- Comando CLI: Para receber e processar os parâmetros do usuário
- Sistema de Configuração: Para armazenar a cor por store-view
- Block: Para encapsular a lógica de geração de CSS
- Layout XML: Para injetar o CSS no head de todas as páginas
- Template: Para renderizar o CSS de forma segura
app/code/Devnicolas/ButtonColor/
├── registration.php # Registro do módulo no Magento
├── etc/
│ ├── module.xml # Declaração do módulo e dependências
│ └── di.xml # Registro do comando de console no DI
├── Console/
│ └── Command/
│ └── ChangeButtonColor.php # Comando CLI principal
├── Block/
│ └── ButtonColor.php # Block que gera CSS customizado
├── view/
│ └── frontend/
│ ├── layout/
│ │ └── default.xml # Layout XML para injetar CSS no head
│ └── templates/
│ └── button-color.phtml # Template que renderiza o CSS
└── README.md # Este arquivo
registration.php
- Registra o módulo no sistema Magento usando
ComponentRegistrar - Define o namespace do módulo como
Devnicolas_ButtonColor
etc/module.xml
- Declara o módulo e suas dependências
- Dependências:
Magento_Store(para gerenciar stores) eMagento_Theme(para temas e layouts)
etc/di.xml
- Registra o comando de console no sistema de Dependency Injection do Magento
- Conecta o comando
color:changeà classeChangeButtonColor
Console/Command/ChangeButtonColor.php
- Classe principal do comando CLI
- Responsabilidades:
- Receber e validar argumentos (cor HEX e ID da store-view)
- Normalizar formato de cor HEX (aceita com ou sem #)
- Validar formato HEX (6 caracteres hexadecimais)
- Validar existência da store-view usando
StoreRepositoryInterface - Salvar configuração usando
WriterInterfacecom scopestores - Remover configuração usando flag
--resetpara restaurar cores originais - Limpar cache de configuração após salvar ou remover
- Exibir mensagens de sucesso/erro ao usuário
Block/ButtonColor.php
- Block que lê a configuração da store-view atual
- Responsabilidades:
- Obter cor configurada usando
ScopeConfigInterfacecom scopestores - Gerar CSS customizado que sobrescreve cores de todos os botões
- Retornar CSS formatado para injeção no template
- Obter cor configurada usando
view/frontend/layout/default.xml
- Layout XML que injeta o block no
<head>de todas as páginas - Utiliza o handle
defaultpara garantir que o CSS seja carregado em todas as páginas
view/frontend/templates/button-color.phtml
- Template que renderiza o CSS inline no
<head> - Verifica se há cor configurada antes de renderizar
- Utiliza
@noEscapepara permitir renderização de CSS (com segurança controlada)
Fluxo de Alteração de Cor:
-
Execução do Comando
- Cliente executa:
./bin/magento color:change 000000 1 - Symfony Console recebe os argumentos
- Cliente executa:
-
Validação de Cor HEX
- Remove
#se presente - Converte para maiúsculas
- Valida formato usando regex
/^[0-9A-F]{6}$/i - Retorna erro se formato inválido
- Remove
-
Validação de Store-View
- Converte ID para inteiro
- Verifica se é numérico
- Usa
StoreRepositoryInterface::getById()para verificar existência - Retorna erro se store-view não existir
-
Salvamento da Configuração
- Usa
WriterInterface::save()com:- Path:
devnicolas/button_color/color - Scope:
ScopeInterface::SCOPE_STORES - Scope ID: ID da store-view
- Path:
- Limpa cache de configuração usando
CacheManager
- Usa
-
Feedback ao Usuário
- Exibe mensagem de sucesso com cor e nome da store
- Confirma limpeza de cache
Fluxo de Reset (--reset):
-
Execução do Comando
- Cliente executa:
./bin/magento color:change --reset 1 - Symfony Console detecta a flag
--reset
- Cliente executa:
-
Validação de Store-View
- Converte ID para inteiro
- Verifica se é numérico
- Usa
StoreRepositoryInterface::getById()para verificar existência - Retorna erro se store-view não existir
-
Remoção da Configuração
- Usa
WriterInterface::delete()com:- Path:
devnicolas/button_color/color - Scope:
ScopeInterface::SCOPE_STORES - Scope ID: ID da store-view
- Path:
- Remove registro do banco de dados
- Limpa cache de configuração usando
CacheManager
- Usa
-
Feedback ao Usuário
- Exibe mensagem de sucesso confirmando reset
- Confirma remoção de estilos inline e limpeza de cache
-
Carregamento da Página
- Magento carrega o layout
default.xml - O block
button.color.cssé adicionado ao<head>
- Magento carrega o layout
-
Obtenção da Cor Configurada
- Block
ButtonColorobtém a store atual viaStoreManager - Lê configuração usando
ScopeConfigInterfacecom scopestores - Retorna
nullse não houver cor configurada
- Block
-
Geração de CSS
- Se cor configurada existe, gera CSS que targeta:
button(todos os botões).action.primary(botões primários do Magento).btn-primary(botões primários Bootstrap)[class*="button"](qualquer elemento com "button" no nome da classe)- Variações de botões primários e secundários
- Aplica
!importantpara garantir sobrescrita - Inclui estilos para hover
- Se cor configurada existe, gera CSS que targeta:
-
Renderização
- Template verifica se há cor configurada
- Renderiza tag
<style>com CSS customizado - CSS é injetado no
<head>da página
Decisão: Utilizei o sistema nativo de configuração do Magento (WriterInterface e ScopeConfigInterface)
Razão:
- Escopo por Store-View: Permite diferentes cores para cada store-view, atendendo exatamente ao requisito do teste
- Cache Nativo: Integra-se perfeitamente com o sistema de cache do Magento, garantindo performance
- Persistência: Dados são salvos no banco de dados na tabela
core_config_data, garantindo que a configuração seja mantida mesmo após limpezas de cache - Padrão Magento: Segue as melhores práticas do framework, tornando o código mais manutenível e compatível com futuras versões
Implementação:
$this->configWriter->save(
self::CONFIG_PATH,
$hexColor,
ScopeInterface::SCOPE_STORES,
$storeViewId
);Decisão: Injetar CSS inline através de um Block adicionado ao head.additional via layout XML
Razão:
- Universalidade: CSS é aplicado em todas as páginas automaticamente, sem necessidade de modificar cada template
- Performance: CSS inline é carregado imediatamente, sem requisição HTTP adicional, melhorando o tempo de carregamento
- Simplicidade: Não requer modificação de arquivos LESS/CSS do tema, mantendo o tema limpo e facilitando atualizações
- Flexibilidade: Pode ser facilmente removido ou modificado sem afetar o tema base
Implementação:
<referenceBlock name="head.additional">
<block class="Devnicolas\ButtonColor\Block\ButtonColor"
name="button.color.css"
template="Devnicolas_ButtonColor::button-color.phtml"/>
</referenceBlock>Decisão: Aplicar !important em todas as propriedades CSS geradas
Razão:
- Garantia de Sobrescrita: Garante que a cor customizada sobrescreva estilos do tema, mesmo aqueles com alta especificidade CSS
- Compatibilidade: Funciona com qualquer tema, sem necessidade de conhecer a estrutura CSS específica de cada tema
- Simplicidade: Evita necessidade de calcular especificidade CSS ou criar seletores muito específicos, tornando o código mais simples e manutenível
Implementação:
button,
.action.primary,
.btn-primary {
background-color: #000000 !important;
border-color: #000000 !important;
}Decisão: Implementar validação rigorosa do formato HEX usando regex
Razão:
- Segurança: Previne injeção de código malicioso através de valores não validados. Se não validasse, um usuário poderia tentar injetar código JavaScript ou CSS malicioso
- Consistência: Garante formato padronizado, facilitando o processamento e evitando erros
- Experiência do Usuário: Fornece feedback claro sobre erros, ajudando o cliente a corrigir o problema rapidamente
Implementação:
private function isValidHexColor(string $hexColor): bool
{
return preg_match('/^[0-9A-F]{6}$/i', $hexColor) === 1;
}Decisão: Limpar automaticamente o cache de configuração após salvar ou remover a configuração
Razão:
- Aplicação Imediata: Mudanças são visíveis imediatamente no frontend, sem necessidade de o cliente executar comandos adicionais
- Consistência: Garante que a configuração seja lida corretamente do banco de dados, evitando problemas com cache desatualizado
- Melhor Experiência: Cliente vê resultado instantaneamente, melhorando a experiência de uso do módulo
Implementação:
$this->cacheManager->clean([\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER]);Decisão: Normalizar a cor HEX para aceitar tanto 000000 quanto #000000
Razão:
- Flexibilidade: Permite que o cliente use o formato que preferir, tornando o comando mais intuitivo
- Experiência do Usuário: Reduz erros comuns, como esquecer ou adicionar o
#incorretamente - Compatibilidade: Funciona com diferentes formatos de entrada, tornando o módulo mais robusto
Implementação:
private function normalizeHexColor(string $hexColor): string
{
$hexColor = trim($hexColor);
$hexColor = ltrim($hexColor, '#');
return strtoupper($hexColor);
}Problema: Inicialmente, tentei aplicar a cor sem usar !important, mas alguns temas têm estilos muito específicos que não eram sobrescritos. Isso fazia com que a cor customizada não fosse aplicada em alguns botões.
Solução: Utilizei !important em todas as propriedades CSS geradas. Isso garante que a cor customizada sempre sobrescreva os estilos do tema, independentemente da especificidade CSS.
Problema: Diferentes temas e módulos usam diferentes classes CSS para botões. Se eu targetasse apenas button, muitos botões não seriam afetados.
Solução: Criei uma lista abrangente de seletores CSS que targeta:
button(todos os elementos button).action.primary(botões primários do Magento).btn-primary(botões primários Bootstrap)[class*="button"](qualquer elemento com "button" no nome da classe)- Variações de botões primários e secundários
Isso garante cobertura completa, independentemente do tema usado.
Problema: Precisava validar se a store-view existe antes de salvar a configuração, mas não queria que o comando falhasse silenciosamente ou com mensagens confusas.
Solução: Utilizei StoreRepositoryInterface::getById() que lança uma exceção NoSuchEntityException se a store não existir. Capturei essa exceção e exibi uma mensagem de erro clara e amigável ao usuário.
Problema: Após salvar a configuração, o cache do Magento poderia não ser atualizado imediatamente, fazendo com que a cor não aparecesse no frontend.
Solução: Implementei limpeza automática do cache de configuração após salvar ou remover a configuração. Isso garante que as mudanças sejam visíveis imediatamente.
Problema: O teste original não mencionava reset, mas percebi que seria útil permitir que o cliente remova a cor customizada e volte às cores originais do tema.
Solução: Adicionei a flag --reset ao comando, que remove a configuração do banco de dados. Quando não há configuração, o template não renderiza CSS, permitindo que os estilos originais do tema sejam aplicados.
private function normalizeHexColor(string $hexColor): string
{
$hexColor = trim($hexColor);
$hexColor = ltrim($hexColor, '#');
return strtoupper($hexColor);
}
private function isValidHexColor(string $hexColor): bool
{
return preg_match('/^[0-9A-F]{6}$/i', $hexColor) === 1;
}Decisões:
- Aceita cor com ou sem
# - Normaliza para maiúsculas para consistência
- Valida exatamente 6 caracteres hexadecimais
public function getButtonColorCss(): string
{
$color = $this->getButtonColor();
if (!$color) {
return '';
}
$hexColor = '#' . ltrim($color, '#');
// CSS que targeta múltiplos seletores de botões
// Aplica background-color e border-color
// Inclui estados hover
}Decisões:
- Targeta múltiplos seletores para garantir cobertura completa
- Aplica tanto
background-colorquantoborder-colorpara consistência visual - Inclui estado hover com opacidade reduzida para feedback visual
Salvamento:
$this->configWriter->save(
self::CONFIG_PATH,
$hexColor,
ScopeInterface::SCOPE_STORES,
$storeViewId
);Remoção (reset):
$this->configWriter->delete(
self::CONFIG_PATH,
ScopeInterface::SCOPE_STORES,
$storeViewId
);Decisões:
- Path:
devnicolas/button_color/color(namespace do módulo) - Scope:
SCOPE_STORESpermite configuração por store-view - Scope ID: ID da store-view permite múltiplas configurações
- Remoção via
delete()remove completamente a configuração, fazendo com que o template não renderize CSS
app/code/Devnicolas/ButtonColor/
├── registration.php # Registro do módulo no Magento
├── etc/
│ ├── module.xml # Declaração do módulo e dependências
│ └── di.xml # Registro do comando de console no DI
├── Console/
│ └── Command/
│ └── ChangeButtonColor.php # Comando CLI principal
├── Block/
│ └── ButtonColor.php # Block que gera CSS customizado
├── view/
│ └── frontend/
│ ├── layout/
│ │ └── default.xml # Layout XML para injetar CSS no head
│ └── templates/
│ └── button-color.phtml # Template que renderiza o CSS
└── README.md # Este arquivo
button,
.action.primary,
.btn-primary,
[class*="button"],
/* ... outros seletores ... */
{
background-color: #000000 !important;
border-color: #000000 !important;
}
button:hover,
.action.primary:hover,
/* ... outros seletores ... */
{
background-color: #000000 !important;
border-color: #000000 !important;
opacity: 0.9;
}Para validar que o módulo funciona conforme o teste original:
-
Executar o comando:
./bin/magento color:change 000000 1
-
Verificar resultado:
- Todos os botões da store-view ID 1 devem aparecer pretos
- A mudança deve ser visível imediatamente no frontend
- A configuração deve ser persistida
Alterar cor para preto na store-view 1 (Cenário do Teste):
./bin/magento color:change 000000 1Resultado esperado:
- Comando valida que
000000é um HEX válido - Comando valida que store-view ID 1 existe
- Configuração é salva no banco de dados
- Cache é limpo automaticamente
- Mensagem de sucesso é exibida
- Ao acessar a loja, todos os botões aparecem pretos
Remover cor customizada e restaurar cores originais:
./bin/magento color:change --reset 1Resultado esperado:
- Comando valida que store-view ID 1 existe
- Configuração é removida do banco de dados
- Cache é limpo automaticamente
- Mensagem de sucesso é exibida
- Ao acessar a loja, os botões voltam a usar as cores originais do tema
Quando uma cor é configurada, o seguinte CSS é gerado e injetado no <head>:
button,
.action.primary,
.btn-primary,
[class*="button"],
/* ... outros seletores ... */
{
background-color: #000000 !important;
border-color: #000000 !important;
}
button:hover,
.action.primary:hover,
/* ... outros seletores ... */
{
background-color: #000000 !important;
border-color: #000000 !important;
opacity: 0.9;
}- Copiar o módulo para
app/code/Devnicolas/ButtonColor/ - Executar:
php bin/magento module:enable Devnicolas_ButtonColor php bin/magento setup:upgrade php bin/magento cache:flush
./bin/magento color:change <hexColor> <storeViewId>
./bin/magento color:change --reset <storeViewId>hexColor: Cor hexadecimal (com ou sem #). Exemplos:000000,#000000,FF5733,#FF5733- Obrigatório quando não usar
--reset
- Obrigatório quando não usar
storeViewId: ID numérico da store-view. Exemplo:1,2,3- Obrigatório em todos os casos
--resetou-r: Flag para remover a configuração de cor customizada e restaurar as cores originais dos botões- Remove os estilos inline do frontend
- Remove a configuração do banco de dados
- Quando usada, não é necessário fornecer
hexColor
Alterar cor para vermelho na store-view 2:
./bin/magento color:change FF0000 2Alterar cor para azul na store-view 3 (com #):
./bin/magento color:change #0066CC 3Remover cor customizada e restaurar cores originais na store-view 1:
./bin/magento color:change --reset 1ou usando a forma curta:
./bin/magento color:change -r 1Cor inválida:
$ ./bin/magento color:change xyz123 1
Invalid hex color format. Please provide a valid 6-character hexadecimal color (e.g., 000000 or #000000).Store-view inexistente:
$ ./bin/magento color:change 000000 999
Store view with ID 999 does not exist.Sucesso (alteração de cor):
$ ./bin/magento color:change 000000 1
Button color successfully changed to #000000 for store view "Default Store View" (ID: 1).
Configuration cache has been cleared.Sucesso (reset):
$ ./bin/magento color:change --reset 1
Button color configuration successfully reset for store view "Default Store View" (ID: 1).
Inline styles have been removed. Configuration cache has been cleared.- CSS inline é carregado uma vez por página
- Configuração é lida do cache do Magento
- Não há impacto significativo no tempo de carregamento
- Validação rigorosa de formato HEX previne injeção de código
- Uso de
@noEscapeé controlado e seguro (apenas CSS gerado internamente) - Validação de store-view previne acesso não autorizado
- Funciona com qualquer tema do Magento 2
- Não requer modificação de arquivos do tema
- Compatível com diferentes versões do Magento 2
- Cache de configuração é limpo automaticamente após alteração
- Cliente pode precisar limpar cache do navegador para ver mudanças imediatamente
- Cache de página pode precisar ser limpo em alguns casos
- CSS inline pode não sobrescrever estilos muito específicos de alguns temas
- Requer limpeza de cache do navegador em alguns casos
- Não modifica cores de botões carregados via JavaScript dinamicamente
- Adicionar suporte para cores de hover customizadas
- Adicionar suporte para cores de texto dos botões
- Adicionar interface administrativa para gerenciar cores
- Adicionar preview de cores antes de aplicar
- Adicionar histórico de cores utilizadas
- Adicionar suporte para gradientes
- Adicionar validação de contraste para acessibilidade
Soluções:
- Limpar cache:
php bin/magento cache:flush - Verificar se store-view ID está correto
- Verificar se cor foi salva:
php bin/magento config:show devnicolas/button_color/color --scope=stores --scope-code=1 - Limpar cache do navegador
Soluções:
- Verificar IDs de store-views:
php bin/magento store:list - Usar ID numérico correto
Soluções:
- Verificar se módulo está habilitado:
php bin/magento module:status Devnicolas_ButtonColor - Limpar cache de layout:
php bin/magento cache:clean layout - Verificar se template está sendo carregado
Solução:
- Use a flag
--resetcom o ID da store-view:./bin/magento color:change --reset 1
- Isso remove a configuração do banco de dados e os estilos inline não serão mais renderizados
- Tabela:
core_config_data - Path:
devnicolas/button_color/color - Scope:
stores - Scope ID: ID da store-view
- Value: Cor HEX (sem #, maiúsculas)
path: devnicolas/button_color/color
scope: stores
scope_id: 1
value: 000000