PHP MVCDOCUMENTAÇÃO
v2.0.0

Models

Mapeamento de tabela e operações de persistência da classe base Model.

Definição

Um Model estende Framework\Database\Model e configura propriedades protegidas como $table, $fillable, $hidden, $timestamps e $softDelete. A implementação do projeto, por exemplo, usa App\Models\User. O exemplo Post é uma classe de domínio que você criaria; no repositório entregue, o Model concreto é App\Models\User.

class Post extends Model
{
    protected string $table = 'posts';
    protected array $fillable = ['title', 'body'];
    protected bool $timestamps = true;
}

Leituras e filtros

Consulte a referência individual dos métodos para assinaturas, parâmetros, retornos e exemplos de cada leitura e filtro; os filtros fluentes retornam a mesma instância.

Escritas

As operações de criação, atualização e remoção estão explicadas uma a uma na referência individual do Model, incluindo mass assignment, soft delete e os requisitos de WHERE.

Métodos de query alteram o builder interno. Inicie outra instância ou encerre a consulta antes de compor uma consulta independente. O projeto não traz relações ORM como hasMany/belongsTo.

Paginação

paginate($perPage=15,$page=1) devolve array com dados e metadados de página utilizados pelo componente e pela resposta paginada da API.

Transações

Use Database::transaction(callable) para operações atômicas. Services herdam transaction() protegido. O suporte de conexão fornecido é MySQL, não SQLite.

O Model que existe no projeto

App\Models\User mapeia a tabela users, ativa timestamps e soft delete e declara fillable/hidden. É uma implementação Active Record pequena: instancia-se o Model para executar a consulta; não há proxy de relações nem eager loading.

$users = new \App\Models\User();
$user = $users->findByEmail('ana@example.com');

$page = (new \App\Models\User())
    ->where('active', 1)
    ->orderBy('name')
    ->paginate(15, 1);

`findByEmail()` é método específico de User; `where()`, `orderBy()` e `paginate()` vêm do Model base. O resultado de findByEmail é objeto ou false; paginate devolve array com data e metadados.

Mass assignment, soft delete e retorno

OperaçãoRetornoComportamento relevante
find($id), findBy($column,$value)object|falseConsulta simples; soft-deleted não é retornado quando habilitado.
findOrFail($id), firstOrFail()objectLança RuntimeException se ausente; não converte sozinho em HTTP 404.
create($data)string|falseRetorna ID inserido; filtra fillable quando a lista está configurada e adiciona timestamps.
update($id,$data), delete($id)boolAtualiza por ID; delete é soft delete quando $softDelete=true.
updateWhere($data), deleteWhere()boolOperam na condição fluente; exigem WHERE por segurança.
forceDelete($id)boolRemove fisicamente mesmo se soft delete estiver ativado.
Defina uma lista $fillable explícita: no código, lista vazia significa que filterFillable() não remove nenhum campo. Não assuma que $hidden filtra qualquer `json_encode()` genérico; a sessão Auth e os Resources da API fazem suas próprias escolhas de campos.

Operações de escrita que precisam ser atômicas podem usar `Database::transaction(callable)`. A paginação retorna `data`, `total`, `page`, `per_page`, `last_page`, `from` e `to`. Para separar consultas do controller, use a camada Repository.

Comportamentos importantes do Model

  • Os filtros vivem na instância. Métodos terminais como get()/first()/paginate() executam e limpam o builder; crie outra instância para uma consulta independente.
  • O filtro whereIn() com array vazio gera condição sempre falsa; whereNotIn() vazio não adiciona filtro.
  • updateWhere()/deleteWhere() exigem condição WHERE e lançam exceção se não houver filtro.
  • delete()/deleteWhere() atualizam deleted_at quando soft delete está ativo; forceDelete() remove fisicamente.
  • insertMany() usa o conjunto de colunas da primeira linha; forneça a mesma chave/coluna em cada linha.
  • firstOrCreate() e updateOrCreate() declaram retorno object; se a inserção subjacente falhar e não houver ID para buscar, a implementação pode gerar TypeError. Trate/valide entradas e constraints no banco.
  • Paginação normaliza perPage para pelo menos 1 e page para pelo menos 1, sem teto geral de 100. Com zero resultados, last_page=0, from=1 e to=0. O UserApiController aplica seu próprio teto HTTP de 100.
  • Não há relações ORM/eager loading; joins são explícitos. $hidden não é serialização automática genérica; Resource/Auth escolhem os campos.
