marwen-brini/bob-the-builder

A complete ORM and query builder for PHP with Laravel-like fluent syntax, models, relationships, and migrations

Fund package maintenance!
Marwen-Brini

Installs: 13

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/marwen-brini/bob-the-builder


README

A highly optimized, standalone PHP query builder with Laravel-like fluent syntax. Originally designed to enhance Quantum ORM's query building capabilities, Bob Query Builder is a fully independent package that can be used in ANY PHP project - from WordPress plugins to modern PHP frameworks, microservices, or standalone applications.

PHP Version License Tests Coverage Documentation

Why Bob Query Builder?

While initially created to modernize Quantum ORM's query building capabilities, Bob Query Builder was designed from the ground up as a universal PHP query builder that can enhance ANY PHP application:

  • Framework Agnostic - Use it with Laravel, Symfony, WordPress, or vanilla PHP
  • Zero Lock-in - No framework dependencies, just pure PHP and PDO
  • Modern PHP - Built for PHP 8.1+ with full type safety
  • Production Ready - 1773 tests, 100% passing, battle-tested
  • High Performance - <10ms query building overhead, handles 50k+ rows efficiently

Features

  • 🚀 Full ORM with ActiveRecord - Complete ORM layer with models and relationships (v2.0)
  • 🔗 Laravel-like Relationships - HasOne, HasMany, BelongsTo, BelongsToMany with eager loading
  • 🗄️ Database Migrations - Complete migration system with versioning and rollbacks (v3.0)
  • 🏗️ Schema Builder - Fluent interface for creating and modifying database tables
  • 🔍 Schema Inspector - Reverse engineer existing databases and generate migrations
  • 🌐 WordPress Schema Support - Specialized helpers for WordPress/WooCommerce tables
  • 💼 Laravel-like Fluent Interface - Familiar, expressive query builder syntax
  • 🔧 Database Agnostic - Support for MySQL, PostgreSQL, SQLite via PDO
  • 🎯 Zero Dependencies - Only requires PHP and PDO
  • High Performance - Query caching, prepared statements, 1M+ rows/second streaming
  • 🧪 Fully Tested - 1773 tests with Pest, 100% code coverage
  • 🔒 Secure - Automatic SQL injection prevention via parameter binding
  • 📦 Modular - Easy integration with ANY PHP project, use as query builder or full ORM
  • 🔄 Transaction Support - Including savepoints for nested transactions
  • 📊 PSR-3 Logging - Built-in query logging with slow query detection
  • 💾 Memory Efficient - Stream 50k+ rows with minimal memory usage
  • 🎁 Collections - Powerful collection class for working with result sets
  • 🎪 Event System - Hook into migration lifecycle with custom event listeners

Recent Updates (v3.0.0) 🎉

🗄️ Complete Migration System & Schema Builder

Bob v3.0 introduces a comprehensive database migration and schema management system:

  • Schema Builder - Fluent interface for creating and modifying database tables across MySQL, PostgreSQL, and SQLite
  • Migration System - Complete migration lifecycle management with versioning, rollbacks, and batch tracking
  • WordPress Support - Specialized WordPressBlueprint with helpers for WordPress and WooCommerce schemas
  • Schema Inspector - Reverse engineer existing databases and generate migration files automatically
  • Event System - Hook into migration lifecycle events for logging and custom workflows
  • Migration Features:
    • Dependency resolution between migrations
    • Transaction support with automatic rollback on failure
    • Lifecycle hooks (before(), after())
    • Pretend mode for safe testing
    • Batch tracking and status reporting

See the Database Migrations & Schema Builder section for complete documentation.

Previous Updates (v2.2.2)

🎯 100% Code Coverage Achieved!

The test suite now has complete 100% code coverage across all components:

  • 1773 tests passing with 4387 assertions
  • Model class: Full coverage including edge cases for existing ID updates
  • All test failures resolved: Fixed naming conflicts and relationship configurations
  • Enhanced test reliability: Better handling of complex scenarios

🛠️ Test Suite Improvements

  • Fixed class naming conflicts in Issue #13 debug tests
  • Updated Issue #15 test expectations to reflect corrected behavior
  • Improved BelongsToMany relationship tests for WordPress-style tables
  • Added comprehensive tests for models with existing database IDs

Previous Updates (v2.2.1)

🛠️ Bug Fixes

