Explorador de banco de dados
O Explorer oferece uma maneira intuitiva e eficiente de trabalhar com seu banco de dados. Ele lida automaticamente com as relações entre as tabelas, cria consultas otimizadas e permite que você se concentre na lógica do aplicativo. Não é necessária nenhuma configuração. Para obter controle total, você pode mudar para o modo SQL.
- Trabalhar com dados é natural e fácil de entender
- Gera consultas SQL otimizadas que buscam apenas os dados necessários
- Oferece acesso fácil a dados relacionados sem a necessidade de escrever consultas JOIN
- Funciona imediatamente sem nenhuma configuração ou geração de entidades
O trabalho com o Explorer começa com a chamada do método table()
no objeto Nette\Database\Explorer (consulte Conexão e configuração para obter detalhes sobre a configuração da
conexão com o banco de dados):
O método retorna um objeto Selection,
que representa uma consulta SQL. Métodos adicionais podem ser encadeados a esse objeto para filtragem e classificação de
resultados. A consulta é montada e executada somente quando os dados são solicitados, por exemplo, por meio da iteração com
foreach
. Cada linha é representada por um objeto ActiveRow:
O Explorer simplifica muito o trabalho com relacionamentos de tabela. O exemplo a seguir mostra a facilidade com que podemos gerar dados de tabelas relacionadas (livros e seus autores). Observe que não é necessário escrever consultas JOIN; o Nette as gera para nós:
O Nette Database Explorer otimiza as consultas para obter o máximo de eficiência. O exemplo acima executa apenas duas consultas SELECT, independentemente do fato de processarmos 10 ou 10.000 livros.
Além disso, o Explorer rastreia quais colunas são usadas no código e busca apenas essas colunas no banco de dados, economizando ainda mais desempenho. Esse comportamento é totalmente automático e adaptável. Se você modificar o código posteriormente para usar colunas adicionais, o Explorer ajustará automaticamente as consultas. Você não precisa configurar nada nem pensar em quais colunas serão necessárias – deixe isso para a Nette.
Filtragem e classificação
A classe Selection
fornece métodos para filtragem e classificação de dados.
where($condition, ...$params) |
Adiciona uma condição WHERE. Várias condições são combinadas com AND |
whereOr(array $conditions) |
Adiciona um grupo de condições WHERE combinadas por meio de OR |
wherePrimary($value) |
Adiciona uma condição WHERE com base na chave primária |
order($columns, ...$params) |
Define a classificação com ORDER BY |
select($columns, ...$params) |
Especifica quais colunas devem ser buscadas |
limit($limit, $offset = null) |
Limita o número de linhas (LIMIT) e, opcionalmente, define OFFSET |
page($page, $itemsPerPage, &$total = null) |
Define a paginação |
group($columns, ...$params) |
Agrupa as linhas (GROUP BY) |
having($condition, ...$params) |
Adiciona uma condição HAVING para filtrar as linhas agrupadas |
Os métodos podem ser encadeados (a chamada interface fluente):
$table->where(...)->order(...)->limit(...)
.
Esses métodos também permitem o uso de notações especiais para acessar dados de tabelas relacionadas.
Escapes e identificadores
Os métodos escapam automaticamente dos parâmetros e dos identificadores de citação (nomes de tabelas e colunas), evitando a injeção de SQL. Para garantir a operação adequada, algumas regras devem ser seguidas:
- Escreva palavras-chave, nomes de funções, procedimentos, etc., em maiúsculas.
- Escrever nomes de colunas e tabelas em minúsculas.
- Sempre passe strings usando parâmetros.
where(string|array $condition, …$parameters): static
Filtra os resultados usando as condições WHERE. Sua força reside no tratamento inteligente de vários tipos de valores e na seleção automática de operadores SQL.
Uso básico:
Graças à detecção automática de operadores adequados, você não precisa lidar com casos especiais – a Nette lida com eles para você:
O método também lida corretamente com condições negativas e matrizes vazias:
Você também pode passar o resultado de outra consulta de tabela como parâmetro, criando uma subconsulta:
As condições também podem ser passadas como uma matriz, com os itens combinados usando AND:
Na matriz, podem ser usados pares de valores-chave, e o Nette novamente escolherá automaticamente os operadores corretos:
Também podemos misturar expressões SQL com placeholders e vários parâmetros. Isso é útil para condições complexas com operadores definidos com precisão:
Várias chamadas para where()
combinam automaticamente as condições usando AND.
whereOr(array $parameters): static
Semelhante ao where()
, mas combina condições usando OR:
Expressões mais complexas também podem ser usadas:
wherePrimary(mixed $key): static
Adiciona uma condição para a chave primária da tabela:
Se a tabela tiver uma chave primária composta (por exemplo, foo_id
, bar_id
), nós a passaremos como
uma matriz:
order(string $columns, …$parameters): static
Especifica a ordem em que as linhas são retornadas. Você pode classificar por uma ou mais colunas, em ordem crescente ou decrescente, ou por uma expressão personalizada:
select(string $columns, …$parameters): static
Especifica as colunas a serem retornadas do banco de dados. Por padrão, o Nette Database Explorer retorna apenas as colunas
que são realmente usadas no código. Use o método select()
quando precisar recuperar expressões específicas:
Os aliases definidos com o uso de AS
podem ser acessados como propriedades do objeto ActiveRow
:
limit(?int $limit, ?int $offset = null): static
Limita o número de linhas retornadas (LIMIT) e, opcionalmente, define um deslocamento:
Para paginação, é mais apropriado usar o método page()
.
page(int $page, int $itemsPerPage, &$numOfPages = null): static
Simplifica a paginação dos resultados. Ele aceita o número da página (a partir de 1) e o número de itens por página. Opcionalmente, você pode passar uma referência a uma variável na qual o número total de páginas será armazenado:
group(string $columns, …$parameters): static
Agrupa as linhas pelas colunas especificadas (GROUP BY). Normalmente, é usado em combinação com funções de agregação:
having(string $having, …$parameters): static
Define uma condição para filtragem de linhas agrupadas (HAVING). Pode ser usado em combinação com o método
group()
e as funções de agregação:
Leitura de dados
Para ler dados do banco de dados, há vários métodos úteis disponíveis:
foreach ($table as $key => $row) |
Itera por todas as linhas, $key é o valor da chave primária, $row é um objeto ActiveRow |
$row = $table->get($key) |
Retorna uma única linha por chave primária |
$row = $table->fetch() |
Retorna a linha atual e avança o ponteiro para a próxima |
$array = $table->fetchPairs() |
Cria uma matriz associativa a partir dos resultados. |
$array = $table->fetchAll() |
Retorna todas as linhas como um array |
count($table) |
Retorna o número de linhas no objeto Selection |
O objeto ActiveRow é somente leitura. Isso significa que você não pode alterar os valores de suas propriedades. Essa restrição garante a consistência dos dados e evita efeitos colaterais inesperados. Os dados são obtidos do banco de dados e todas as alterações devem ser feitas explicitamente e de forma controlada.
foreach
– Iteração em todas as linhas
A maneira mais fácil de executar uma consulta e recuperar linhas é por meio da iteração com o loop foreach
.
Ele executa automaticamente a consulta SQL.
get($key): ?ActiveRow
Executa uma consulta SQL e retorna uma linha por sua chave primária ou null
se ela não existir.
fetch(): ?ActiveRow
Retorna uma linha e avança o ponteiro interno para a próxima. Se não houver mais linhas, ele retorna null
.
fetchPairs(string|int|null $key = null, string|int|null $value = null): array
Retorna os resultados como uma matriz associativa. O primeiro argumento especifica o nome da coluna a ser usada como a chave da matriz e o segundo argumento especifica o nome da coluna a ser usada como o valor:
Se apenas o primeiro parâmetro for fornecido, a linha inteira será usada como o valor, representado como um objeto
ActiveRow
:
No caso de chaves duplicadas, é usado o valor da última linha. Ao usar null
como chave, a matriz será indexada
numericamente a partir de zero (nesse caso, não ocorrem colisões):
fetchPairs(Closure $callback): array
Como alternativa, você pode passar um retorno de chamada como parâmetro. O retorno de chamada será aplicado a cada linha para retornar um único valor ou um par de valores-chave.
fetchAll(): array
Retorna todas as linhas como uma matriz associativa de objetos ActiveRow
, em que as chaves são os valores da
chave primária.
count(): int
O método count()
sem parâmetros retorna o número de linhas no objeto Selection
:
Observação: count()
com um parâmetro executa a função de agregação COUNT no banco de dados, conforme
descrito abaixo.
ActiveRow::toArray(): array
Converte o objeto ActiveRow
em uma matriz associativa em que as chaves são os nomes das colunas e os valores
são os dados correspondentes.
Agregação
A classe Selection
fornece métodos para executar facilmente funções de agregação (COUNT, SUM, MIN, MAX,
AVG, etc.).
count($expr) |
Conta o número de linhas |
min($expr) |
Retorna o valor mínimo em uma coluna |
max($expr) |
Retorna o valor máximo em uma coluna |
sum($expr) |
Retorna a soma dos valores em uma coluna |
aggregation($function) |
Permite qualquer função de agregação, como AVG() ou GROUP_CONCAT() |
count(string $expr): int
Executa uma consulta SQL com a função COUNT e retorna o resultado. Esse método é usado para determinar quantas linhas correspondem a uma determinada condição:
Observação: count() sem um parâmetro simplesmente retorna o número de linhas no objeto
Selection
.
min(string $expr) and max(string $expr)
Os métodos min()
e max()
retornam os valores mínimo e máximo na coluna ou expressão
especificada:
sum(string $expr): int
Retorna a soma dos valores na coluna ou expressão especificada:
aggregation(string $function, ?string $groupFunction = null): mixed
Permite a execução de qualquer função de agregação.
Se precisarmos agregar resultados que resultem de uma agregação e agrupamento (por exemplo, SUM(value)
sobre
linhas agrupadas), especificaremos a função de agregação a ser aplicada a esses resultados intermediários como o segundo
argumento:
Neste exemplo, primeiro calculamos o preço total dos produtos em cada categoria
(SUM(price * stock) AS category_total
) e agrupamos os resultados por category_id
. Em seguida, usamos
aggregation('SUM(category_total)', 'SUM')
para somar esses subtotais. O segundo argumento 'SUM'
especifica a função de agregação a ser aplicada aos resultados intermediários.
Inserir, atualizar e excluir
O Nette Database Explorer simplifica a inserção, a atualização e a exclusão de dados. Todos os métodos mencionados
lançam um Nette\Database\DriverException
em caso de erro.
Selection::insert(iterable $data): static
Insere novos registros em uma tabela.
Inserção de um único registro:
O novo registro é passado como uma matriz associativa ou objeto iterável (como ArrayHash
usado em formulários), em que as chaves correspondem aos nomes das colunas na tabela.
Se a tabela tiver uma chave primária definida, o método retornará um objeto ActiveRow
, que é recarregado do
banco de dados para refletir quaisquer alterações feitas no nível do banco de dados (por exemplo, acionadores, valores de
coluna padrão ou cálculos de incremento automático). Isso garante a consistência dos dados e o objeto sempre contém os dados
atuais do banco de dados. Se uma chave primária não for explicitamente definida, o método retornará os dados de entrada como
uma matriz.
Inserção de vários registros de uma vez:
O método insert()
permite inserir vários registros com uma única consulta SQL. Nesse caso, ele retorna
o número de linhas inseridas.
Você também pode passar um objeto Selection
com uma seleção de dados como parâmetro.
Inserindo valores especiais:
Os valores podem incluir arquivos, objetos DateTime
ou literais SQL:
Selection::update(iterable $data): int
Atualiza as linhas em uma tabela com base em um filtro especificado. Retorna o número de linhas realmente modificadas.
As colunas a serem atualizadas são passadas como uma matriz associativa ou objeto iterável (como ArrayHash
usado
em formulários), em que as chaves correspondem aos nomes das colunas na tabela:
Para alterar valores numéricos, você pode usar os operadores +=
e -=
:
Selection::delete(): int
Exclui linhas de uma tabela com base em um filtro especificado. Retorna o número de linhas excluídas.
Ao chamar update()
ou delete()
, certifique-se de usar where()
para
especificar as linhas a serem atualizadas ou excluídas. Se where()
não for usado, a operação será executada em
toda a tabela!
ActiveRow::update(iterable $data): bool
Atualiza os dados em uma linha do banco de dados representada pelo objeto ActiveRow
. Ele aceita dados iteráveis
como parâmetro, em que as chaves são nomes de colunas. Para alterar valores numéricos, você pode usar os operadores
+=
e -=
:
Após a atualização, o ActiveRow
é automaticamente recarregado do banco de dados para refletir quaisquer
alterações feitas no nível do banco de dados (por exemplo, acionadores). O método retorna true
somente se tiver
ocorrido uma alteração real nos dados.
Esse método atualiza apenas uma linha específica no banco de dados. Para atualizações em massa de várias linhas, use o método Selection::update().
ActiveRow::delete()
Exclui uma linha do banco de dados que é representada pelo objeto ActiveRow
.
Esse método exclui apenas uma linha específica no banco de dados. Para a exclusão em massa de várias linhas, use o método Selection::delete().
Relacionamentos entre tabelas
Nos bancos de dados relacionais, os dados são divididos em várias tabelas e conectados por meio de chaves estrangeiras. O Nette Database Explorer oferece uma maneira revolucionária de trabalhar com esses relacionamentos – sem escrever consultas JOIN ou exigir qualquer configuração ou geração de entidades.
Para demonstração, usaremos o banco de dados de exemplo(disponível no GitHub). O banco de dados inclui as seguintes tabelas:
author
– autores e tradutores (colunasid
,name
,web
,born
)book
– livros (colunasid
,author_id
,translator_id
,title
,sequel_id
)tag
– tags (colunasid
,name
)book_tag
– tabela de links entre livros e tags (colunasbook_id
,tag_id
)

