codinggames

Eu criei uma extensão em estilo RPG retro para a minha loja do Magento 2

Criando um marketplace personalizado no Magento 2 com Vue, Tailwind e um toque de RPG

Escrito em 2 de fevereiro de 2025 - 🕒 11 min. de leitura

O meu mais novo projeto (ou side-gig?), Zenkai Zone, já se transformou em um site de e-commerce sólido, mas sejamos francos — usar apenas o template padrão do Magento 2 é meio sem graça, quer dizer, vamos lá, eu sou o cara que criou um jogo inteiro para este blog, eu deveria conseguir trazer um pouco de diversão para o meu e-commerce. Não é mesmo?

Ideação

Eu queria dar um toque especial, injetar um pouco de personalidade e tornar a experiência de compra mais envolvente. Então, decidi criar o GameShop — uma extensão para Magento 2 desenvolvida especificamente para a Zenkai Zone que transforma a vitrine em algo reminiscentemente de uma loja de RPG old-school. Com uma página dedicada, completa com um avatar de lojista, endpoints de API customizados e um toque de magia em SEO para compartilhamento nas mídias sociais, a ideia era deixar meu site menos monótono e mais divertido.

Google be like: I have never met this page in my life
Google be like: I have never met this page in my life

Neste guia, vou mostrar como combinei Vue.js, TailwindCSS, APIs customizadas do Magento e melhorias estratégicas de SEO para dar aquele toque especial na Zenkai Zone. Ao longo do caminho, você verá trechos de código e meus comentários sobre o que está acontecendo nos bastidores. Vamos nessa!

Visão Geral do Projeto: O que Estamos Construindo

  • Uma nova página de vitrine alimentada por Vue + Tailwind em /game-shop, personalizada para a Zenkai Zone.
  • Endpoints de API para gerenciar carrinho, categorias e produtos de formas que o núcleo do Magento talvez não permita tanta flexibilidade.
  • Metadados amigáveis para SEO e tags Open Graph para uma pré-visualização matadora no compartilhamento em redes sociais.
  • Uma nova configuração no painel de administração para ativar ou desativar toda a extensão sem precisar mexer no código.

Estrutura do Módulo Magento 2

Segue a estrutura organizada de arquivos para o módulo GameShop. Mantê-la organizada significa que sempre posso encontrar o que preciso — mesmo se estiver no meio de outra ideia para a Zenkai Zone:

app/code/Werules/GameShop/
├── Api/
│   ├── CartManagementInterface.php
│   ├── CategoryManagementInterface.php
│   ├── ProductManagementInterface.php
├── Controller/
│   ├── Index/
│   │   ├── Index.php
├── etc/
│   ├── adminhtml/
│   │   ├── system.xml
│   ├── frontend/
│   │   ├── di.xml
│   │   ├── module.xml
│   │   ├── routes.xml
│   ├── webapi.xml
├── Model/
│   ├── CartManagement.php
│   ├── CategoryManagement.php
│   ├── ProductManagement.php
├── view/
│   ├── frontend/
│   │   ├── layout/
│   │   │   ├── werules_gameshop_index_index.xml
│   │   ├── templates/
│   │   │   ├── index.phtml
│   │   ├── web/
│   │   │   ├── css/
│   │   │   │   ├── gameshop.css
│   │   │   ├── js/
│   │   │   │   ├── vue.global.js
│   │   │   │   ├── tailwind.min.js
│   │   │   ├── images/
│   │   │   │   ├── shopkeeper.png
├── registration.php

Passo 1: Criando a Página Customizada no Frontend

Definindo a Rota

O primeiro passo foi configurar a rota. No arquivo routes.xml, mapeei /game-shop para a rota do módulo customizado, garantindo que todo o tráfego para essa extensão seja direcionado corretamente.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="werules_gameshop" frontName="game-shop">
            <module name="Werules_GameShop"/>
        </route>
    </router>
</config>

Criando o Controller

Em seguida, veio o controller Index.php. Este arquivo decide se a nova página do GameShop será exibida ou, se a extensão estiver desativada, mostra a página padrão de “rota não encontrada” do Magento.

<?php
namespace Werules\GameShop\Controller\Index;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;

class Index extends Action
{
    protected $scopeConfig;

    public function __construct(
        Context $context,
        ScopeConfigInterface $scopeConfig
    ) {
        parent::__construct($context);
        $this->scopeConfig = $scopeConfig;
    }