Global Scope Field References in WHERE Clauses

Fixed an important issue with global scopes and field references:

  • Global scopes now apply in toSql() - SQL generation now includes global scope modifications
  • Prevents duplicate scope application - Added tracking to ensure scopes apply only once
  • No side effects - Clone builder in toSql() to avoid modifying original instance

Previous Updates (v2.1.1)

🛠️ Critical Bug Fixes

Table Prefix Handling in JOIN Clauses

Fixed all table prefix issues reported in production environments:

  • Double prefix bug fixed - No more duplicate prefixes in JOIN WHERE clauses
  • Global scopes with JOINs - Global scopes containing JOINs now work correctly
  • Table aliases - Proper handling of aliases in SELECT statements with JOINs
  • Subqueries - whereIn() with subquery builders handles prefixes correctly

New Model Method: forceFill()

Added Laravel-compatible forceFill() method for bypassing mass assignment:

// Hydrate models from database without checking fillable/guarded
$model->forceFill($databaseRow); // Bypasses mass assignment protection

Previous Updates (v2.1.0)

🚀 New Features

Query Caching for exists()

Optimize repeated existence checks with the new opt-in caching mechanism:

$builder = $connection->table('users')
    ->enableExistsCache(120) // Cache for 2 minutes
    ->where('email', 'user@example.com');

// First call - hits database
$exists = $builder->exists();

// Subsequent calls within 2 minutes - uses cache
$exists = $builder->exists(); // No database query!

Global Scopes in Relationships

Relationships now properly inherit global scopes:

// Global scopes automatically apply to relationships
$user->posts()->get(); // Includes global scopes from Post model

// Or disable for specific queries
$user->posts()->withoutGlobalScopes()->get();

Previous Updates (v2.0.7)

  • Global Scopes Support - Laravel-style instance-level global scopes
  • Nested WHERE Closures - Fixed SQL generation for nested conditions
  • Delete Bindings Isolation - Fixed parameter binding in delete operations
  • Timestamp Handling - Properly respects $timestamps = false
  • Scope Chaining - Full support for chaining custom scope methods
  • Aggregate Functions - Automatic detection and handling
  • Subquery Support - Fixed whereIn() with Builder subqueries

🔒 PHP 8.4+ Compatibility

  • Full compatibility with PHP 8.1, 8.2, 8.3, and 8.4
  • All implicit nullable parameter warnings fixed

Requirements

  • PHP 8.1, 8.2, 8.3, or 8.4
  • PDO extension
  • Database-specific PDO driver (pdo_mysql, pdo_pgsql, pdo_sqlite)

Installation

Install via Composer:

composer require marwen-brini/bob-the-builder

Quick Start

use Bob\Database\Connection;

// Configure your database connection
$connection = new Connection([
    'driver' => 'mysql',
    'host' => '127.0.0.1',
    'port' => 3306,
    'database' => 'your_database',
    'username' => 'your_username',
    'password' => 'your_password',
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
]);

// Start building queries
$users = $connection->table('users')
    ->where('active', true)
    ->where('age', '>=', 18)
    ->orderBy('name')
    ->limit(10)
    ->get();

Configuration

Fetch Mode

By default, Bob Query Builder returns query results as associative arrays. You can configure this behavior:

// Option 1: Configure via connection config
$connection = new Connection([
    'driver' => 'mysql',
    'database' => 'mydb',
    // ... other config
    'fetch' => PDO::FETCH_OBJ,  // Return objects instead of arrays
]);

// Option 2: Change dynamically at runtime
$connection->setFetchMode(PDO::FETCH_OBJ);  // Use objects
$users = $connection->table('users')->get(); // Returns array of stdClass objects

$connection->setFetchMode(PDO::FETCH_ASSOC); // Back to arrays (default)
$users = $connection->table('users')->get(); // Returns array of associative arrays

Available fetch modes:

  • PDO::FETCH_ASSOC - Associative arrays (default)
  • PDO::FETCH_OBJ - stdClass objects
  • PDO::FETCH_NUM - Numeric arrays
  • PDO::FETCH_BOTH - Both numeric and associative arrays
  • Any other PDO fetch mode constant

Basic Usage

Select Queries

// Get all records
$users = $connection->table('users')->get();

// Get specific columns
$users = $connection->table('users')
    ->select('id', 'name', 'email')
    ->get();