$result = (new \App\Models\User())
    ->where('active', 1)
    ->whereGroup(function ($q) use ($term): void {
        $q->whereLike('name', $term)
          ->whereLike('email', $term, 'or');
    })
    ->orderBy('name')
    ->paginate(15, 1);

O exemplo agrupa a alternativa OR para que active=1 se aplique aos dois campos. Para os métodos e a semântica de cada filtro, veja Query Builder; para operações SQL diretas, veja Database/PDO.

Writes: whitelist, identificadores e soft delete

Defina protected array $fillable explicitamente em cada Model. Quando a lista está vazia, o filtro de atribuição aceita as chaves recebidas; métodos como create(), insertMany(), update() e updateWhere() usam nomes das chaves para compor SQL. Não passe $request->all() ou mapas de JSON diretamente para esses métodos. Valide valores e mantenha as chaves/colunas sob controle do código; $fillable não é validação de domínio nem substitui a validação de identificadores.

Com soft delete, as leituras comuns excluem registros com deleted_at, e delete($id)/deleteWhere() marcam a exclusão. A proteção não é simétrica nas escritas: update($id), updateWhere() e deleteWhere() não acrescentam automaticamente deleted_at IS NULL. Quando aplicável, filtre explicitamente antes da escrita, por exemplo $model->whereNull('deleted_at')->where('id', $id)->updateWhere($data). forceDelete($id) é a remoção física explícita.

Chave primária e ordenação padrão

O Model base declara protected string $primaryKey = 'id'; não infere automaticamente nomes alternativos. Em uma subclasse com outra chave, configure a propriedade protegida e revise os métodos cuja assinatura recebe int $id:

final class LegacyUser extends \Framework\Database\Model
{
    protected string $table = 'legacy_users';
    protected string $primaryKey = 'user_id';
    protected array $fillable = ['name', 'email'];
}

find(int $id), update(int $id, array $data), delete(int $id) e forceDelete(int $id) usam essa propriedade, mas continuam exigindo chave numérica por assinatura. all(string $orderBy = 'id', string $direction = 'ASC'): array ordena por id ASC por padrão; passe uma coluna existente. Direção diferente de DESC é normalizada para ASC.

Model: referência método a método

O exemplo concreto usa App\Models\User, declarado no projeto, salvo quando a descrição diz que o método é um hook protegido. Os métodos fluentes só montam a consulta; terminais como get/first/paginate a executam. Os exemplos supõem o schema de users existente e não substituem validação/autorização.

PropriedadeAssinatura/padrãoComportamento
$dbpublic DatabaseInstância compartilhada de Database obtida pelo construtor.
$tableprotected string = ''Subclasse deve indicar a tabela.
$primaryKeyprotected string = 'id'Chave padrão usada em find/update/delete; assinatura de operações por ID continua int.
$fillableprotected array = []Whitelist; vazio significa aceitar todas as chaves.
$hiddenprotected array = []Metadado usado por camadas como Auth/Resource; não filtra genericamente todo json_encode().
$timestampsprotected bool = trueControla created_at/updated_at em operações de escrita.
$softDeleteprotected bool = falseAtiva exclusão lógica baseada em deleted_at em leituras e delete.
Índice: 41 métodos públicos e 5 hooks protegidos

__construct()

Assinatura: public function __construct(): void

Parâmetros: Nenhum.

