Download the PHP package diogodg/neoorm without Composer

On this page you can find all versions of the php package diogodg/neoorm. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package neoorm

NeoORM

Data mapper para MySQL e PostgreSQL, com consulta tipada e migrações versionadas em arquivo.

$maiores[0]->created_at é um DateTimeImmutable, ->active é um bool e ->balance é uma string numérica, nos dois bancos. A IDE completa os nomes das colunas, e o PHPStan reprova $u->emial antes de você rodar.

Instalação

.env na raiz do projeto:

Os caminhos são relativos à raiz do projeto — o diretório com o composer.json —, não ao diretório de onde você chamou o CLI. Um caminho absoluto é usado como está, e uma chave declarada vazia (PATH_MODEL=) vale como não declarada: cai no padrão.

O layout padrão, portanto, é este, e num projeto que o siga o .env não precisa de nenhuma dessas chaves:

O modelo mental

Quatro coisas, e cada uma faz uma:

O quê Quem escreve
Model descreve a tabela você
Seeder popula a tabela você
Generated/ linhas, payloads de insert e referências de coluna, tipados neoorm generate:types
Database monta e executa a consulta você usa

Um model não consulta nada. Ele não tem get(), não tem store(), não guarda linha. Na 1.x tinha: Model estendia Db, que era ao mesmo tempo conexão, construtor de query e linha de resultado — e era por isso que não existia tipo de retorno possível além de array. Ver UPGRADE.md.

Definindo uma tabela

App/Models/State.php:

As colunas são um mapa nome => Col: o nome é a chave, e o tipo é uma fábrica. Escrever o nome duas vezes — uma na chave e outra num construtor — seria a única forma de os dois discordarem, e o tipo como string livre ('VARCAHR') só quebrava no build().

Descrever a tabela é operação de memória: table() não abre conexão e não roda DDL. (Na 1.x rodava — cinco CREATE TABLE IF NOT EXISTS por instanciação de model, uma vez por linha hidratada.)

As colunas

Col::id() é INT chave primária com auto incremento — a primeira linha de quase todo model. Col::bigId() é o mesmo em BIGINT, e Col::fk(Outro::class) é INT NOT NULL já referenciando.

Uma fábrica por tipo: tinyInt() smallInt() mediumInt() int() bigInt() decimal($p, $s) float() double() boolean() char($n) varchar($n) tinyText() text() mediumText() longText() date() time() dateTime() timestamp() timestampTz() year() json() jsonb() uuid() binary($n) varBinary($n) tinyBlob() blob() mediumBlob() longBlob() bytea() enum($valores). Para o que elas não cobrirem, Col::type('INT UNSIGNED') aceita o tipo como texto.

Os modificadores encadeiam: notNull() nullable() primary() unique() autoIncrement() unsigned() index() default($v) defaultRaw($sql) defaultNull() comment($t) collation($c) references(Outro::class).

Col é imutável, então uma coluna serve de molde sem risco de alias:

references() aceita a classe do model, e não só o nome da tabela: Country::class é verificável pela IDE e pelo PHPStan, 'country' não é.

O que fica na tabela

O que não cabe numa coluna só:

Índice unico se declara com unique(), nunca como "índice único": no MySQL os dois são o mesmo objeto de catálogo, então ter duas representações significaria que uma delas divergiria do banco para sempre.

A ordem das chamadas não importa — toda validação acontece no build(), sobre o schema pronto.

Models em subpastas

PATH_MODEL é varrido recursivamente, com subpasta virando segmento de namespace pela regra do PSR-4:

Populando as tabelas

O dado inicial mora em PATH_SEEDS, um arquivo por tabela — não dentro do model. Schema e dado mudam por motivos diferentes e num ritmo diferente.

App/Seeders/StateSeeder.php:

O executor chega por parâmetro, e é o Tx da transação que o comando abriu: se um seeder falhar no meio, nenhum dado parcial fica. Cada seeder deve ser idempotente — o runner não guarda o que já rodou, e a guarda usual é conferir se a tabela já tem linha.

A ordem vem do grafo de foreign keys, não do nome do arquivo: o seeder de state roda depois do de country porque a tabela depende dela.