// Get first record
$user = $connection->table('users')
    ->where('email', 'john@example.com')
    ->first();

// Get single value
$email = $connection->table('users')
    ->where('id', 1)
    ->value('email');

Where Clauses

// Basic where
$users = $connection->table('users')
    ->where('status', 'active')
    ->get();

// Multiple conditions
$users = $connection->table('users')
    ->where('status', 'active')
    ->where('age', '>', 18)
    ->get();

// Or where
$users = $connection->table('users')
    ->where('role', 'admin')
    ->orWhere('role', 'moderator')
    ->get();

// Where in
$users = $connection->table('users')
    ->whereIn('id', [1, 2, 3])
    ->get();

// Where between
$users = $connection->table('users')
    ->whereBetween('age', [18, 65])
    ->get();

// Where null
$users = $connection->table('users')
    ->whereNull('deleted_at')
    ->get();

Joins

// Inner join
$users = $connection->table('users')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->select('users.*', 'posts.title')
    ->get();

// Left join
$users = $connection->table('users')
    ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
    ->get();

// Multiple joins
$users = $connection->table('users')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->join('comments', 'posts.id', '=', 'comments.post_id')
    ->get();

Aggregates

// Count
$count = $connection->table('users')->count();

// Sum
$total = $connection->table('orders')->sum('amount');

// Average
$avg = $connection->table('products')->avg('price');

// Min/Max
$min = $connection->table('products')->min('price');
$max = $connection->table('products')->max('price');

Insert

// Single record
$connection->table('users')->insert([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'password' => bcrypt('password')
]);

// Multiple records
$connection->table('users')->insert([
    ['name' => 'John', 'email' => 'john@example.com'],
    ['name' => 'Jane', 'email' => 'jane@example.com']
]);

// Insert and get ID
$id = $connection->table('users')->insertGetId([
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

Update

// Update records
$affected = $connection->table('users')
    ->where('id', 1)
    ->update(['status' => 'active']);

// Update with increment
$connection->table('posts')
    ->where('id', 1)
    ->update(['views' => $connection->raw('views + 1')]);

Delete

// Delete records
$deleted = $connection->table('users')
    ->where('status', 'inactive')
    ->delete();

// Delete by ID
$connection->table('users')->delete(5);

// Truncate table
$connection->table('users')->truncate();

Transactions

// Basic transaction
$connection->transaction(function ($connection) {
    $connection->table('users')->insert([...]);
    $connection->table('posts')->insert([...]);
});

// Manual transaction control
$connection->beginTransaction();
try {
    // Your queries here
    $connection->commit();
} catch (Exception $e) {
    $connection->rollBack();
    throw $e;
}

// Transaction with retries
$connection->transaction(function ($connection) {
    // Your queries here
}, attempts: 3);

Raw Expressions

// Raw select
$users = $connection->table('users')
    ->select($connection->raw('COUNT(*) as user_count'))
    ->get();

// Raw where
$users = $connection->table('users')
    ->where('created_at', '>', $connection->raw('NOW() - INTERVAL 1 DAY'))
    ->get();

Pagination

// Simple pagination
$page = 2;
$perPage = 15;

$users = $connection->table('users')
    ->page($page, $perPage)
    ->get();

// Manual limit/offset
$users = $connection->table('users')
    ->limit(10)
    ->offset(20)
    ->get();

Chunking

Process large datasets efficiently:

$connection->table('users')->chunk(100, function ($users) {
    foreach ($users as $user) {
        // Process user
    }
});

Cursor Streaming

For extremely large datasets (50k+ rows), use cursor for memory-efficient processing:

// Stream millions of rows with minimal memory usage
foreach ($connection->table('users')->cursor() as $user) {
    // Process one user at a time
    // Memory usage stays constant regardless of dataset size
    processUser($user);
}

// Performance: 1M+ rows/second throughput
// Memory: ~8MB for 15,000 rows

Query Debugging

// Enable query logging
$connection->enableQueryLog();

// Run queries
$users = $connection->table('users')->get();

// Get query log
$queries = $connection->getQueryLog();
print_r($queries);

// Get SQL without executing
$sql = $connection->table('users')
    ->where('active', true)
    ->toSql();
echo $sql; // select * from "users" where "active" = ?

// Get bindings
$bindings = $connection->table('users')
    ->where('active', true)
    ->getBindings();
print_r($bindings); // [true]

Database Configuration

MySQL/MariaDB

$connection = new Connection([
    'driver' => 'mysql',
    'host' => '127.0.0.1',
    'port' => 3306,
    'database' => 'database_name',
    'username' => 'username',
    'password' => 'password',
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'options' => [
        PDO::ATTR_PERSISTENT => false,
    ]
]);

PostgreSQL

$connection = new Connection([
    'driver' => 'pgsql',
    'host' => '127.0.0.1',
    'port' => 5432,
    'database' => 'database_name',
    'username' => 'username',
    'password' => 'password',
    'prefix' => '',
    'schema' => 'public',
]);

SQLite

$connection = new Connection([
    'driver' => 'sqlite',
    'database' => '/path/to/database.sqlite',
    'prefix' => '',
]);

// In-memory database (great for testing)
$connection = new Connection([
    'driver' => 'sqlite',
    'database' => ':memory:',
    'prefix' => '',
]);

ORM & Model Features

Model Definition

Bob v2.0 introduces a full ORM layer with Laravel-inspired models:

use Bob\Database\Model;

class User extends Model
{
    protected $table = 'users';
    protected $primaryKey = 'id';
    protected $fillable = ['name', 'email', 'password'];

    // Define relationships
    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    public function profile()
    {
        return $this->hasOne(Profile::class);
    }

    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_roles');
    }
}

Relationships

One-to-One (HasOne)

class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

// Usage
$user = User::find(1);
$profile = $user->profile; // Automatically loads the profile

One-to-Many (HasMany)

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

// Usage
$user = User::find(1);
$posts = $user->posts; // Returns a Collection of Post models

Many-to-One (BelongsTo)

class Post extends Model
{
    public function author()
    {
        return $this->belongsTo(User::class, 'user_id');
    }
}

// Usage
$post = Post::find(1);
$author = $post->author; // Loads the User model

Many-to-Many (BelongsToMany)

class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_roles', 'user_id', 'role_id');
    }
}