Retorno/comportamento: Conecta $db ao singleton Database::getInstance().

$users = new \App\Models\User();

Observações: O Model concreto define tabela e propriedades protegidas; a conexão é compartilhada pelo Database.

all()

Assinatura: public function all(string $orderBy = 'id', string $direction = 'ASC'): array

Parâmetros: $orderBy: identificador de coluna; padrão id. $direction: direção; só DESC é aceito como descendente, qualquer outro valor vira ASC.

Retorno/comportamento: Array de objetos com todas as linhas ordenadas.

$users = (new \App\Models\User())->all('name', 'DESC');

Observações: Usa builder/ORDER BY e o terminal get(); identificador passa pela validação do Model.

find()

Assinatura: public function find(int $id): object|false

Parâmetros: $id: chave primária numérica.

Retorno/comportamento: Objeto do registro ou false; aplica filtro deleted_at IS NULL se soft delete ativo.

$user = (new \App\Models\User())->find(42);

Observações: Não lança 404 nem exceção de ausência; trate false. Usa bind para o ID.

findOrFail()

Assinatura: public function findOrFail(int $id): object

Parâmetros: $id: chave primária numérica.

Retorno/comportamento: Registro ou RuntimeException quando find() retorna false.

try { $user = $users->findOrFail(42); } catch (\RuntimeException $e) { /* não encontrado */ }

Observações: A exceção não vira resposta HTTP 404 automaticamente; converta no Controller.

findBy()

Assinatura: public function findBy(string $column, mixed $value): object|false

Parâmetros: $column: identificador simples permitido. $value: valor bindado.

Retorno/comportamento: Primeiro objeto correspondente ou false.

$user = (new \App\Models\User())->findBy('email', 'ana@example.com');

Observações: Coluna é sanitizada e valor usa bind; filtro soft delete é aplicado.

findMany()

Assinatura: public function findMany(array $ids): array

Parâmetros: $ids: valores das chaves primárias.

Retorno/comportamento: Array de objetos; IDs vazios devolvem [].

$users = (new \App\Models\User())->findMany([2, 7, 9]);

Observações: Usa whereIn; com lista vazia retorna antes do terminal e não limpa um builder preexistente.

where()

Assinatura: public function where(string $column, mixed $value, string $op = '='): static

Parâmetros: $column: identificador; $value: bind; $op: operador da whitelist, padrão =.

Retorno/comportamento: Mesma instância para encadear.

$users = (new \App\Models\User())->where('role', 'member')->get();

Observações: Coluna e operador são validados; valores não são concatenados no SQL. Construa consultas novas por instância.

orWhere()

Assinatura: public function orWhere(string $column, mixed $value, string $op = '='): static

Parâmetros: Mesmos parâmetros de where().

Retorno/comportamento: Mesma instância; acrescenta condição OR.

$users = (new \App\Models\User())->where('role', 'admin')->orWhere('role', 'member')->get();

Observações: O operador é validado; a expressão de usuário fica parentizada antes do filtro de soft delete.

whereIn()

Assinatura: public function whereIn(string $column, array $values, string $boolean = 'and'): static

Parâmetros: $column coluna; $values lista bindada; $boolean and/or esperado, padrão and.

Retorno/comportamento: Mesma instância.

$users = (new \App\Models\User())->whereIn('role', ['admin', 'member'])->get();

Observações: Array vazio gera condição sempre falsa 1 = 0; nomes de coluna sanitizados e valores bindados. Use literal and/or.

whereNotIn()

Assinatura: public function whereNotIn(string $column, array $values, string $boolean = 'and'): static

Parâmetros: $column, lista $values e booleano lógico.

Retorno/comportamento: Mesma instância.

$users = (new \App\Models\User())->whereNotIn('role', ['blocked'])->get();

Observações: Lista vazia não adiciona filtro nenhum; evite interpretá-la como “nenhum registro”.

whereNull()