Uma tabela, um seeder. Duas classes para a mesma tabela, ou um table() que não existe no schema, param o comando com a lista completa — o segundo era silêncio puro antes: o seeder simplesmente nunca rodava.

Consultando a partir do model

O tipo concreto vem do @extends Model<StateTable> da classe. É anotação e não use de propósito: um use de algo em Generated/ faria o model fatalar quando o diretório não existisse — e generate:types precisa ler os models para criá-lo. Sem a anotação ref() continua funcionando, só devolve Query\Table; com a anotação errada, generate:types --check reprova.

Gerando os tipos

Lê PATH_MODEL e escreve em PATH_GENERATED:

Roda sem banco — só lê o diretório de models. É o que o torna viável como hook de pre-commit e como gate de CI.

Commite o diretório. Ele é código-fonte como qualquer outro: entra em code review, e o gate abaixo garante que não envelhece.

O --check também confere o @extends Model<XTable> de cada model. A anotação é o que dá tipo concreto a Model::ref(), e é um comentário: nada em tempo de execução a contradiz, então copiar um model e esquecer de trocar a classe faz a IDE e o PHPStan concordarem com colunas que não existem. Model sem anotação não é erro; com a anotação errada, é.

As propriedades têm o nome idêntico à coluna, em snake_case. Camelizar não é injetivo — o IR já minusculou tudo, então user_id e userId seriam a mesma coluna, e is_active colidiria com isactive.

Consultando

Select

Condições

ilike() é operador no PostgreSQL e vira LOWER(a) LIKE LOWER(b) no MySQL — o dialeto decide, você não.

sql() é a via de escape, e o contrato é explícito: o fragmento vai cru, os valores continuam virando bind.

Paginação, ordenação, agrupamento

NULLS FIRST/LAST só existe no PostgreSQL; no MySQL o compilador emite expr IS NULL DESC antes do termo, que dá o mesmo resultado.

Joins e seleção parcial

Seleção parcial devolve array<string,mixed>, e isso está dito no tipo em vez de escondido: o PHP não tem tipo estrutural, então não há como declarar "array com estas chaves e estes tipos" que o runtime verifique.

As colunas da tabela juntada saem com prefixo {tabela}__{coluna} porque FETCH_ASSOC colapsa homônimas: users.id e posts.id escreveriam a mesma chave, e a última venceria, em silêncio.

Uma tabela apelidada requalifica as próprias colunas, o que é o que faz self-join funcionar:

Insert, update, delete

No UsersInsert, coluna com DEFAULT ou auto incremento é opcional, e deixá-la em null omite a coluna do INSERT para o banco aplicar o próprio padrão.

Em lote, os payloads podem omitir opcionais diferentes: a união das colunas é feita uma vez e cada ausência vira DEFAULT na linha correspondente.

execute() de UPDATE devolve linhas casadas nos dois bancos, inclusive quando o valor novo é igual ao antigo. A conexão MySQL liga MYSQL_ATTR_FOUND_ROWS para manter o mesmo contrato do PostgreSQL.

UPDATE e DELETE sem WHERE são recusados, a menos que você peça:

O deleteByFilter() da 1.x já protegia o DELETE; o UPDATE nunca protegeu, e um where() esquecido reescrevia a tabela inteira sem aviso.

O builder é imutável

count() ignora paginação e ordenação. Com GROUP BY, HAVING ou DISTINCT, conta as linhas da consulta resultante por meio de subconsulta — não o tamanho do primeiro grupo. limit() e offset() recusam valores negativos.

Cada cláusula devolve outra instância. É o que elimina o clean() da 1.x, que existia justamente porque o builder era mutável e reusado — "filtro sobrando da consulta anterior" era a classe de bug resultante.

Transações

Aninham de verdade, por savepoint: a falha interna desfaz até o savepoint e a externa commita. Na 1.x Connection::beginTransaction() era no-op se já houvesse transação e commit() era no-op se não houvesse — uma transação interna não commitava nada, e uma falha interna desfazia o trabalho externo sem avisar.