// Usage
$user = User::find(1);
$roles = $user->roles; // Returns Collection of Role models

// Attach/Detach relationships
$user->roles()->attach($roleId);
$user->roles()->detach($roleId);
$user->roles()->sync([1, 2, 3]); // Sync to exact set of IDs

Eager Loading

Prevent N+1 queries with eager loading:

// Load users with their posts and comments
$users = User::with('posts.comments')->get();

// Multiple relationships
$users = User::with(['posts', 'profile', 'roles'])->get();

// Eager loading with constraints
$users = User::with(['posts' => function($query) {
    $query->where('published', true);
}])->get();

Model Queries

Models provide an intuitive ActiveRecord interface:

// Find by primary key
$user = User::find(1);

// Find or fail (throws exception)
$user = User::findOrFail(1);

// Create new record
$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

// Update existing
$user->update(['name' => 'Jane Doe']);

// Delete
$user->delete();

// Query builder integration
$users = User::where('active', true)
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();

// Aggregates
$count = User::where('role', 'admin')->count();
$avg = Product::avg('price');

Collections

Model queries return powerful Collection objects:

$users = User::all();

// Collection methods
$admins = $users->filter(fn($user) => $user->role === 'admin');
$names = $users->pluck('name');
$grouped = $users->groupBy('role');
$sorted = $users->sortBy('created_at');

// Map over items
$emails = $users->map(fn($user) => $user->email);

// Check if collection contains item
$hasAdmin = $users->contains('role', 'admin');

Database Migrations & Schema Builder (v3.0)

Schema Builder

Create and modify database tables with an expressive, fluent interface:

use Bob\Schema\Schema;

// Create a new table
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

// Modify existing table
Schema::table('users', function (Blueprint $table) {
    $table->string('phone')->nullable()->after('email');
    $table->index('phone');
});

// Drop table
Schema::dropIfExists('users');

WordPress Schema Support

Bob includes specialized helpers for WordPress and WooCommerce table creation:

use Bob\Schema\Schema;

// Create WordPress-style post table
Schema::createWordPress('custom_posts', function (WordPressBlueprint $table) {
    $table->wpPost();           // All standard WordPress post columns
    $table->wpPostIndexes();    // Standard WordPress indexes
});