Assinatura: public function whereNull(string $column, string $boolean = 'and'): static

Parâmetros: Coluna e conector lógico.

Retorno/comportamento: Mesma instância, adiciona IS NULL.

$users = (new \App\Models\User())->whereNull('avatar')->get();

Observações: Coluna sanitizada; boolean não é validado, portanto use somente literal controlado and/or.

whereNotNull()

Assinatura: public function whereNotNull(string $column, string $boolean = 'and'): static

Parâmetros: Coluna e conector lógico.

Retorno/comportamento: Mesma instância, adiciona IS NOT NULL.

$users = (new \App\Models\User())->whereNotNull('avatar')->get();

Observações: Coluna sanitizada; use conector fixo.

whereBetween()

Assinatura: public function whereBetween(string $column, mixed $min, mixed $max, string $boolean = 'and'): static

Parâmetros: Coluna, limite inferior e superior, conector lógico.

Retorno/comportamento: Mesma instância; SQL BETWEEN inclui as pontas.

$users = (new \App\Models\User())->whereBetween('id', 10, 20)->get();

Observações: Valores são bindados; coluna sanitizada. Use tipos compatíveis com a coluna.

whereLike()

Assinatura: public function whereLike(string $column, string $value, string $boolean = 'and'): static

Parâmetros: Coluna, fragmento textual e conector lógico.

Retorno/comportamento: Mesma instância; procura %value%.

$users = (new \App\Models\User())->whereLike('email', '@example.com')->get();

Observações: O método envolve valor com % nos dois lados e usa binding; não permite customizar wildcard no início/fim por esta assinatura.

whereRaw()

Assinatura: public function whereRaw(string $sql, array $bindings = [], string $boolean = 'and'): static

Parâmetros: $sql: fragmento SQL controlado pelo código. $bindings: mapa de placeholder para valor, com ou sem :. $boolean: conector.

Retorno/comportamento: Mesma instância; valores são adicionados aos bindings.

$users = (new \App\Models\User())->whereRaw('email = :email', ['email' => 'ana@example.com'])->first();

Observações: Não sanitiza SQL nem $boolean; placeholders protegem apenas valores. Nunca concatene entrada externa, coluna ou operador.

whereGroup()

Assinatura: public function whereGroup(callable $callback, string $boolean = 'and'): static

Parâmetros: Callback recebe um builder static; $boolean controla como o grupo é unido.

Retorno/comportamento: Mesma instância; grupo não vazio é agrupado entre parênteses.

$users = (new \App\Models\User())->where('active', 1)->whereGroup(function ($q) { $q->where('role', 'admin')->orWhere('role', 'member'); })->get();

Observações: Grupo vazio não adiciona filtro. Use para controlar precedência de AND/OR.

join()

Assinatura: public function join(string $table, string $first, string $operator, string $second, string $type = 'INNER'): static

Parâmetros: Tabela e duas colunas, operador, tipo opcional (INNER padrão).

Retorno/comportamento: Mesma instância; adiciona JOIN.

$users = (new \App\Models\User())->join('api_tokens', 'users.id', '=', 'api_tokens.user_id', 'LEFT')->select('users.id', 'users.email')->get();

Observações: Tipo só INNER/LEFT/RIGHT; colunas/tabela/operador passam por whitelist. Sem inferência de relações ou eager loading.

leftJoin()

Assinatura: public function leftJoin(string $table, string $first, string $operator, string $second): static

Parâmetros: Tabela, coluna esquerda, operador e coluna direita.

Retorno/comportamento: Mesma instância; chama join() com tipo LEFT.

$users = (new \App\Models\User())->leftJoin('api_tokens', 'users.id', '=', 'api_tokens.user_id')->get();

Observações: Join manual; não cria relação ORM.

groupBy()

Assinatura: public function groupBy(string ...$columns): static

Parâmetros: Um ou mais nomes de coluna.