Estrutura do banco de dados
Nesse exemplo de banco de dados de livros, encontramos vários tipos de relacionamentos (simplificados em comparação com a realidade):
- Um-para-muitos (1:N) – Cada livro tem um autor; um autor pode escrever múltiplos livros.
- Zero-para-muitos (0:N)** – Um livro pode ter um tradutor; um tradutor pode traduzir múltiplos livros.
- Zero para um (0:1)** – Um livro pode ter uma sequência.
- Muitos para muitos (M:N)** – Um livro pode ter várias tags, e uma tag pode ser atribuída a vários livros.
Nesses relacionamentos, há sempre uma tabela pai e uma tabela filha. Por exemplo, no relacionamento entre
autores e livros, a tabela author
é a tabela pai e a tabela book
é a tabela filha – você pode
pensar nisso como um livro sempre „pertencente“ a um autor. Isso também se reflete na estrutura do banco de dados: a tabela
filha book
contém a chave estrangeira author_id
, que faz referência à tabela pai
author
.
Se quisermos exibir os livros junto com os nomes de seus autores, temos duas opções. Ou recuperamos os dados usando uma única consulta SQL com um JOIN:
Ou recuperamos os dados em duas etapas – primeiro os livros, depois seus autores – e os reunimos em PHP:
A segunda abordagem é, surpreendentemente, mais eficiente. Os dados são obtidos apenas uma vez e podem ser mais bem utilizados no cache. É exatamente assim que o Nette Database Explorer funciona – ele cuida de tudo nos bastidores e fornece uma API limpa:
Acesso à tabela pai
O acesso à tabela pai é simples. Esses são relacionamentos como um livro tem um autor ou um livro pode ter um
tradutor. O registro relacionado pode ser acessado por meio da propriedade de objeto ActiveRow
– o nome da
propriedade corresponde ao nome da coluna da chave estrangeira sem o sufixo id
:
Ao acessar a propriedade $book->author
, o Explorer procura uma coluna na tabela book
que contenha
a string author
(ou seja, author_id
). Com base no valor dessa coluna, ele recupera o registro
correspondente da tabela author
e o retorna como um objeto ActiveRow
. Da mesma forma,
$book->translator
usa a coluna translator_id
. Como a coluna translator_id
pode conter
null
, o operador ?->
é usado.
Uma abordagem alternativa é fornecida pelo método ref()
, que aceita dois argumentos – o nome da tabela de
destino e a coluna de vinculação – e retorna uma instância ActiveRow
ou null
:
O método ref()
é útil se o acesso baseado em propriedade não puder ser usado, por exemplo, quando a tabela
contém uma coluna com o mesmo nome da propriedade (author
). Em outros casos, recomenda-se usar o acesso baseado em
propriedades para melhorar a legibilidade.
O Explorer otimiza automaticamente as consultas ao banco de dados. Ao iterar pelos livros e acessar seus registros relacionados (autores, tradutores), o Explorer não gera uma consulta para cada livro individualmente. Em vez disso, ele executa apenas uma consulta SELECT para cada tipo de relacionamento, reduzindo significativamente a carga do banco de dados. Por exemplo:
Este código executará apenas três consultas otimizadas ao banco de dados:
A lógica para identificar a coluna de vinculação é definida pela implementação de Conventions. Recomendamos o uso do DiscoveredConventions, que analisa chaves estrangeiras e permite que você trabalhe sem problemas com os relacionamentos de tabela existentes.
Acesso à tabela filha
O acesso à tabela secundária funciona na direção oposta. Agora perguntamos quais livros este autor escreveu ou
quais livros este tradutor traduziu. Para esse tipo de consulta, usamos o método related()
, que retorna um
objeto Selection
com registros relacionados. Aqui está um exemplo:
O método related()
aceita a descrição da relação como um único argumento usando a notação de ponto ou
como dois argumentos separados:
O Explorer pode detectar automaticamente a coluna de vinculação correta com base no nome da tabela pai. Nesse caso, ele faz
a vinculação por meio da coluna book.author_id
porque o nome da tabela de origem é author
:
Se houver várias conexões possíveis, o Explorer lançará uma exceção AmbiguousReferenceKeyException.
É claro que também podemos usar o método related()
ao iterar por vários registros em um loop, e o Explorer
também otimizará automaticamente as consultas nesse caso:
Esse código gera apenas duas consultas SQL eficientes:
Relacionamento de muitos para muitos
Para um relacionamento de muitos para muitos (M:N), é necessária uma tabela de junção (no nosso caso,
book_tag
). Essa tabela contém duas colunas de chave estrangeira (book_id
, tag_id
). Cada
coluna faz referência à chave primária de uma das tabelas conectadas. Para recuperar os dados relacionados, primeiro buscamos
os registros da tabela de ligação usando related('book_tag')
e, em seguida, prosseguimos para os dados de
destino:
O Explorer otimiza novamente as consultas SQL em um formato eficiente:
Consultas em tabelas relacionadas
Nos métodos where()
, select()
, order()
e group()
, é possível usar
notações especiais para acessar colunas de outras tabelas. O Explorer cria automaticamente os JOINs necessários.
A notação Dot (parent_table.column
) é usada para relacionamentos 1:N, conforme visto da perspectiva da
tabela pai:
A notação de cólon é usada para relacionamentos 1:N da perspectiva da tabela pai:
No exemplo acima com a notação de dois pontos (:book.title
), a coluna de chave estrangeira não é especificada
explicitamente. O Explorer detecta automaticamente a coluna correta com base no nome da tabela pai. Nesse caso, ele se une por
meio da coluna book.author_id
porque o nome da tabela de origem é author
. Se houver várias conexões
possíveis, o Explorer lançará a exceção AmbiguousReferenceKeyException.
A coluna de vinculação pode ser especificada explicitamente entre parênteses:
As notações podem ser encadeadas para acessar dados em várias tabelas:
Extensão das condições para JOIN
O método joinWhere()
acrescenta condições adicionais às junções de tabelas no SQL após a palavra-chave
ON
.
Por exemplo, digamos que queiramos encontrar livros traduzidos por um tradutor específico:
Na condição joinWhere()
, você pode usar as mesmas construções do método where()
–
operadores, espaços reservados, matrizes de valores ou expressões SQL.
Para consultas mais complexas com vários JOINs, podem ser definidos aliases de tabela:
Observe que, enquanto o método where()
adiciona condições à cláusula WHERE
, o método
joinWhere()
estende as condições da cláusula ON
durante as uniões de tabelas.