    public function execute()
    {
        // Check if the extension is activated for [Zenkai Zone](https://zenkaizone.com)
        $isEnabled = $this->scopeConfig->isSetFlag('werules/gameshop/enabled', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
        if (!$isEnabled) {
            // If not, redirect to Magento’s default no-route page
            return $this->resultFactory->create(ResultFactory::TYPE_FORWARD)->forward('noroute');
        }

        // Otherwise, load our new, fun page
        $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        $resultPage->getConfig()->getTitle()->set(__('Game Shop'));
        return $resultPage;
    }
}

Passo 2: Aprimorando as APIs do Magento

O sistema de API do Magento 2 é robusto, mas eu queria mais controle para a Zenkai Zone. Ao introduzir interfaces e implementações customizadas, pude adaptar o comportamento exatamente como necessário.

API de Gerenciamento de Carrinho (CartManagement.php)

Se a extensão estiver desativada, a API retorna educadamente zero; caso contrário, ela se comporta como as funções padrão de carrinho — só que com um toque extra de personalidade.

public function getCartItemCount()
{
    if (!$this->isModuleEnabled()) {
        return 0;
    }

    $quote = $this->cart->getQuote();
    return (int)$quote->getItemsQty();
}

public function addItemToCart($productId, $qty = 1)
{
    if (!$this->isModuleEnabled()) {
        return ['success' => false, 'message' => 'GameShop is disabled.', 'cart_count' => 0];
    }

    // Normal add-to-cart logic here
}

API de Gerenciamento de Produtos (ProductManagement.php)

De forma similar, esta API fornece listagens de produtos de qualquer categoria, mas somente se a extensão estiver ativa.

public function getProductsByCategoryId($categoryId)
{
    if (!$this->isModuleEnabled()) {
        return [];
    }

    // Standard product retrieval logic
}

Desenhando uma Interface Inspirada em RPG Retrô para o GameShop

Eu queria dar à Zenkai Zone uma dose extra de personalidade. Injetando uma vibe de “loja clássica de RPG” usando Vue.js para reatividade e TailwindCSS para estilização, a vitrine se transforma em algo divertido e memorável — bem mais envolvente do que uma página de e-commerce típica.

Segue o layout:

  1. Cabeçalho fixo exibindo o nome da loja.
  2. Barra lateral do lojista com um avatar, contagem de itens do carrinho em tempo real e opções de menu (Comprar, Vender, Falar, Sair).
  3. Painel principal exibindo listagens dinâmicas de categorias e produtos, além de uma visualização detalhada do produto.

Tela principal da loja
Tela principal da loja

Montando o Layout

Dentro do arquivo index.phtml, configurei o container para minha aplicação Vue:

Container da Nossa Aplicação Vue.js

<div id="app" class="min-h-screen bg-black text-orange-200 font-mono p-6 flex flex-col space-y-4 text-lg">
  • O Vue monta na #app.
  • O fundo preto + texto laranja evocam aquele visual nostálgico de consoles antigos.

Um Cabeçalho Fixo para o Nome da Loja

<div class="border-2 border-orange-500 p-3 sticky top-0 bg-black z-50">
    <h1 class="text-3xl md:text-4xl font-bold tracking-widest uppercase">
        <?php echo $currentStoreName; ?>
    </h1>
</div>
  • O cabeçalho fixo mantém o nome da loja visível, para que os clientes sempre saibam que estão na Zenkai Zone.

Esta seção é toda sobre personalidade. O avatar do lojista dá as boas-vindas aos visitantes, enquanto a contagem do carrinho e os botões do menu (Comprar, Vender, Falar, Sair) adicionam um toque interativo.

<!-- Shopkeeper Avatar -->
<div class="border-2 border-orange-500 p-2 flex justify-center items-center bg-black">
    <img src="<?= $block->getViewFileUrl('Werules_GameShop::images/shopkeeper.png'); ?>"
         alt="<?= __('Shopkeeper Avatar'); ?>"
         class="w-32 h-32 object-cover border border-orange-500 bg-black avatar">
</div>

<!-- Cart Count -->
<div class="mb-4">
    <div class="text-lg text-orange-400"><?= __('Cart items:'); ?></div>
    <div class="text-2xl font-bold">{{ cartCount }}</div>
</div>

Atualizações em tempo real via Vue mantêm a interface dinâmica e responsiva.

Adicionando os Botões do Menu

<button class="block w-full py-3 border border-orange-500 hover:bg-orange-900 text-center px-4 text-lg"
        :class="{'bg-orange-500 text-black': menuSelection === 'Buy'}"
        @click="onMenuItemClick('Buy')">
    <?= __('Buy'); ?>
</button>

<button class="block w-full py-3 border border-orange-500 hover:bg-orange-900 text-center px-4 text-lg"
        :class="{'bg-orange-500 text-black': menuSelection === 'Sell'}"
        @click="onMenuItemClick('Sell')">
    <?= __('Sell'); ?>
</button>

<button class="block w-full py-3 border border-orange-500 hover:bg-orange-900 text-center px-4 text-lg"
        @click="onMenuItemClick('Exit')">
    <?= __('Exit'); ?>
</button>
  • A estilização condicional destaca a seleção atual.
  • O botão Sair pode redirecionar os clientes de volta ao site principal da Zenkai Zone, se necessário.

Área de Conteúdo Principal: Categorias & Listagens de Produtos

Quando os usuários selecionam “Comprar”, eles veem categorias exibidas no topo. Ao selecionar uma categoria, uma grade de produtos é mostrada — conteúdo dinâmico alimentado pelas nossas APIs customizadas.

Abas de Categorias

<!-- Category Tabs -->
<div v-if="activeView === 'list'" class="flex flex-wrap gap-2 mb-3">
    <button v-for="cat in categories" :key="cat.id"
            class="px-3 py-1 border border-orange-500 hover:bg-orange-500 hover:text-black transition"
            :class="{'bg-orange-500 text-black': currentCategoryId === cat.id}"
            @click="loadProducts(cat.id, cat.name)">
        {{ cat.name }}
    </button>
</div>
  • As categorias são buscadas através do endpoint rest/V1/gameshop/categories.
  • Um loop Vue v-for garante que cada categoria seja renderizada automaticamente.

Grade de Listagem de Produtos

<div v-if="activeView === 'list'">
    <div v-if="products.length === 0" class="text-center text-orange-400 text-lg mt-6">
        <?= __('No products found in this category.'); ?>
    </div>