Retorno/comportamento: Mesma instância; substitui a lista atual de GROUP BY.

$rows = (new \App\Models\User())->select('role')->groupBy('role')->get();

Observações: Colunas sanitizadas; selecione agregados/colunas compatíveis com o modo SQL do banco.

having()

Assinatura: public function having(string $condition): static

Parâmetros: $condition: trecho SQL após GROUP BY.

Retorno/comportamento: Mesma instância.

$rows = (new \App\Models\User())->select('role')->groupBy('role')->having('COUNT(*) >= 2')->get();

Observações: O fragmento não é sanitizado nem recebe binding; use somente SQL fixo/controlado pelo código.

orderBy()

Assinatura: public function orderBy(string $column, string $dir = 'ASC'): static

Parâmetros: Coluna; direção opcional.

Retorno/comportamento: Mesma instância; define ORDER BY.

$users = (new \App\Models\User())->orderBy('name', 'DESC')->get();

Observações: Coluna sanitizada; exatamente DESC vira DESC, qualquer outra direção vira ASC.

limit()

Assinatura: public function limit(int $n): static

Parâmetros: $n: limite inteiro.

Retorno/comportamento: Mesma instância.

$users = (new \App\Models\User())->limit(20)->get();

Observações: Não impõe mínimo/máximo; valide valores externos e evite negativo.

offset()

Assinatura: public function offset(int $n): static

Parâmetros: $n: deslocamento inteiro.

Retorno/comportamento: Mesma instância.

$users = (new \App\Models\User())->limit(20)->offset(40)->get();

Observações: Não valida faixa; normalmente combine com limit().

select()

Assinatura: public function select(string ...$cols): static

Parâmetros: Lista variável de identificadores de coluna.

Retorno/comportamento: Mesma instância; substitui colunas selecionadas.

$rows = (new \App\Models\User())->select('id', 'email')->get();

Observações: Sanitiza identificadores, mas não oferece aliases/expressões SQL; não passe input de usuário.

get()

Assinatura: public function get(): array

Parâmetros: Nenhum; executa o builder atual.

Retorno/comportamento: Array de objetos; após execução bem-sucedida limpa o estado fluente.

$users = (new \App\Models\User())->where('active', 1)->get();

Observações: Se a execução lançar exceção, a limpeza posterior não é alcançada. Use uma nova instância para consulta independente.

first()

Assinatura: public function first(): object|false

Parâmetros: Nenhum; executa com LIMIT 1.

Retorno/comportamento: Primeiro objeto ou false; limpa builder após execução bem-sucedida.

$user = (new \App\Models\User())->where('email', 'ana@example.com')->first();

Observações: Ausência não lança exceção; soft delete é aplicado no SQL do builder.

firstOrFail()

Assinatura: public function firstOrFail(): object

Parâmetros: Nenhum.

Retorno/comportamento: Objeto ou RuntimeException se first() devolver false.

$user = $users->where('email', $email)->firstOrFail();

Observações: A exceção não é HTTP 404 automaticamente.

pluck()

Assinatura: public function pluck(string $column): array

Parâmetros: Coluna a extrair.

Retorno/comportamento: Array simples dos valores dessa propriedade em todas as linhas encontradas.

$emails = (new \App\Models\User())->where('active', 1)->pluck('email');

Observações: Coluna sanitizada; usa get(), portanto executa e limpa builder.

value()

Assinatura: public function value(string $column): mixed

Parâmetros: Coluna a extrair do primeiro resultado.

Retorno/comportamento: Valor da primeira linha ou null se não houver linha/valor.

$email = (new \App\Models\User())->where('id', 42)->value('email');

Observações: Usa first() e limpa builder; mixed mantém o tipo retornado pelo driver.

firstOrCreate()

Assinatura: public function firstOrCreate(array $attributes, array $values = []): object

Parâmetros: $attributes identificam busca; $values complementam criação.

Retorno/comportamento: Registro encontrado ou criado.

