clintonnzedimma / sommy-orm
Sequelize-style lightweight ORM for PHP (Sommy ORM)
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/clintonnzedimma/sommy-orm
Requires
- php: >=8.0
- ext-pdo: *
This package is not auto-updated.
Last update: 2025-11-03 17:04:29 UTC
README
Sommy ORM is a tiny, Sequelize‑style ORM for PHP built on PDO. It focuses on being simple and explicit: you define models with a schema, create tables via a query interface, and do straightforward CRUD without magic.
Works with MySQL, MariaDB, SQLite, and PostgreSQL.
Requirements
- PHP >= 8.0
ext-pdoand the PDO driver for your database (e.g.,pdo_mysql,pdo_sqlite,pdo_pgsql)
Installation
Install via Composer (recommended):
composer require clintonnzedimma/sommy-orm
Or, if you are using this repository directly:
- Install dependencies and dump autoload
composer install composer dump-autoload
- Require Composer autoloader from your app:
require __DIR__ . '/vendor/autoload.php';
Composer autoload is configured for Sommy\ORM\ → src/.
Configuration
Create an array of connection settings and pass it to Sommy\ORM\SommyManager.
SQLite example:
use Sommy\ORM\SommyManager; $sommy = new SommyManager([ 'dialect' => 'sqlite', 'database' => __DIR__ . '/database.sqlite', ]);
MySQL/MariaDB example:
$sommy = new SommyManager([ 'dialect' => 'mysql', // or 'mariadb' 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'testdb', 'username' => 'root', 'password' => '', 'charset' => 'utf8mb4', ]);
Connecting to MySQL
- Make sure the
pdo_mysqlextension is enabled (check withphp -m). - Ensure a MySQL server is running and you have database credentials.
- Create a database and user (example using the MySQL client):
mysql -u root -p
CREATE DATABASE testdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'secret'; GRANT ALL PRIVILEGES ON testdb.* TO 'appuser'@'localhost'; FLUSH PRIVILEGES;
- Configure Sommy to use MySQL (see example above), e.g.:
use Sommy\ORM\SommyManager; $sommy = new SommyManager([ 'dialect' => 'mysql', 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'testdb', 'username' => 'appuser', 'password' => 'secret', 'charset' => 'utf8mb4', ]); // Optional: quick connectivity check if (!$sommy->authenticate()) { throw new RuntimeException('MySQL connection failed'); }
PostgreSQL example:
$sommy = new SommyManager([ 'dialect' => 'pgsql', 'host' => '127.0.0.1', 'port' => 5432, 'database' => 'testdb', 'username' => 'postgres', 'password' => 'secret', ]);
Data Types
Use the factory in Sommy\ORM\DataTypes to declare columns:
- INTEGER:
DataTypes::INTEGER([...options]) - BIGINT, SMALLINT:
DataTypes::BIGINT(),DataTypes::SMALLINT() - STRING/VARCHAR:
DataTypes::STRING($length = 255, [...]) - TEXT:
DataTypes::TEXT([...]) - BOOLEAN:
DataTypes::BOOLEAN([...]) - DATE, DATETIME, TIME, TIMESTAMP
- FLOAT, DECIMAL($precision = 10, $scale = 0)
- JSON (falls back to TEXT where unsupported)
- UUID (native on PostgreSQL,
CHAR(36)elsewhere)
Common column options:
allowNull(bool): allow NULL values (default: true)default(mixed): default valueunique(bool): unique constraint (column-level)primaryKey(bool): marks column as primary keyautoIncrement(bool): auto-increment (dialect‑aware)
Creating Tables
Use QueryInterface to create tables. Example users table:
use Sommy\ORM\DataTypes; $qi = $sommy->getQueryInterface(); $qi->createTable('users', [ 'id' => DataTypes::INTEGER(['primaryKey' => true, 'autoIncrement' => true, 'allowNull' => false]), 'name' => DataTypes::STRING(255, ['allowNull' => false]), 'age' => DataTypes::INTEGER(), 'is_admin' => DataTypes::BOOLEAN(['default' => false]), 'created_at' => DataTypes::DATETIME(['allowNull' => false]), ], ['ifNotExists' => true]);
Drop a table:
$qi->dropTable('users', ['ifExists' => true]);
Defining Models
Define a model with SommyManager::define($name, $attributes, $options = []). This returns an instance of an anonymous subclass of Model that is bound to your table and schema.
use Sommy\ORM\Model; use Sommy\ORM\DataTypes; $User = $sommy->define('User', [ 'id' => DataTypes::INTEGER(['primaryKey' => true, 'autoIncrement' => true, 'allowNull' => false]), 'name' => DataTypes::STRING(255, ['allowNull' => false]), 'age' => DataTypes::INTEGER(), ], [ 'tableName' => 'users', // optional; defaults to strtolower('User') ]);
Important: Many Model methods are static (Sequelize‑like). You can:
- Call via the class name string of the returned instance:
$UserClass = $User::class; // get anonymous class name $id = $UserClass::create(['name' => 'Alice', 'age' => 30]);
- Or create an instance and use instance helpers like
save():
$u = new $UserClass(['name' => 'Bob', 'age' => 25]); $u->save(); // INSERT, sets primary key if available
For convenience, calling static methods on the instance (e.g., $User->findAll()) also works in practice, but using the class name string as shown above is clearer.
CRUD Examples
Create (static):
$UserClass = $User::class; $id = $UserClass::create(['name' => 'Alice', 'age' => 30]);
Create + save (instance):
$u = new $UserClass(['name' => 'Bob']); $u->age = 25; $u->save(); // INSERT
Find:
$all = $UserClass::findAll(); $admins = $UserClass::findAll(['is_admin' => 1], ['order' => ['id' => 'DESC']]); $first = $UserClass::findOne(['id' => 1]);
Update:
// Bulk update $affected = $UserClass::update(['age' => 31], ['name' => 'Alice']); // Instance update via save() $alice = $UserClass::findOne(['name' => 'Alice']); if ($alice) { $alice->age = 32; $alice->save(); }
Delete:
// Bulk delete $deleted = $UserClass::destroy(['id' => [3,4,5]]); // IN (...) support // Instance delete $u = $UserClass::findOne(['id' => 2]); if ($u) { $u->delete(); }
QueryInterface Reference
Available on $sommy->getQueryInterface().
- select:
select(string $table, array $columns = ['*'], array $where = [], array $options = []) : array- Where supports:
['col' => value, 'status' => ['a','b'], 'deleted_at' => null] - Options:
order(string or array),limit(int),offset(int)
- Where supports:
- insert:
insert(string $table, array $data) : string|bool(returns last insert id if available) - update:
update(string $table, array $data, array $where) : int - delete:
delete(string $table, array $where) : int - createTable:
createTable(string $name, array $attributes, array $options = []) : bool - dropTable:
dropTable(string $name, array $options = []) : bool
Identifier quoting and SQL types are dialect‑aware where it matters (e.g., MySQL backticks vs. PostgreSQL double quotes, SERIAL/BIGSERIAL, SQLite INTEGER PRIMARY KEY).
CLI: Migrations
Sommy includes a tiny CLI in bin/sommy for simple time‑stamped migrations.
- Initialize config (creates
sommy.config.php):
php bin/sommy init:config
- Create a migration file:
php bin/sommy migrate:create create_users
This generates database/migrations/YYYMMDDHHMMSS_create_users.php with a class like Migration_YYYMMDDHHMMSS_create_users containing up() and down().
- Edit your migration, e.g.:
use Sommy\ORM\QueryInterface; class Migration_20250101010101_create_users { public function up(QueryInterface $qi): void { $qi->createTable('users', [ 'id' => ['type' => 'INTEGER', 'primaryKey' => true, 'autoIncrement' => true, 'allowNull' => false], 'name' => ['type' => 'VARCHAR', 'length' => 255, 'allowNull' => false], ], ['ifNotExists' => true]); } public function down(QueryInterface $qi): void { $qi->dropTable('users', ['ifExists' => true]); } }
- Apply pending migrations:
php bin/sommy migrate:up
- Revert last migration:
php bin/sommy migrate:down
The CLI records applied migrations in a table sommy_migrations.
Quick Start (End‑to‑End)
require __DIR__ . '/vendor/autoload.php'; use Sommy\ORM\SommyManager; use Sommy\ORM\DataTypes; $sommy = new SommyManager([ 'dialect' => 'sqlite', 'database' => __DIR__ . '/database.sqlite', ]); // Create a table (usually via migration) $qi = $sommy->getQueryInterface(); $qi->createTable('users', [ 'id' => DataTypes::INTEGER(['primaryKey' => true, 'autoIncrement' => true, 'allowNull' => false]), 'name' => DataTypes::STRING(255, ['allowNull' => false]), ], ['ifNotExists' => true]); // Define a model $User = $sommy->define('User', [ 'id' => DataTypes::INTEGER(['primaryKey' => true, 'autoIncrement' => true, 'allowNull' => false]), 'name' => DataTypes::STRING(255, ['allowNull' => false]), ], ['tableName' => 'users']); // Use the class name for static methods $UserClass = $User::class; $id = $UserClass::create(['name' => 'Alice']); $all = $UserClass::findAll(); foreach ($all as $u) { echo $u->name . "\n"; }
Design Notes
- Minimal abstraction: generated SQL is straightforward and debug‑friendly.
- Dialect‑aware DDL: common types and primary/auto‑increment behaviors map correctly for MySQL/MariaDB, PostgreSQL, SQLite.
- Anonymous model classes:
define()returns an instance; use$Model::classto access static methods cleanly.
Troubleshooting
- Ensure the PDO driver for your DB is installed and enabled.
- For MySQL/MariaDB, set a valid
charset(defaults toutf8mb4). - If
migrate:upfails, check your migration for typos in column definitions (e.g.,allowNull,primaryKey). - If a migration class fails to load or you see a PHP parse error, ensure method signatures include the parameter variable:
use
public function up(Sommy\\ORM\\QueryInterface $qi): voidandpublic function down(Sommy\\ORM\\QueryInterface $qi): void. - On MySQL/MariaDB, DDL statements (CREATE/DROP TABLE) auto-commit and may end a transaction. The CLI now guards commit/rollback, but if you saw "There is no active transaction", update to the latest version and re-run.
Model Generator
Generate a model class (Laravel-style) into database/models:
php bin/sommy make:model User
Options:
--table=users: set table name (default: lowercase name + 's')-mor--migration: also generate a migration stub (e.g.,create_users)--force: overwrite the file if it exists
Autoload for dev:
composer.jsonincludes anautoload-devmappingDatabase\Models\→database/models/.- Run
composer dump-autoloadafter generating models so Composer picks them up.
Using a generated model:
use Sommy\ORM\SommyManager; use Database\Models\User; $sommy = new SommyManager([...]); User::register($sommy); // binds table + attributes defined in the model // After defining attributes in User::register(): $id = User::create(['name' => 'Alice']);