    <div class="space-y-3">
        <div v-for="product in products" :key="product.id"
             class="border border-orange-500 p-4 flex flex-col md:flex-row items-center cursor-pointer hover:bg-orange-900 transition"
             @click="showDetails(product)">
            <img :src="product.image_url"
                 class="w-32 h-32 object-cover border border-orange-500 bg-black">
            <div class="flex-1 text-center md:text-left">
                <span class="block text-lg">{{ product.name }}</span>
                <span class="block text-xl text-orange-300 mt-2">{{ formatPrice(product.price) }}</span>
            </div>
        </div>
    </div>
</div>
  • Clicar em um produto exibe a visualização detalhada.
  • O design garante que cada produto se destaque, adicionando um toque especial à experiência de compra.

Tela de listagem de produtos
Tela de listagem de produtos

Visualização Detalhada do Produto

Quando um produto é selecionado, uma visualização detalhada mostra seu nome, preço, descrição e um botão de Adicionar ao Carrinho.

<div v-if="activeView === 'detail' && currentProduct" class="space-y-4">
    <p class="italic text-lg mb-2">
        <?= sprintf(__('Ah, the %s! This item might do something special...'), '<strong>{{ currentProduct.name }}</strong>'); ?>
    </p>
    
    <div class="flex flex-col md:flex-row space-y-4 items-start border border-orange-500 p-4">
        <img :src="currentProduct.image_url"
             class="w-72 h-72 object-cover border border-orange-500 bg-black">
        <div class="flex-1">
            <h3 class="text-2xl font-bold">{{ currentProduct.name }}</h3>
            <p class="text-orange-300 text-xl mb-3">{{ formatPrice(product.price) }}</p>
            <p class="text-lg text-orange-300">
                {{ currentProduct.description }}
            </p>
        </div>
    </div>

