A lightweight, easy-to-use Object-Relational Mapping (ORM) library for PHP that provides an elegant Active Record implementation for working with databases. Built with modern PHP practices including Composer autoloading and Docker support.
- π Lightweight: Minimal overhead with maximum functionality
- π Active Record Pattern: Intuitive database operations
- π Query Builder: Fluent interface for complex queries
- π‘οΈ Mass Assignment Protection: Secure attribute filling
- π Hidden Attributes: Exclude sensitive data from output
- π Automatic Table Names: Convention over configuration
- π³ Docker Ready: Complete development environment
- π¦ Composer Integration: PSR-4 autoloading
- ποΈ Modern PHP: Follows current best practices
- πΎ Connection Management: Singleton database connections
- PHP 8.0 or higher
- PDO MySQL extension
- Composer
- Docker & Docker Compose (for development environment)
- Clone the repository:
git clone <repository-url>
cd simple-php-orm- Copy environment file:
cp .env.example .env- Start the Docker environment:
docker-compose up -d- Install dependencies:
docker-compose exec app composer install- Access the application:
- Web: http://localhost:8080
- Example: http://localhost:8080/example.php
- Clone the repository
- Install dependencies:
composer install- Configure your database in
.env - Include the autoloader in your application:
require_once 'vendor/autoload.php';βββ db/ # Database classes
β βββ Builder.php # Query builder
β βββ Connection.php # Database connection
βββ public/ # Web accessible files
β βββ example.php # Usage examples
βββ src/ # Source code
β βββ models/
β βββ Model.php # Base model class
β βββ User.php # User model example
β βββ Post.php # Post model example
βββ vendor/ # Composer dependencies
βββ .env # Environment variables
βββ .dockerignore # Docker ignore file
βββ 000-default.conf # Apache configuration
βββ composer.json # Composer configuration
βββ docker-compose.yml # Docker services
βββ Dockerfile # Docker image definition
βββ README.md # This file
The .env file contains your environment configuration:
DB_HOST=mysql
DB_PORT=your_port
DB_DATABASE=your_database
DB_USERNAME=your_username
DB_PASSWORD=your_passwordThe database configuration is handled automatically through environment variables in your .env file. The Connection class uses the vlucas/phpdotenv package to load these variables:
// Environment variables loaded automatically
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_username
DB_PASSWORD=your_password<?php
require_once dirname(__DIR__).'/vendor/autoload.php';
use Database\Connection;
use Source\Models\User;
use Source\Models\Post;
// Initialize database connection
Connection::init();<?php
namespace Source\Models;
class User extends Model
{
protected static $table = 'users';
protected static $fillable = ['name', 'email', 'password'];
protected static $hidden = ['password'];
}// Create a user
$user = User::create([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => 'secret123'
]);
// Find a user
$user = User::find(1);
// Update a user
$user->name = 'Jane Doe';
$user->save();The Docker setup includes:
- PHP 8.1 with Apache: Web server
- MySQL 8.0: Database server
- phpMyAdmin: Database management (http://localhost:8081)
# Start services
docker-compose up -d
# Stop services
docker-compose down
# View logs
docker-compose logs app
# Execute commands in container
docker-compose exec app bash
docker-compose exec app composer install
# Database access
docker-compose exec mysql mysql -u root -pIf you encounter permission issues:
# Fix ownership (Linux/Mac)
sudo chown -R $USER:$USER .
# Or run in Docker
docker-compose exec app chown -R www-data:www-data /var/www/htmlThe composer.json includes PSR-4 autoloading:
{
"name": "your-username/simple-php-orm",
"description": "A lightweight PHP ORM",
"autoload": {
"psr-4": {
"Database\\": "db/",
"Source\\": "src/"
}
},
"require": {
"php": ">=8.0",
"vlucas/phpdotenv": "^5.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
}
}# Add a package
docker-compose exec app composer require package/name
# Add dev dependency
docker-compose exec app composer require --dev package/name
# Update dependencies
docker-compose exec app composer update<?php
require_once dirname(__DIR__).'/vendor/autoload.php';
use Database\Connection;
use Source\Models\User;
Connection::init();
// Create
$user = User::create([
'name' => 'John Doe',
'email' => 'john@example.com'
]);
// Read
$user = User::find(1);
$users = User::all();
// Update
$user->name = 'Jane Doe';
$user->save();
// Delete
$user->delete();use Database\Builder;
use Source\Models\User;
// Complex query with Builder
$activeUsers = User::get(
User::query()
->where('status', 'active')
->where('email', 'LIKE', '%@company.com')
->orderBy('created_at', 'DESC')
->limit(10)
);
// Join queries
$usersWithPosts = User::get(
User::query()
->select(['users.*', 'COUNT(posts.id) as post_count'])
->leftJoin('posts', 'users.id', 'posts.user_id')
->groupBy('users.id')
->having('post_count', '>', 0)
);<?php
namespace Source\Models;
class User extends Model
{
protected static $fillable = ['name', 'email', 'password'];
protected static $hidden = ['password'];
public function posts()
{
return Post::get(
Post::query()->where('user_id', $this->id)
);
}
public static function findByEmail($email)
{
return static::findBy('email', $email);
}
}
class Post extends Model
{
protected static $fillable = ['title', 'content', 'user_id'];
public function user()
{
return User::find($this->user_id);
}
}Set these in your .env file:
# Database Configuration
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=orm_database
DB_USERNAME=orm_user
DB_PASSWORD=secure_password
# Application
APP_ENV=development
APP_DEBUG=true
# Docker MySQL
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=orm_database
MYSQL_USER=orm_user
MYSQL_PASSWORD=secure_passwordThe database connection is automatically configured using environment variables. The Database\Connection class uses the vlucas/phpdotenv package to load configuration from your .env file:
<?php
// Inside Database\Connection class
private function __construct()
{
// Handle PDO options
$config = [
'options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
]
];
// Load environment variables
Dotenv::createImmutable(dirname(__DIR__))->load();
// Build DSN and create connection
$dsn = "mysql:host={$_ENV['DB_HOST']};port={$_ENV['DB_PORT']};dbname={$_ENV['DB_DATABASE']};charset=utf8mb4";
$this->pdo = new \PDO($dsn, $_ENV['DB_USERNAME'], $_ENV['DB_PASSWORD'], $config['options']);
}Required environment variables in your .env file:
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_username
DB_PASSWORD=your_password# Install dev dependencies
docker-compose exec app composer install
# Run tests
docker-compose exec app ./vendor/bin/phpunit
# Run with coverage
docker-compose exec app ./vendor/bin/phpunit --coverage-html coverage<?php
use PHPUnit\Framework\TestCase;
use Source\Models\User;
class UserTest extends TestCase
{
public function testUserCreation()
{
$user = User::create([
'name' => 'Test User',
'email' => 'test@example.com'
]);
$this->assertInstanceOf(User::class, $user);
$this->assertEquals('Test User', $user->name);
}
}- Set production environment variables
- Configure web server (Apache/Nginx)
- Set proper file permissions
- Enable PHP OPcache
- Use environment-specific configuration
// Always validate input
$userData = filter_var_array($_POST, [
'name' => FILTER_SANITIZE_STRING,
'email' => FILTER_VALIDATE_EMAIL
]);
// Use prepared statements (built-in)
$users = User::get(
User::query()->where('email', $email) // Automatically uses prepared statements
);
// Hash passwords properly
class User extends Model
{
public function setPassword($password)
{
$this->password = password_hash($password, PASSWORD_DEFAULT);
}
}The Connection class automatically loads environment variables using Dotenv:
use Database\Connection;
// Initialize connection (loads .env automatically)
Connection::init();
// Get PDO instance
$pdo = Connection::getInstance()->getPDO();Note: The Connection class automatically loads the .env file from the project root using vlucas/phpdotenv. Make sure your .env file contains all required database variables.
use Database\Builder;
$builder = new Builder('users');
$builder->select(['id', 'name'])
->where('status', 'active')
->orderBy('name')
->limit(10);
$sql = $builder->toSql();
$results = $builder->get();All models extend the base Model class:
// Static methods
Model::find($id)
Model::findOrFail($id)
Model::all()
Model::create($attributes)
Model::get($query)
// Instance methods
$model->save()
$model->delete()
$model->toArray()
$model->toJson()
$model->isDirty()- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Make changes and add tests
- Run tests:
docker-compose exec app composer test - Commit changes:
git commit -am 'Add feature' - Push to branch:
git push origin feature-name - Submit a Pull Request
# Clone and setup
git clone <repo-url>
cd simple-php-orm
cp .env.example .env
# Start development environment
docker-compose up -d
docker-compose exec app composer install
# Run tests
docker-compose exec app composer testPermission Denied
sudo chown -R $USER:$USER .
docker-compose exec app chown -R www-data:www-data /var/www/htmlDatabase Connection Failed
- Check
.envconfiguration - Ensure MySQL container is running:
docker-compose ps - Check logs:
docker-compose logs mysql
Composer Issues
# Clear cache
docker-compose exec app composer clear-cache
# Reinstall dependencies
docker-compose exec app rm -rf vendor
docker-compose exec app composer installDocker Issues
# Rebuild containers
docker-compose down
docker-compose build --no-cache
docker-compose up -dThis project is open source and available under the MIT License.
- π Check the
/public/example.phpfor usage examples - π Report issues on GitHub
- π¬ Start discussions for questions
- π§ Contact me for support
- Docker development environment
- Composer PSR-4 autoloading
- Namespaced classes
- Modern PHP 8.0+ support
- Complete CRUD operations
- Query builder with fluent interface
- Mass assignment protection
- JSON serialization