brybry / sql-practice
A secure PHP package for SQL learning and practice with Laravel and SQLite integration.
Requires
- php: ^7.4|^8.0
- ext-pdo: *
Requires (Dev)
- phpunit/phpunit: ^9.0|^10.0
This package is auto-updated.
Last update: 2025-09-15 13:23:42 UTC
README
A secure PHP package for SQL learning and practice with Laravel and SQLite integration. Perfect for educational environments where students need to practice SQL queries safely.
๏ฟฝ NEW: Zero-Configuration Installation
Routes and web interface are now included automatically! No manual setup required.
composer require brybry/sql-practice
Then visit: http://yourapp.com/sql-practice
- That's it!
๏ฟฝ๐ Security Features
- SQL Injection Prevention: All queries use parameterized statements
- Input Sanitization: Table and column names are properly escaped
- Operation Restrictions: Configurable allowed operations
- Safe Environment: Isolated practice database separate from production
๐ฆ Installation
Install via Composer:
composer require brybry/sql-practice
For Laravel projects, the service provider will be automatically registered.
๐ Laravel Integration
Quick Setup for SQL Practice
-
Publish the configuration:
php artisan vendor:publish --provider="SqlCommands\Providers\SqlCommandsServiceProvider" --tag="config"
-
Create a practice database:
touch database/practice.sqlite
-
Configure database connection in
config/database.php
:'practice' => [ 'driver' => 'sqlite', 'database' => database_path('practice.sqlite'), 'prefix' => '', 'foreign_key_constraints' => true, ],
-
Initialize sample data:
use SqlCommands\SqlSimulator; $simulator = new SqlSimulator(database_path('practice.sqlite')); $simulator->createSampleTables(); $simulator->insertSampleData();
๐ป Usage Examples
Basic Query Building (Secure)
use SqlCommands\SqlCommands; // All methods return ['sql' => $query, 'params' => $parameters] // SELECT with WHERE conditions $query = SqlCommands::select('users', ['id', 'name'], [ 'where' => ['status' => 'active', 'age' => [18, 25, 30]], // IN clause 'orderBy' => 'name', 'limit' => 10 ]); // Result: ['sql' => 'SELECT `id`, `name` FROM `users` WHERE `status` = ? AND `age` IN (?, ?, ?) ORDER BY `name` LIMIT 10', 'params' => ['active', 18, 25, 30]] // Secure INSERT $query = SqlCommands::insert('users', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'age' => 28 ]); // Secure UPDATE with WHERE conditions $query = SqlCommands::update('users', ['status' => 'inactive'], ['id' => 123] ); // Safe DELETE with conditions $query = SqlCommands::delete('users', ['status' => 'spam']);
Advanced Queries
// JOINs $query = SqlCommands::leftJoin('users', 'orders', 'users.id = orders.user_id', ['users.name', 'orders.total'] ); // Aggregates $countQuery = "SELECT " . SqlCommands::count('id') . " FROM users"; $sumQuery = "SELECT " . SqlCommands::sum('total') . " FROM orders"; // BETWEEN with parameters $between = SqlCommands::between('created_at', '2024-01-01', '2024-12-31'); $query = SqlCommands::select('orders', ['*'], [ 'where' => [$between['expression'] => $between['params']] ]);
SQLite Functions (200+ Available)
The package includes comprehensive support for all major SQLite functions plus advanced educational features:
// ๐ค String Functions SqlCommands::upper('name'); // UPPER(`name`) SqlCommands::lower('email'); // LOWER(`email`) SqlCommands::length('description'); // LENGTH(`description`) SqlCommands::trim('text', ' .'); // TRIM(`text`, ' .') SqlCommands::substr('phone', 1, 3); // SUBSTR(`phone`, 1, 3) SqlCommands::replace('text', 'old', 'new'); // REPLACE(`text`, 'old', 'new') SqlCommands::printf('User: %s (ID: %d)', 'John', 123); // ๐ NEW: Educational Functions for Teaching Advanced SQL SqlCommands::caseWhen([ 'age < 18' => 'Minor', 'age < 65' => 'Adult' ], 'Senior'); // CASE/WHEN conditional logic SqlCommands::coalesce('mobile', 'home', 'email', 'N/A'); // NULL handling SqlCommands::like('name', 'John%'); // Pattern matching with params SqlCommands::exists($subquery); // Subquery existence checks // CTE (Common Table Expressions) $withQuery = SqlCommands::with(['active_users' => $cte], $mainQuery); // PIVOT simulation for data analysis $pivot = SqlCommands::pivot('sales', 'quarter', ['Q1', 'Q2', 'Q3', 'Q4']); // Database constraints for teaching design SqlCommands::foreignKey('user_id', 'users', 'id', 'CASCADE'); SqlCommands::check('age >= 0 AND age <= 150'); // ๐ข Math & Numeric Functions SqlCommands::abs('balance'); // ABS(`balance`) SqlCommands::round('price', 2); // ROUND(`price`, 2) SqlCommands::sqrt('area'); // SQRT(`area`) SqlCommands::pow('base', 'exponent'); // POW(`base`, `exponent`) SqlCommands::random(); // RANDOM() // ๐ Date & Time Functions SqlCommands::date('now'); // date('now') SqlCommands::datetime('now', '+1 day'); // datetime('now', '+1 day') SqlCommands::strftime('%Y-%m-%d', 'now'); // strftime('%Y-%m-%d', 'now') // ๐ Enhanced Aggregates SqlCommands::total('amount'); // TOTAL(`amount`) - float sum SqlCommands::groupConcat('tags', ', '); // GROUP_CONCAT(`tags`, ', ') // ๐ช Window Functions (SQLite 3.25+) SqlCommands::rowNumber(); // ROW_NUMBER() SqlCommands::rank(); // RANK() SqlCommands::lag('salary', 1); // LAG(`salary`, 1) SqlCommands::lead('price', 1, '0'); // LEAD(`price`, 1, '0') // ๐๏ธ JSON Functions (json1 extension) SqlCommands::jsonExtract('{"name": "John"}', '$.name'); SqlCommands::jsonArray('item1', 'item2'); // json_array('item1', 'item2') SqlCommands::jsonObject('key', 'value'); // json_object('key', 'value') // ๐ง Utility Functions SqlCommands::iif('age >= 18', 'adult', 'minor'); // IIF(age >= 18, 'adult', 'minor') SqlCommands::changes(); // CHANGES() SqlCommands::lastInsertRowid(); // LAST_INSERT_ROWID() // Real-world educational example $query = SqlCommands::select('employees', [ 'name', SqlCommands::upper('department') . ' as dept_upper', SqlCommands::caseWhen([ 'salary < 30000' => 'Entry', 'salary < 60000' => 'Mid', 'salary >= 60000' => 'Senior' ], 'Executive') . ' as level', SqlCommands::coalesce('bonus', '0') . ' as bonus_amount', SqlCommands::round('salary * 1.1', 2) . ' as salary_with_raise' ], [ 'where' => ['active' => 1], 'orderBy' => 'salary' ]);
๐ Perfect for Teaching:
- Conditional Logic - CASE/WHEN statements for business rules
- NULL Handling - COALESCE, NULLIF for data quality
- Pattern Matching - LIKE, GLOB, REGEXP for search functionality
- Advanced Queries - Subqueries, CTEs, window functions
- Data Analysis - PIVOT simulation, aggregations
- Database Design - Constraints, indexes, relationships
๐ Complete SQLite Functions Reference โ - Detailed documentation with 200+ functions, examples, and usage patterns.
### Using the Simulator
```php
use SqlCommands\SqlSimulator;
$simulator = new SqlSimulator(database_path('practice.sqlite'));
// Execute queries safely
$selectQuery = SqlCommands::select('users', ['name', 'email']);
$result = $simulator->executeQuery($selectQuery);
if ($result['success']) {
foreach ($result['data'] as $row) {
echo $row['name'] . ': ' . $row['email'] . "\n";
}
} else {
echo "Error: " . $result['error'];
}
// Get database schema for learning
$schema = $simulator->getSchema();
print_r($schema);
Laravel Controller Example
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use SqlCommands\SqlCommands; use SqlCommands\SqlSimulator; class SqlPracticeController extends Controller { private $simulator; public function __construct() { $this->simulator = new SqlSimulator(database_path('practice.sqlite')); } public function executeQuery(Request $request) { $request->validate([ 'table' => 'required|string|max:50', 'operation' => 'required|in:select,insert,update,delete', 'data' => 'array' ]); try { switch ($request->operation) { case 'select': $query = SqlCommands::select($request->table, $request->columns ?? ['*']); break; case 'insert': $query = SqlCommands::insert($request->table, $request->data); break; // ... other operations } $result = $this->simulator->executeQuery($query); return response()->json($result); } catch (\Exception $e) { return response()->json(['error' => $e->getMessage()], 400); } } public function getSchema() { return response()->json($this->simulator->getSchema()); } } }
๐ฑ Mobile-Friendly Features
- Lightweight SQLite: Perfect for mobile web applications
- Responsive Design: Built-in web interface adapts to mobile screens
- Offline Capability: SQLite works without network connectivity
- Touch-Friendly: Optimized for touch interactions on tablets/phones
๐ Available Methods
Query Builders (All return ['sql' => $query, 'params' => $parameters]
)
Method | Description | Example |
---|---|---|
select($table, $columns, $options) |
SELECT with WHERE, ORDER BY, LIMIT | select('users', ['name'], ['where' => ['active' => 1]]) |
insert($table, $data) |
Parameterized INSERT | insert('users', ['name' => 'John']) |
update($table, $data, $where) |
Parameterized UPDATE | update('users', ['name' => 'Jane'], ['id' => 1]) |
delete($table, $where) |
Parameterized DELETE | delete('users', ['status' => 'inactive']) |
createTable($table, $columns) |
CREATE TABLE | createTable('users', ['id' => 'INTEGER PRIMARY KEY']) |
dropTable($table, $ifExists) |
DROP TABLE | dropTable('temp_table', true) |
truncateTable($table) |
TRUNCATE/DELETE ALL | truncateTable('logs') |
JOIN Operations
innerJoin($table1, $table2, $on, $columns)
leftJoin($table1, $table2, $on, $columns)
crossJoin($table1, $table2, $columns)
rightJoin()
- MySQL only, throws exception in SQLite
Aggregate Functions
count($column)
- COUNT functionsum($column)
- SUM functionavg($column)
- AVG functionmin($column)
- MIN functionmax($column)
- MAX function
String Functions
length($column)
- LENGTH functionsubstring($column, $start, $length)
- SUBSTRING/SUBSTRconcat(...$args)
- Database-specific concatenation
Utility Functions
groupBy($columns)
- GROUP BY clausehaving($condition)
- HAVING clausebetween($column, $start, $end)
- BETWEEN expression with parametersshowTables()
- Database-specific table listingdescribeTable($table)
- Database-specific table structure
๐ง Database Compatibility
Feature | SQLite | MySQL | PostgreSQL |
---|---|---|---|
Basic CRUD | โ | โ | โ |
JOINs (except RIGHT) | โ | โ | โ |
RIGHT JOIN | โ | โ | โ |
TRUNCATE | โ* | โ | โ |
SHOW TABLES | โ* | โ | โ* |
String Functions | โ | โ | โ |
*Automatically converted to compatible alternatives
๐งช Testing
Run the test suite:
php test_improved.php
For PHPUnit tests (requires dev dependencies):
composer install --dev vendor/bin/phpunit tests/
โ Configuration Options
The config/sql-commands.php
file provides these options:
return [ 'practice_database_path' => database_path('practice.sqlite'), 'allowed_operations' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP', 'ALTER'], 'auto_create_samples' => true, 'max_execution_time' => 30, 'enable_web_interface' => true, 'route_prefix' => 'sql-practice', 'middleware' => ['web'], ];
๐จ Security Notes
- Always use parameterized queries - Never concatenate user input directly into SQL
- Validate user input - Check table/column names against allowed lists
- Use separate database - Keep practice database isolated from production
- Limit operations - Configure allowed operations in config file
- Set execution timeouts - Prevent runaway queries in learning environment
๐ Educational Use Cases
- SQL Learning Platforms: Safe environment for students to practice
- Database Design Courses: Create and modify table structures
- Query Optimization Training: Analyze query performance
- Mobile Learning Apps: Offline SQL practice on tablets/phones
- Coding Bootcamps: Hands-on SQL exercises
- Self-Paced Learning: Individual practice with immediate feedback
๐ค Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
๐ License
MIT License - see LICENSE file for details.
๐ Links
Made with โค๏ธ for SQL education and Laravel developers by Bryan Rondinab