Arquitetura de conteúdo e scaffold PHP para import em escala

114 páginas de referência e 14 categorias importadas via script PHP idempotente. Planilha como fonte de verdade, hash como checksum.

Cliente
Plataforma de educação médica B2C, #1 no Brasil
Setor
EdTech médica
Categoria
Papel
Arquiteto de conteúdo + dev backend
Duração
2025
Stack
ExcelPHP 8.2WordPressCustom Post Typeswp-cli
S01

Contexto

O que é: arquitetura de conteúdo + importer PHP idempotente para WordPress que publicou 114 páginas A-Z de exames, 14 categorias e 80 subcategorias a partir de uma planilha-fonte. Hash por linha como checksum, reexecutável N vezes sem efeito colateral. Live em sanarmed.com/exames; exemplo de página individual: 17-OH-progesterona.

Contexto: plano de expansão com 14 categorias e ~80 subcategorias alinhadas aos 5 ciclos de formação médica. Importação manual era inviável e não versionável.

S02

Problema

Criar ~100 páginas de taxonomia e 114 páginas de referência (A-Z Exames) à mão era lento, propenso a erro e impossível de versionar.

S03

Abordagem

Planilha como fonte de verdade (6 abas). Exportação para JSON normalizado. Importer PHP idempotente (pode rodar N vezes, só atualiza o que mudou). Hash da linha como checksum.

S04

Execução

Script CLI rodando via wp-cli. Cada linha vira um post no CPT certo, com meta fields populados e taxonomias aplicadas.

phpbin/import-exam-pages.php
<?php
declare( strict_types=1 );

require_once __DIR__ . '/../../../wp-load.php';

final class Exam_Importer {
    public function __construct( private string $source ) {}

    public function run(): void {
        $rows = json_decode( file_get_contents( $this->source ), true );
        foreach ( $rows as $row ) {
            $hash = md5( serialize( $row ) );
            $existing = get_page_by_path( $row['slug'], OBJECT, 'exame_medico' );

            if ( $existing && get_post_meta( $existing->ID, '_source_hash', true ) === $hash ) {
                continue; // Nenhuma mudança, pula
            }

            $id = wp_insert_post( $this->map_fields( $row, $existing->ID ?? 0 ) );
            update_post_meta( $id, '_source_hash', $hash );
            $this->apply_taxonomy( $id, $row['category'] );
        }
    }

    private function map_fields( array $row, int $id = 0 ): array {
        return [
            'ID'           => $id ?: 0,
            'post_type'    => 'exame_medico',
            'post_status'  => 'publish',
            'post_title'   => $row['title'],
            'post_name'    => $row['slug'],
            'post_content' => $row['body'],
            'meta_input'   => [
                '_cpt_indication'  => $row['indication'],
                '_cpt_preparation' => $row['preparation'],
                '_cpt_references'  => $row['references'],
            ],
        ];
    }

    private function apply_taxonomy( int $post_id, string $category ): void {
        wp_set_object_terms( $post_id, $category, 'exam_category', false );
    }
}

( new Exam_Importer( __DIR__ . '/data/exames.json' ) )->run();
Importer idempotente. Hash como checksum.
S05

Resultados

Resultado em tráfego orgânico após a publicação do hub /exames/ e das 114 páginas de referência individuais:

Dashboard de SEO do domínio sanarmed.com/exames mostrando 35 menções em IA, 3,1K menções totais, 4,6K páginas citadas, 5,6K de tráfego orgânico, 3K palavras-chave orgânicas com +295% de crescimento, e curva de tráfego ascendente de dez/2025 a abr/2026.
sanarmed.com/exames, 5,6K sessões orgânicas, +295% em palavras-chave e 35 menções em motores de IA (ChatGPT, Claude, Gemini) pós-importação.
114
Páginas A-Z Exames publicadas
Via importer
14
Categorias e 80 subcategorias
Alinhadas aos 5 ciclos
Idempotente
Reexecutável N vezes
Seguro em qualquer ambiente
S06

Output reutilizável

Workbook de 6 abas como sistema de planejamento, importer parametrizável para qualquer CPT, convenção de hash para controle de mudança.

Este case alimenta o grafo semântico do case 7, linkagem por embeddings: cada uma das 114 páginas virou nó no engine que conecta Exames, CID-10, Ferramentas e Blog.