$user = (new \App\Models\User())->firstOrCreate(['email' => $email], ['name' => $name, 'password' => password_hash($password, PASSWORD_DEFAULT)]);

Observações: Mescla attributes e values (values prevalecem em chave repetida), não é atômico e precisa de constraint UNIQUE para concorrência. Se create falhar, o retorno object pode causar TypeError ao buscar ID inválido.

updateOrCreate()

Assinatura: public function updateOrCreate(array $attributes, array $values = []): object

Parâmetros: Atributos de busca e valores a gravar.

Retorno/comportamento: Atualiza registro encontrado ou cria; devolve registro consultado novamente.

$user = (new \App\Models\User())->updateOrCreate(['email' => $email], ['name' => $name]);

Observações: Não é operação atômica. Atualização segue fillable/timestamp; criação mescla attributes+values. Falha de create pode quebrar o retorno object.

count()

Assinatura: public function count(): int

Parâmetros: Nenhum; usa filtros/join acumulados.

Retorno/comportamento: Número inteiro de linhas contadas.

$total = (new \App\Models\User())->where('role', 'member')->count();

Observações: Preserva o estado do builder para uso por paginate(). Inclui JOINs, não aplica groupBy/having/limit/orderBy no COUNT; joins podem multiplicar linhas.

exists()

Assinatura: public function exists(): bool

Parâmetros: Nenhum; usa filtros atuais.

Retorno/comportamento: true quando count()>0.

$hasAdmins = (new \App\Models\User())->where('role', 'admin')->exists();

Observações: count() preserva estado; não use para substituir constraint contra concorrência.

create()

Assinatura: public function create(array $data): string|false

Parâmetros: Mapa coluna => valor.

Retorno/comportamento: ID inserido como string ou false quando fillable filtra todas as chaves.

$id = (new \App\Models\User())->create(['name' => $name, 'email' => $email, 'password' => password_hash($password, PASSWORD_DEFAULT)]);

Observações: Aplica fillable e timestamps. Chaves vazias significam aceitar tudo; chaves de coluna entram no SQL, então whitelist explícita é essencial. Não valida domínio nem cria transação.

insertMany()

Assinatura: public function insertMany(array $rows): int

Parâmetros: Lista de mapas; use as mesmas colunas em cada linha.

Retorno/comportamento: Quantidade de linhas enviada ao INSERT; array vazio retorna 0.

$n = (new \App\Models\User())->insertMany([['name' => 'Ana', 'email' => 'ana@example.com', 'password' => password_hash('secret', PASSWORD_DEFAULT)], ['name' => 'Bia', 'email' => 'bia@example.com', 'password' => password_hash('secret', PASSWORD_DEFAULT)]]);

Observações: Colunas vêm da primeira linha após fillable; faltas em outras linhas viram null e extras não são incluídas. Adiciona timestamps; erro SQL é lançado pelo driver.

update()

Assinatura: public function update(int $id, array $data): bool

Parâmetros: ID da chave primária e dados para atualizar.

Retorno/comportamento: Booleano de execute(), não número de linhas afetadas.

$ok = (new \App\Models\User())->update(42, ['name' => 'Ana Silva']);

Observações: Filtra fillable e atualiza updated_at. Não aplica filtro soft-delete por si só; false se dados vazios, e true não garante que ID existia.

updateWhere()

Assinatura: public function updateWhere(array $data): bool

Parâmetros: Dados para atualizar; exige where() previamente acumulado.

Retorno/comportamento: Booleano do execute().

$ok = (new \App\Models\User())->where('role', 'member')->updateWhere(['active' => 0]);

Observações: Sem WHERE lança RuntimeException. Usa apenas condições do builder (não acrescenta automaticamente soft-delete filter); limpe/filtro explicitamente. Estado só é resetado depois de executar.

delete()

Assinatura: public function delete(int $id): bool

Parâmetros: ID da chave primária.

