prochst / bs-orm
Simple, lightweight ORM for PHP with multi-language support, relations, and migrations
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/prochst/bs-orm
Requires
- php: >=8.1
Requires (Dev)
- phpstan/phpstan: ^1.10
This package is auto-updated.
Last update: 2026-02-11 09:51:28 UTC
README
Simple, lightweight ORM for PHP with multi-language support, relations, and migrations.
Features
- ✅ Active Record & Repository Pattern - Choose the pattern that fits your needs
- ✅ Type-Safe - Full PHP 8.1+ type support with custom types
- ✅ Relations - HasOne, HasMany, BelongsTo, BelongsToMany
- ✅ Multi-Language - Built-in internationalization for labels, placeholders, help texts
- ✅ Migrations - Database schema versioning
- ✅ Eager Loading - Solve N+1 query problems
- ✅ Change Tracking - Only update modified fields
- ✅ Validation - Built-in and custom validators
- ✅ Database Agnostic - Works with MySQL, PostgreSQL, SQLite
- ✅ PDO Based - No external dependencies
Installation
composer require prochst/bs-orm
Documentation
For detailed documentation, guides, and examples, see docs/README.md.
Quick Start
1. Define Your Entity
<?php use prochst\bsOrm\Entity; use prochst\bsOrm\Table; use prochst\bsOrm\Column; use prochst\bsOrm\Types\StringType; use prochst\bsOrm\Types\IntegerType; #[Table(name: 'users', label: 'Users')] class User extends Entity { #[Column( type: new IntegerType(), primaryKey: true, autoIncrement: true )] private ?int $id = null; #[Column( name: 'email', type: new StringType(maxLength: 255), label: 'Email Address', nullable: false, unique: true )] private string $email; #[Column( type: new StringType(maxLength: 100), label: 'Name' )] private string $name; // Getters and setters public function getId(): ?int { return $this->id; } public function getEmail(): string { return $this->email; } public function getName(): string { return $this->name; } public function setEmail(string $email): void { $this->email = $email; $this->markFieldAsModified('email'); } public function setName(string $name): void { $this->name = $name; $this->markFieldAsModified('name'); } }
For PHP 8.4+ with Property Hooks and Asymmetric Visibility
If you're using PHP 8.4 or later, you can leverage the new public private(set) syntax for cleaner code without explicit getters:
<?php use prochst\bsOrm\Entity; use prochst\bsOrm\Table; use prochst\bsOrm\Column; use prochst\bsOrm\Types\StringType; use prochst\bsOrm\Types\IntegerType; #[Table(name: 'users', label: 'Users')] class User extends Entity { #[Column( type: new IntegerType(), primaryKey: true, autoIncrement: true )] public private(set) ?int $id = null; #[Column( name: 'email', type: new StringType(maxLength: 255), label: 'Email Address', nullable: false, unique: true )] public private(set) string $email; #[Column( type: new StringType(maxLength: 100), label: 'Name' )] public private(set) string $name; // Only setters needed - properties are publicly readable public function setEmail(string $email): void { $this->email = $email; $this->markFieldAsModified('email'); } public function setName(string $name): void { $this->name = $name; $this->markFieldAsModified('name'); } } // Usage - direct property access for reading: $user = $userRepo->find(1); echo $user->name; // No getter needed! echo $user->email; // Direct property access // Writing still requires setters for change tracking: $user->setName('John Doe');
This approach provides:
- Cleaner syntax - No boilerplate getters
- Type safety - Full type hints preserved
- Write protection - Properties can only be modified via setters
- Change tracking - Setters still call
markFieldAsModified()
See examples/basic-usage-84.php for a complete working example.
2. Use the Repository
<?php use prochst\bsOrm\Dbal; use prochst\bsOrm\Repository; // Create PDO connection $pdo = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'password'); $dbal = new Dbal($pdo); // Create repository $userRepo = new Repository($dbal, User::class); // Find user by ID $user = $userRepo->find(1); // Find all users $users = $userRepo->findAll(); // Find with criteria $activeUsers = $userRepo->findBy( ['active' => true], ['name' => 'ASC'], limit: 10 ); // Create new user $user = new User(); $user->setEmail('john@example.com'); $user->setName('John Doe'); $userRepo->save($user); // Update user $user = $userRepo->find(1); $user->setEmail('newemail@example.com'); $userRepo->save($user); // Only updates modified fields // Delete user $userRepo->delete($user);
3. Working with Relations
<?php use prochst\bsOrm\Relations\HasMany; use prochst\bsOrm\Relations\BelongsTo; #[Table(name: 'users')] class User extends Entity { #[Column(primaryKey: true)] private ?int $id = null; #[HasMany(entityClass: Post::class, foreignKey: 'user_id')] private array $posts = []; public function getPosts(): array { return $this->posts; } } #[Table(name: 'posts')] class Post extends Entity { #[Column(primaryKey: true)] private ?int $id = null; #[Column] private int $user_id; #[BelongsTo(entityClass: User::class, foreignKey: 'user_id')] private ?User $user = null; public function getUser(): ?User { return $this->user; } } // Eager loading to avoid N+1 queries $users = $userRepo->findAllWithRelations(['posts']); foreach ($users as $user) { echo $user->getName() . " has " . count($user->getPosts()) . " posts\n"; }
Multi-Language Support
<?php use prochst\bsOrm\Types\LocaleManager; #[Column( label: 'Email', labels: [ 'cs_CZ' => 'E-mailová adresa', 'en_US' => 'Email Address', 'de_DE' => 'E-Mail-Adresse', ], placeholder: 'Enter email', placeholders: [ 'cs_CZ' => 'Zadejte e-mail', 'en_US' => 'Enter email', 'de_DE' => 'E-Mail eingeben', ] )] private string $email; // Set locale LocaleManager::setDefaultLocale('cs_CZ'); // Get translated label $label = User::getColumnLabel('email'); // "E-mailová adresa"
Custom Types
BS ORM includes several built-in types:
StringType- VARCHAR/TEXT with max lengthIntegerType- INT/BIGINTDecimalType- DECIMAL/NUMERIC with precisionBooleanType- BOOLEAN/TINYINT(1)DateTimeType- DATETIME/TIMESTAMPJsonType- JSON dataEnumType- ENUM valuesBlobType- BLOB/BYTEATextType- TEXT/LONGTEXTCurrencyType- Money values with currency code
Transactions
<?php $dbal->transaction(function($dbal) use ($userRepo, $postRepo) { $user = new User(); $user->setEmail('john@example.com'); $userRepo->save($user); $post = new Post(); $post->setUserId($user->getId()); $post->setTitle('My First Post'); $postRepo->save($post); });
Requirements
- PHP 8.1 or higher
- PDO extension
- One of: MySQL, PostgreSQL, or SQLite
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Author
prochst - GitHub Profile