{
  "id": "cast-cpfcast",
  "title": "Cast — CpfCast",
  "description": "Cast Eloquent que valida CPF e armazena o documento sem máscara.",
  "type": "technical-reference",
  "status": "implemented",
  "visibility": "public",
  "tags": [
    "sge/casts",
    "sge/dados-pessoais",
    "sge/seguranca"
  ],
  "related": [
    "migration-02-user-personal-data",
    "model-user",
    "helpers"
  ],
  "sourceRefs": [
    "https://github.com/sge-suite/sge/blob/master/app/Casts/CpfCast.php",
    "https://github.com/sge-suite/sge/blob/master/tests/Unit/CpfCastTest.php"
  ],
  "authors": [],
  "updated": null,
  "diagram": null,
  "body": "> [!success] Estado atual\n> Implementado em `app/Casts/CpfCast.php` e usado hoje em `User::$cpf`.\n\n## Responsabilidade\n\nImplementa `CastsAttributes<string, string>` para controlar a fronteira entre o valor recebido pela aplicação e o valor persistido.\n\n| Operação                   | Comportamento                                                                                                  |\n| -------------------------- | -------------------------------------------------------------------------------------------------------------- |\n| `get()`                    | Retorna `null` para `null`; para os demais valores, retorna string sem alterar o valor.                        |\n| `set()` com `null` ou `''` | Persiste `null`.                                                                                               |\n| `set()` com valor          | Usa o helper global `unmask()`, valida com `LaravelLegends\\PtBrValidator\\Rules\\Cpf` e retorna somente dígitos. |\n| CPF inválido               | Lança `InvalidArgumentException` com mensagem em português.                                                    |\n\n## Exemplo\n\n```php\n$user->cpf = '529.982.247-25';\n$user->save();\n\n// banco: 52998224725\n```\n\n## Uso\n\n`User` possui `cpf` no `$fillable`, na docblock e no cast. O CPF permanece na conta como identificador único; [`user_personal_data`](doc:migration-02-user-personal-data) não o replica.\n\n## Checklist\n\n- [x] Implementar `get()` e `set()`.\n- [x] Remover máscara antes da persistência.\n- [x] Rejeitar CPF inválido.\n- [x] Cobrir CPF mascarado, limpo e inválido em `tests/Unit/CpfCastTest.php`.\n- [ ] Definir comportamento para string composta apenas por espaços.\n- [x] Manter o cast em `User`, sem duplicar CPF no Model de dados pessoais.\n- [ ] Confirmar que CPF não é editável pela configuração do usuário.\n- [ ] Garantir que consultas e snapshots usem o formato canônico.\n\n## Relacionamentos\n\n- [Model User](doc:model-user)\n- [Helpers](doc:helpers)\n- [Migration de user_personal_data](doc:migration-02-user-personal-data)",
  "sections": [
    {
      "id": "responsabilidade",
      "level": 2,
      "title": "Responsabilidade",
      "text": "Implementa `CastsAttributes<string, string>` para controlar a fronteira entre o valor recebido pela aplicação e o valor persistido.  | Operação                   | Comportamento                                                                                                  | | -------------------------- | -------------------------------------------------------------------------------------------------------------- | | `get()`                    | Retorna `null` para `null`; para os demais valores, retorna string sem alterar o valor.                        | | `set()` com `null` ou `''` | Persiste `null`.                                                                                               | | `set()` com valor          | Usa o helper global `unmask()`, valida com `LaravelLegends\\PtBrValidator\\Rules\\Cpf` e retorna somente dígitos. | | CPF inválido               | Lança `InvalidArgumentException` com mensagem em português.                                                    |",
      "line": 4
    },
    {
      "id": "exemplo",
      "level": 2,
      "title": "Exemplo",
      "text": "",
      "line": 15
    },
    {
      "id": "uso",
      "level": 2,
      "title": "Uso",
      "text": "`User` possui `cpf` no `$fillable`, na docblock e no cast. O CPF permanece na conta como identificador único; [`user_personal_data`](doc:migration-02-user-personal-data) não o replica.",
      "line": 24
    },
    {
      "id": "checklist",
      "level": 2,
      "title": "Checklist",
      "text": "- [x] Implementar `get()` e `set()`. - [x] Remover máscara antes da persistência. - [x] Rejeitar CPF inválido. - [x] Cobrir CPF mascarado, limpo e inválido em `tests/Unit/CpfCastTest.php`. - [ ] Definir comportamento para string composta apenas por espaços. - [x] Manter o cast em `User`, sem duplicar CPF no Model de dados pessoais. - [ ] Confirmar que CPF não é editável pela configuração do usuário. - [ ] Garantir que consultas e snapshots usem o formato canônico.",
      "line": 28
    },
    {
      "id": "relacionamentos",
      "level": 2,
      "title": "Relacionamentos",
      "text": "- [Model User](doc:model-user) - [Helpers](doc:helpers) - [Migration de user_personal_data](doc:migration-02-user-personal-data)",
      "line": 39
    }
  ],
  "sourcePath": "content/cast-cpfcast.md",
  "visuals": [],
  "apiVersion": 1
}