O estado dos savepoints pertence ao handle PDO: dois objetos Database sobre a mesma conexão compartilham a profundidade. Os métodos transacionais estáticos de Connection não existem na v2.

Tx implementa a mesma interface Executor que Database, então um repositório pode type-hintar Executor e funcionar dos dois jeitos.

Vendo o SQL

Tipos das colunas

Coluna PHP Por quê
inteiros int
FLOAT, DOUBLE float
DECIMAL string (numeric-string) é o que o PDO devolve, e DECIMAL(19,4) de dinheiro excede a precisão de float
BOOLEAN bool mysqlnd devolve int, pdo_pgsql devolve bool — o caster resolve
textuais string CHAR sofre rtrim para os dois dialetos concordarem
DATE, DATETIME, TIMESTAMP DateTimeImmutable fuso normalizado para UTC na leitura
TIME string é duração no MySQL (±838h); DateTimeImmutable corromperia
JSON, JSONB mixed decodificado array seria mentira: '42' e 'null' são JSON válidos
UUID string
binários string bytea chega como stream no pdo_pgsql — o caster drena
ENUM enum PHP gerado

O fuso da sessão é fixado na conexão (SET time_zone = '+00:00' / SET TIME ZONE 'UTC'). DATETIME, TIMESTAMP e TIMESTAMPTZ são convertidos para UTC tanto na escrita quanto na leitura. DATE e TIME preservam o valor de calendário, sem conversão de fuso.

Use isNull($coluna) e isNotNull($coluna) para nulidade. eq($coluna, null) e ne($coluna, null) lançam cedo, porque = NULL/<> NULL nunca são verdadeiros em SQL.

Migrações

Mudança de schema é arquivo versionado no repositório: seus models são comparados com o último snapshot, e a diferença sai como um .sql numerado que dá para ler no code review antes de tocar em ambiente nenhum.

migration:generate não abre conexão. Ele compara dois arquivos JSON e escreve SQL, o que é o que permite gerar e revisar uma migração em CI sem serviço de banco no ar.

O que vai para o repositório

Tudo é código-fonte. O snapshot é a única entrada do differ — fora do repositório, dois desenvolvedores geram cada um um 0002 com conteúdos diferentes e o mesmo número.

Um diretório por dialeto porque um .sql é inerentemente específico, e porque engine e collation só existem no MySQL.

Os comandos

Comando O que toca
generate:types [--check] [--dry-run] arquivos gerados. Sem banco
migration:generate [--name=x] [--empty] [--rename=a:b] [--allow-destructive] [--dry-run] arquivos do repositório. Sem banco
migration:up [--to=tag] [--step=n] [--dry-run] o banco
migration:status lê os dois. Sai 1 se houver inconsistência
db:check [--strict] lê tudo. O gate de CI
db:push [--dry-run] [--allow-destructive] o banco, sem arquivo. Só desenvolvimento
db:pull [--write-to=x] [--only-declared] lê o banco para um snapshot
db:seed [--tables=a,b] o banco, só dados. Roda os seeders de PATH_SEEDS
db:reset [--force] [--seed] [--confirm=nome] derruba e recria. Só desenvolvimento

Dentro do NeoFramework os mesmos nomes valem em php neof.

Não há migração down

De propósito. Um down automático sugere uma reversibilidade que não existe: um DROP COLUMN desfeito recria a coluna vazia, e o dado não volta. Em desenvolvimento se usa db:reset; em produção se escreve uma migração corretiva para frente.

A atomicidade é assimétrica, e isso não é escondido

db:push vs migration:generate

push compara os models com o banco vivo e aplica a diferença direto, sem escrever arquivo. É para o ciclo de desenvolvimento em que você muda um model dez vezes por hora e não quer dez migrações no histórico. Recusa rodar em produção — não porque o SQL seria diferente (há teste afirmando que não é), mas porque não deixa rastro: nada para revisar, e nenhuma forma de reproduzir o estado resultante em outro lugar.

Desenvolvimento


All versions of neoorm with dependencies

PHP Build Version
Package Version
Requires php Version >=8.4
ext-pdo Version *
ext-json Version *
ext-mbstring Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package diogodg/neoorm contains the following files

Loading the files please wait ...