Retorno/comportamento: Booleano do execute(), não quantidade afetada.

$ok = (new \App\Models\User())->delete(42);

Observações: Com softDelete=true atualiza deleted_at; caso contrário DELETE físico. Não verifica se houve linha correspondente.

deleteWhere()

Assinatura: public function deleteWhere(): bool

Parâmetros: Nenhum argumento; exige WHERE prévio.

Retorno/comportamento: Booleano do execute().

$ok = (new \App\Models\User())->where('active', 0)->deleteWhere();

Observações: Sem WHERE lança RuntimeException. Soft delete atualiza deleted_at, mas não acrescenta filtro de registros ainda ativos; reset após executar.

forceDelete()

Assinatura: public function forceDelete(int $id): bool

Parâmetros: ID numérico.

Retorno/comportamento: Booleano de execute().

$ok = (new \App\Models\User())->forceDelete(42);

Observações: DELETE físico mesmo com softDelete ativo; irreversível sem backup e não verifica quantidade afetada.

paginate()

Assinatura: public function paginate(int $perPage = 15, int $page = 1): array

Parâmetros: Tamanho da página e página pedida; defaults 15 e 1.

Retorno/comportamento: Array com data, total, page, per_page, last_page, from, to.

$page = (new \App\Models\User())->where('active', 1)->paginate(15, 2);

Observações: perPage é limitado no mínimo a 1; page fica entre 1 e max(1,last_page). Com zero resultados, last_page=0/page=1/from=1/to=0. count() preserva filtros; count não considera groupBy/having.

Hooks protegidos de extensão

Estes métodos são visíveis a subclasses, não chamadas públicas de aplicação. A alteração deles pode afetar o SQL gerado e a segurança; os exemplos mostram wrapper explícito de subclasse.

buildSelectSql()

Assinatura: protected function buildSelectSql(): string

Parâmetros: Sem parâmetros.

Retorno/comportamento: Monta SELECT com colunas, joins, where, group/having, order, limit/offset; não executa nem faz bind.

class InspectableUser extends \App\Models\User { public function sql(): string { return $this->buildSelectSql(); } }

Observações: Hook protegido para subclasses; não é contrato público de consulta.

filterFillable()

Assinatura: protected function filterFillable(array $data): array

Parâmetros: Mapa de dados.

Retorno/comportamento: Devolve original se fillable vazio; caso contrário intersecta as chaves permitidas.

class SafeUser extends \App\Models\User { public function allowed(array $data): array { return $this->filterFillable($data); } }

Observações: Whitelist de atribuição, não validação de valores; empty fillable aceita tudo.

resetBuilder()

Assinatura: protected function resetBuilder(): void

Parâmetros: Nenhum.

Retorno/comportamento: Limpa where/bindings/joins/group/having/order/limit/offset/selects.

class ReusableUser extends \App\Models\User { public function clearQuery(): void { $this->resetBuilder(); } }

Observações: Uso interno após terminais; chame apenas se implementar comportamento derivado.

sanitizeIdentifier()

Assinatura: protected function sanitizeIdentifier(string $identifier): string

Parâmetros: Identificador SQL.

Retorno/comportamento: Devolve identificador permitido ou lança InvalidArgumentException.

class CheckedUser extends \App\Models\User { public function column(string $name): string { return $this->sanitizeIdentifier($name); } }

Observações: Whitelist léxica para letras/números/underscore/ponto; não aceita aliases ou expressões.

sanitizeOperator()

Assinatura: protected function sanitizeOperator(string $op): string

Parâmetros: Operador de comparação.

Retorno/comportamento: Normaliza uppercase ou lança InvalidArgumentException se fora da whitelist.

class CheckedUser extends \App\Models\User { public function operator(string $op): string { return $this->sanitizeOperator($op); } }

Observações: Whitelist: =, !=, <>, <, >, <=, >=, LIKE, NOT LIKE, IN, NOT IN.