Repositories
Camada de consulta e persistência que coordena Models.
Repository
Framework\Database\Repository instancia o model configurado pela propriedade $modelClass e implementa o contrato de RepositoryInterface: all(), findById(), create(), update(), delete(), paginate(). O model fica acessível por helpers protegidos model() e db().
Especializar
$users = new \App\Repositories\UserRepository();
$user = $users->findByEmail('ana@example.com');
$activeUsers = $users->getActive();
$page = $users->search('ana', page: 1, perPage: 15);O exemplo usa APIs presentes no Model; repositories reais da aplicação incluem buscas de usuários e tokens de API. O repository não substitui autorização nem validação.
Contrato de base e extensão por domínio
Framework\Database\Repository guarda um Model configurado em $modelClass e delega operações genéricas. O UserRepository da aplicação acrescenta consultas por e-mail, ativos e busca paginada; este é o ponto em que filtro de domínio específico pode ganhar nome e teste próprios.
$users = new \App\Repositories\UserRepository();
$matches = $users->search('ana', page: 1, perPage: 15);
$user = $users->findByEmail('ana@example.com');`search()` seleciona colunas não sensíveis, combina nome/e-mail e retorna array paginado. `findByEmail()` retorna objeto ou false. A consulta usa Model; Controller/API Resource ainda são responsáveis por autorização e formato de saída.
Assinaturas: interface versus classe concreta
| Método | Retorno | Origem |
|---|---|---|
| all() | array | RepositoryInterface e Repository |
| findById(int $id) | object|false | RepositoryInterface e Repository |
| create(array $data) | string|false | RepositoryInterface e Repository |
| update(int $id, array $data) | bool | RepositoryInterface e Repository |
| delete(int $id) | bool | RepositoryInterface e Repository |
| paginate(int $perPage=15, int $page=1) | array paginado | Repository concreto; não está declarado em RepositoryInterface |
Escolha Repository quando a consulta recebe significado de domínio ou deve ser reutilizada; para uma operação CRUD isolada, o Model já fornece query builder e persistência. A camada não implementa transações automaticamente.
Contrato base e falhas de construção
Framework\Database\Repository recebe $modelClass configurado pela subclasse, instancia esse Model e delega operações comuns. A construção lança RuntimeException se a classe estiver ausente ou não existir. Métodos protegidos de resolução não fazem parte do contrato público da RepositoryInterface; paginate() é método concreto do Repository, não da interface minimalista.
Os exemplos atuais do projeto são UserRepository e ApiTokenRepository. Para filtros fluentes e limites, consulte Model; operações diretas PDO ficam em Database.
ApiTokenRepository: ciclo de vida e retornos
issue(int $userId, string $name = 'api', ?string $expiresAt = null): array cria o token e retorna o segredo em texto puro apenas nessa resposta; persiste somente o hash. findValidByPlainToken(string $plain): object|false valida o texto recebido contra o hash e rejeita tokens expirados. touchLastUsed(int $id): void atualiza o uso sem retorno. revoke(int $id): bool e revokeAllForUser(int $userId): bool informam sucesso como booleano.
$issued = $tokens->issue($userId, 'integration', '2026-12-31 23:59:59');
$plainToken = $issued['token']; // entregar uma vez; não registrar em log
$record = $tokens->findValidByPlainToken($plainToken);Guarde o token apenas no cliente autorizado; não há como recuperar o texto puro do hash persistido. API tokens e sessões de navegador são mecanismos distintos.