// Create custom meta table
Schema::createWordPress('custom_meta', function (WordPressBlueprint $table) {
    $table->wpMeta('custom');   // Creates: meta_id, custom_id, meta_key, meta_value
});

// Create WooCommerce order table
Schema::createWordPress('wc_custom_orders', function (WordPressBlueprint $table) {
    $table->wcOrder();          // All WooCommerce HPOS order columns
});

Migration System

Manage database schema changes with version-controlled migrations:

use Bob\Database\Migrations\Migration;
use Bob\Schema\Blueprint;
use Bob\Schema\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
    }
}

Running Migrations

use Bob\Database\Migrations\MigrationRunner;
use Bob\Database\Migrations\MigrationRepository;
use Bob\Database\Connection;

$connection = new Connection([/* config */]);
$repository = new MigrationRepository($connection, 'migrations');
$runner = new MigrationRunner($connection, $repository, ['/path/to/migrations']);

// Run pending migrations
$runner->run();

// Rollback last batch
$runner->rollback();

// Rollback all migrations
$runner->reset();

// Drop all tables and re-run migrations
$runner->fresh();

// Rollback and re-run all migrations
$runner->refresh();

// Check migration status
$status = $runner->status();

Migration Features

  • Dependency Resolution - Migrations can declare dependencies on other migrations
  • Transaction Support - Run migrations within database transactions
  • Batch Tracking - Track which migrations ran together
  • Lifecycle Hooks - before() and after() methods for setup/cleanup
  • Event System - Hook into migration events for logging and monitoring
  • Pretend Mode - See what migrations would do without running them
  • Version Control - Each migration is tracked with execution time and batch number

Schema Inspector

Reverse engineer existing databases and generate migration files:

use Bob\Schema\Inspector;

$inspector = new Inspector($connection);

// Get all tables
$tables = $inspector->getTables();

// Get table structure
$columns = $inspector->getColumns('users');
$indexes = $inspector->getIndexes('users');
$foreignKeys = $inspector->getForeignKeys('users');

// Generate migration from existing table
$migration = $inspector->generateMigration('users');
file_put_contents('2024_01_01_000000_create_users_table.php', $migration);

Column Types

Bob supports all standard database column types:

// Numeric types
$table->id();                      // Auto-incrementing BIGINT
$table->bigInteger('votes');
$table->integer('count');
$table->smallInteger('votes');
$table->tinyInteger('active');
$table->decimal('amount', 8, 2);
$table->float('amount');
$table->double('amount');

// String types
$table->string('name', 100);
$table->text('description');
$table->longText('content');
$table->char('code', 4);

// Date/Time types
$table->date('birthday');
$table->dateTime('created_at');
$table->timestamp('updated_at');
$table->time('sunrise');
$table->timestamps();              // created_at + updated_at

// Other types
$table->boolean('active');
$table->json('metadata');
$table->binary('data');
$table->uuid('identifier');
$table->enum('status', ['active', 'inactive']);

// Column modifiers
$table->string('email')->nullable();
$table->string('name')->default('Guest');
$table->string('slug')->unique();
$table->integer('position')->unsigned();
$table->text('bio')->comment('User biography');

Indexes and Constraints

// Indexes
$table->primary('id');
$table->unique('email');
$table->index('status');
$table->index(['user_id', 'created_at']);

// Foreign keys
$table->foreign('user_id')
    ->references('id')
    ->on('users')
    ->onDelete('cascade')
    ->onUpdate('cascade');

// Drop constraints
$table->dropPrimary('users_id_primary');
$table->dropUnique('users_email_unique');
$table->dropIndex('users_status_index');
$table->dropForeign('posts_user_id_foreign');

Advanced Features

Query Builder Cloning

$baseQuery = $connection->table('users')->where('active', true);

// Clone for variations
$admins = $baseQuery->clone()->where('role', 'admin')->get();
$users = $baseQuery->clone()->where('role', 'user')->get();

Subqueries

$subquery = $connection->table('posts')
    ->select('user_id')
    ->where('published', true);

$users = $connection->table('users')
    ->whereIn('id', $subquery)
    ->get();

Custom Grammars

Extend the grammar for custom SQL dialects:

use Bob\Query\Grammar;

class CustomGrammar extends Grammar
{
    // Override methods for custom SQL generation
}

$connection->setQueryGrammar(new CustomGrammar());

Testing

Run the test suite:

# Run all tests
composer test