    <div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-6">
        <button @click="addToCart(currentProduct.id)"
                class="px-5 py-3 bg-orange-500 text-black border border-orange-500 hover:bg-orange-600 transition text-xl">
            <?= __('Add to Cart'); ?>
        </button>
        <button @click="goBackToList()"
                class="px-5 py-3 border border-orange-500 hover:bg-orange-900 text-orange-200 transition text-xl">
            <?= __('Back'); ?>
        </button>
    </div>
</div>

A simples alternância entre as visualizações “list” e “detail” — alimentada pelo Vue — mantém a experiência do usuário suave e sem interrupções.

Tela de detalhes do produto
Tela de detalhes do produto

Conversa com o Lojista: O Menu “Falar”

Para dar um toque de personalidade, o menu “Falar” permite que o lojista compartilhe frases de efeito aleatórias. É um recurso opcional que adiciona ainda mais caráter à experiência de compra.

<div v-else-if="menuSelection === 'Talk'" class="flex items-center justify-center h-64 border-2 border-orange-500 p-6">
    <p class="text-3xl text-orange-400 text-center">
        {{ currentTalkLine }}
    </p>
</div>
methods: {
    onMenuItemClick(selection) {
        if (selection === 'Talk') {
            const randomIndex = Math.floor(Math.random() * this.talkLines.length);
            this.currentTalkLine = this.talkLines[randomIndex];
        }
    }
}

Este pequeno recurso pode até ser estendido para oferecer descontos surpresa ou recomendações de produtos.

Passo 3: Aprimoramentos em SEO

Para impulsionar o compartilhamento nas mídias sociais da Zenkai Zone, adicionei meta tags e dados JSON-LD dentro do <head>. Isso garante que plataformas como Facebook e Twitter exibam uma pré-visualização atraente com a imagem do lojista.

Atualize werules_gameshop_index_index.xml

<referenceBlock name="head.additional">
    <block class="Magento\Framework\View\Element\Template"
           name="werules.gameshop.head"
           template="Werules_GameShop::seo/head.phtml"/>
</referenceBlock>

Crie o head.phtml

<?php
$mediaUrl = $block->getViewFileUrl('Werules_GameShop::images/shopkeeper.png');
$baseUrl = $block->getBaseUrl();
$pageTitle = __('GameShop - Buy & Sell Games');
$pageDescription = __('Find the best gaming deals, buy and sell games easily!');
?>

<!-- Open Graph Meta Tags -->
<meta property="og:image" content="<?= $mediaUrl ?>"/>
<meta property="og:image:alt" content="GameShop Shopkeeper Avatar"/>
<meta property="og:url" content="<?= $baseUrl ?>game-shop"/>

<!-- Twitter Card -->
<meta name="twitter:image" content="<?= $mediaUrl ?>"/>

<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "WebPage",
    "name": "<?= $pageTitle ?>",
    "description": "<?= $pageDescription ?>",
    "image": "<?= $mediaUrl ?>",
    "url": "<?= $baseUrl ?>game-shop",
    "publisher": {
        "@type": "Organization",
        "name": "GameShop",
        "logo": "<?= $mediaUrl ?>"
    }
}
</script>

Com essas tags implementadas, a extensão não só tem uma ótima aparência como também apresenta um desempenho superior em SEO e compartilhamento nas mídias sociais.

Etapas Finais & Testes

Ativando a Extensão

bin/magento module:enable Werules_GameShop
bin/magento setup:upgrade
bin/magento cache:flush

Agora o seu módulo está oficialmente ativo.

Testando o Frontend

Acesse https://seumagento.com/game-shop e se você vir a interface em preto e laranja com o lojista, o trabalho está concluído.

Claro, você também pode conferir uma demonstração ao vivo da extensão GameShop em zenkaizone.com/game-shop.

Conclusão

É isso aí! Transformamos a vitrine da Zenkai Zone criando uma extensão customizada para Magento 2 que não só introduz novas APIs para carrinho e produtos, mas também traz um design divertido, inspirado em RPG, com excelente integração de SEO. Para o futuro, há muito espaço para expandir este conceito:

  • Autenticação de usuários para permitir que os clientes salvem seu progresso na loja virtual.
  • Integração de checkout com a rota customizada game-shop para uma experiência completa de vitrine alternativa.
  • Filtragem avançada de produtos para combinar gêneros de jogos ou até mesmo referências retrô aleatórias.

Eu me diverti muito misturando um pouco da nostalgia dos jogos com a funcionalidade moderna do e-commerce usando Vue.js e TailwindCSS. O resultado final é uma extensão peculiar e envolvente que torna a Zenkai Zone muito mais interessante. Quem diria que fazer compras online poderia se transformar em uma aventura?

Contribuindo com a Extensão GameShop

Como em todos os meus projetos, a extensão GameShop é open-source e está disponível no GitHub. Fique à vontade para fazer um fork, testar e contribuir com o projeto. Adoraria ver o que você vai criar!

Para onde vamos a partir daqui? Talvez integrar o ChatGPT para oferecer recomendações de produtos personalizadas ou adicionar um sistema de moedas para que os clientes possam trocar “moedas de ouro” por descontos. Independentemente do próximo passo, o objetivo é claro: manter a experiência de compra sempre fresca e divertida.

Happy coding!

Tags:


Publicar um comentário

Comentários

Nenhum comentário.