migration-reference · implemented
Migration 01 — addresses
Base backend de endereços próprios de cada cadastro e cópia histórica na própria tabela.
Contrato implementado
| Coluna | Tipo PostgreSQL | Nulo | Chaves/índices | Regra |
|---|---|---|---|---|
id | bigint | não | PK | Identificador interno. |
city_id | bigint | não | FK, índice | Referencia cities.id; exclusão RESTRICT. A UF é obtida da cidade. |
street | varchar(255) | não | — | Rua ou logradouro obrigatório. |
number | varchar(255) | não | — | Número textual obrigatório; aceita 123 A, 0 e s/n. |
neighborhood | varchar(255) | não | — | Bairro obrigatório. |
zip_code | varchar(255) | sim | — | Valor opcional; sem validação de formato ou limpeza de dígitos nesta etapa. |
created_at | timestamp(0) | sim | — | Timestamp nativo do Laravel; preenchido pelo Eloquent no timezone institucional. |
updated_at | timestamp(0) | sim | — | Timestamp nativo do Laravel; atualizado pelo Eloquent. |
A migration usa $table->timestamps(), com a nulabilidade padrão do Laravel. O Eloquent converte esses campos automaticamente em datas, sem casts explícitos no model. Inserções diretas que não passem pelo Eloquent podem deixar os timestamps nulos. O down() remove a tabela.
A cidade é obrigatória e selecionada no catálogo local, criado antes desta tabela. Não há complement, UF duplicada, tabela de snapshots, copied_from_address_id ou campos de autoria. A tabela não usa SoftDeletes nem Scout/Searchable.
Propriedade dos endereços
addresses centraliza as colunas de endereço, mas suas linhas não são compartilhadas. Cada linha pode pertencer a no máximo um registro proprietário, seja em user_personal_data, campuses, granting_parties ou nos futuros endereços históricos de internships. Isso vale também para dois registros da mesma tabela: conteúdo idêntico recebe IDs diferentes. Um novo proprietário recebe uma nova linha, nunca o address_id já vinculado a outro registro. A cópia histórica cria outra linha na mesma tabela.
O schema atual contém FKs nos cadastros proprietários, mas ainda não impõe essa exclusividade entre todas as tabelas. Os futuros fluxos de integração devem respeitar a regra ao criar e trocar endereços; uma garantia no banco exige desenho próprio antes de ser declarada implementada.
Na gestão de campi, a edição atualiza a mesma linha de endereço e preserva seu ID. Antes da mudança, o controller verifica referências em campi, dados pessoais, concedentes e estágios; se encontrar outro proprietário ou uso histórico, rejeita a operação. A verificação é de aplicação, pois o schema não impõe exclusividade entre as FKs.
Model, validação e auditoria
Address declara os campos cadastrais com #[Fillable], usa HasFactory e possui casts de city_id para inteiro e dos campos textuais para string. Address::city() é BelongsTo; City::addresses() é HasMany. As relações com modelos ainda inexistentes não foram adicionadas.
AddressValidationRules centraliza as regras aplicadas no evento saving do model: cidade existente, logradouro, número e bairro obrigatórios, todos com os limites do schema. O número é textual, sem restringir os valores ao formato numérico. A rota de campus valida o endereço aninhado em um Form Request e cria ou atualiza Address via Eloquent. Ainda não há endpoint genérico para gerenciar endereços.
O CEP omitido, nulo ou vazio é persistido como null. Valores informados não passam por validação de CEP, normalização por helper ou remoção de máscara; varchar(255) preserva o valor sem preenchimento de espaços. A validação de oito dígitos e o tratamento de máscaras serão definidos em uma etapa futura.
O Activity Log registra os campos cadastrais, somente quando há mudanças, sem registros vazios. A autoria vem do próprio mecanismo de auditoria. AddressFactory usa CityFactory para criar fixtures independentes de rede e da carga completa do catálogo.
Cópia histórica implementada
CopyAddress::handle(Address $address): Address exige uma origem persistida, relê a linha dentro de uma transação com bloqueio compartilhado e cria outra linha em addresses. A cópia recebe novo ID e timestamps, não altera a origem e ignora mudanças ainda não salvas no objeto recebido. Cópias sucessivas são independentes.
Uma origem não persistida gera InvalidArgumentException; se a linha já foi removida, a releitura gera ModelNotFoundException. A criação da cópia também passa pelas regras do model e pelo Activity Log.
O fluxo de campus impede editar seu endereço quando uma referência de outro proprietário ou estágio aponta para a mesma linha. Não há bloqueio genérico de edição/exclusão de endereços históricos para os demais cadastros; essa proteção será implementada junto às futuras FKs e operações de formalização. A cópia existe, mas os outros fluxos ainda precisam integrar seu uso.
Na formalização futura, internships.workplace_address_id deverá apontar para uma cópia do endereço da concedente nesta mesma tabela. internships.student_address_id poderá apontar para a cópia do endereço do discente quando necessário. Essas linhas deverão ser preservadas sem alterações retroativas; a mudança do cadastro atual poderá criar outra linha e trocar sua FK.
Consulta de CEP futura
A consulta à BrasilAPI está fora da base atual. Quando implementada, sua resposta servirá apenas como sugestão de preenchimento e não impedirá a edição manual em caso de CEP geral, ausência ou indisponibilidade.
A cidade deverá continuar sendo resolvida exclusivamente no catálogo local: primeiro pelo código IBGE quando disponível; caso contrário, pelo nome normalizado e pela UF. Ausência ou ambiguidade exigirá seleção manual. A integração não poderá criar cidades a partir da resposta externa.
Testes
tests/Feature/AddressesTest.php cobre tipos e limites PostgreSQL, nulabilidade, índices, FK RESTRICT, migrate e rollback, factories, casts, relacionamentos, campos obrigatórios, número textual, CEP opcional sem validação de formato, cópia histórica e Activity Log. tests/Feature/CampusManagementTest.php verifica edição no mesmo endereço, bloqueio quando ele também estiver ligado a outro cadastro ou estágio e rollback dos registros e atividades. Como user_personal_data referencia addresses, o teste de rollback reverte em conjunto as migrations aplicadas a partir de addresses, testa a remoção da tabela e reaplica o conjunto. Os testes usam Http::fake() e preventStrayRequests() para impedir acesso à rede.
./vendor/bin/sail artisan test --compact tests/Feature/AddressesTest.php tests/Feature/CitiesTest.php tests/Unit/DatabaseDriverGuardTest.php
./vendor/bin/sail exec laravel.test vendor/bin/pint --dirty --format agentOs testes cobrem o schema PostgreSQL, endereços, cidades e a guarda global de migrations; o mapa atual está em Testes existentes.
Checklist
- Definir cidade por FK para catálogo IBGE e CEP opcional.
- Confirmar campos obrigatórios e usar o limite padrão de 255 caracteres para os campos textuais.
- Criar migration reversível
create_addresses_tablecom índice emcity_ide FKRESTRICT. - Criar
Address,AddressFactory,CityFactorye relacionamentos existentes. - Validar cidade, logradouro, bairro e número textual, incluindo
s/n. - Usar timestamps nativos, sem casts explícitos de data.
- Registrar alterações cadastrais no Activity Log, sem campos de autoria.
- Implementar e testar a cópia histórica na própria tabela.
- Testar schema, migrate, rollback e persistência em PostgreSQL.
- Atualizar Domínio e modelo de dados.
- Definir validação e normalização de CEP no futuro fluxo de cadastro.
- Implementar a consulta opcional de CEP e resolução da cidade no catálogo local.
- Criar telas e endpoints de cadastro nas etapas correspondentes.
- Integrar cópias e impedir alteração/exclusão de linhas históricas ao implementar a formalização.