# Run with coverage
composer test:coverage

# Run specific test suite
vendor/bin/pest tests/Integration

# Run with verbose output
vendor/bin/pest -vvv

Performance Benchmarks

Bob Query Builder is highly optimized for real-world applications:

Query Building Performance

  • Simple SELECT: ~0.02ms average overhead
  • Complex queries with joins: ~0.1ms average overhead
  • Subqueries: ~0.07ms average overhead
  • All query types: <10ms overhead guaranteed

Large Dataset Handling

  • Streaming: 1M+ rows per second throughput
  • Memory usage: ~8MB for 15,000 rows with cursor
  • 50,000 rows: Handled efficiently with <30MB memory
  • Chunk processing: Process millions of rows without memory issues

Optimization Features

The query builder includes several optimization features:

  • Prepared Statement Caching: Reuses prepared statements for identical queries
  • Connection Pooling: Efficient connection management
  • Query Result Caching: Optional caching of query results
  • Lazy Loading: Use cursor() for memory-efficient iteration
  • Bulk Operations: Optimized bulk inserts and updates
  • Statement Caching: Second run of cached statements is significantly faster

Use Cases

Bob Query Builder is perfect for:

  • WordPress Plugins - Modern query building without the overhead
  • Legacy PHP Applications - Modernize database interactions incrementally
  • Microservices - Lightweight, efficient database layer
  • API Development - Clean, readable query construction
  • Any PHP Project - From simple scripts to complex applications

Integration Examples

WordPress / Quantum ORM

use Bob\Database\Connection;

$connection = new Connection([
    'driver' => 'mysql',
    'host' => DB_HOST,
    'database' => DB_NAME,
    'username' => DB_USER,
    'password' => DB_PASSWORD,
    'prefix' => $wpdb->prefix,
]);

// Now use modern query building in WordPress!
$posts = $connection->table('posts')
    ->where('post_status', 'publish')
    ->orderBy('post_date', 'desc')
    ->limit(10)
    ->get();

Standalone PHP Application

use Bob\Database\Connection;

$connection = new Connection([
    'driver' => 'sqlite',
    'database' => 'database.sqlite',
]);

$users = $connection->table('users')
    ->where('active', true)
    ->get();

Integration with Any Framework

Bob Query Builder can be registered as a service in any dependency injection container:

// In your service provider or bootstrap
$container->singleton(Connection::class, function () {
    return new Connection(config('database'));
});

📚 Documentation

Full documentation is available at https://marwen-brini.github.io/bob-the-builder/

The documentation includes:

  • Getting Started Guide
  • Complete API Reference
  • Database-specific Features
  • Performance Optimization Tips
  • WordPress Integration Guide
  • CLI Tool Usage
  • Migration from Other Query Builders
  • Troubleshooting Guide

Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

Testing Requirements

  • Tests must be written before implementation (TDD)
  • All tests must pass before merging
  • Maintain 100% code coverage
  • Follow PSR-12 coding standards

License

The Bob Query Builder is open-sourced software licensed under the MIT license.

Project Repository

🔗 GitHub: https://github.com/Marwen-Brini/bob-the-builder

Credits

  • Originally built to enhance Quantum ORM, but designed as a standalone solution for ANY PHP project
  • Inspired by Laravel's Eloquent Query Builder
  • Designed with modern PHP best practices and 100% test coverage
  • Built with love for the PHP community

Support

For bugs and feature requests, please use the GitHub issues.

Roadmap

Completed ✅

  • Core query building functionality
  • Multi-database support (MySQL, PostgreSQL, SQLite)
  • Transaction support
  • Prepared statement caching
  • Query result caching
  • Connection pooling
  • Performance profiling
  • PSR-3 logging integration
  • Slow query detection
  • Memory-efficient streaming (cursor/chunk)
  • CLI tools for testing and query building
  • Comprehensive test suite (1644 tests)
  • Performance benchmarks

v3.0 - Completed ✅

  • Schema builder with fluent interface
  • Migration system with dependency resolution
  • WordPress/WooCommerce schema helpers
  • Schema inspector for reverse engineering
  • Migration event system
  • Transaction support for migrations

Planned Features

  • Database seeding system
  • Query builder macros/extensions
  • Additional database support (SQL Server, Oracle)
  • Query builder IDE helpers
  • Advanced migration features (squashing, etc.)