Convert raw SQL queries to clean, production-ready CakePHP Query Builder code with just a few clicks!
Navigate to /test-helper/query-builder in your browser to access the converter.
- SELECT - Full support with all clauses
- INSERT - Single and bulk (multiple rows)
- UPDATE - Single and multi-table with JOINs
- DELETE - With complex WHERE conditions
CONCAT()→$query->func()->concat([])SUBSTRING()/SUBSTR()→$query->func()->substring([])TRIM(),LTRIM(),RTRIM()→$query->func()->trim()UPPER()/LOWER()→$query->func()->upper()/lower()COALESCE()→$query->func()->coalesce([])LENGTH()→$query->func()->length()REPLACE()→ Guidance provided
NOW(),CURDATE(),CURTIME()→$query->func()->now()YEAR(),MONTH(),DAY()→$query->func()->year()etc.DATEDIFF()→$query->func()->dateDiff([])DATE_FORMAT()→ Guidance for FrozenTimeDATE_ADD(),DATE_SUB()→ Guidance provided
COUNT(),SUM(),AVG(),MIN(),MAX()→$query->func()->GROUP_CONCAT()→$query->func()->groupConcat()
Converts SQL CASE/WHEN/THEN/ELSE to QueryExpression:
SQL:
CASE
WHEN status = 1 THEN 'Active'
WHEN status = 2 THEN 'Pending'
ELSE 'Inactive'
END AS status_labelGenerated:
'status_label' => $query->newExpr()->case()
->when(['status' => 1])
->then('Active')
->when(['status' => 2])
->then('Pending')
->else('Inactive')Recursively parses and generates subquery code:
SQL:
SELECT * FROM users
WHERE id IN (SELECT user_id FROM active_sessions)Generated:
$subquery1 = $this->find()
->select(['user_id'])
->from('active_sessions');
$query = $this->find()
->where(['id IN' => $subquery1]);Detects and provides guidance (limited CakePHP 5.x support):
ROW_NUMBER()OVER (...)RANK(),DENSE_RANK()PARTITION BY,ORDER BYLAG(),LEAD(),FIRST_VALUE(),LAST_VALUE()
Parses WITH clauses and suggests alternatives:
- Subquery alternatives
- Raw SQL option
- Separate query approach
Handles all JOIN types with complex conditions:
INNER JOIN→->innerJoin()LEFT JOIN→->leftJoin()RIGHT JOIN→->rightJoin()- Multiple JOINs with table aliases
- Complex ON conditions with AND/OR
Example:
LEFT JOIN posts AS p ON p.user_id = u.id AND p.status = 'published'Generates:
->leftJoin(['p' => 'posts'], 'p.user_id = u.id AND p.status = \'published\'')Detects UPDATE queries with JOINs and provides transaction-based approach:
SQL:
UPDATE users u
JOIN profiles p ON p.user_id = u.id
SET u.last_login = NOW(), p.updated = NOW()
WHERE u.id = 5Generated:
$this->getConnection()->transactional(function () {
$this->UsersTable->updateAll([...], [...]);
$this->ProfilesTable->updateAll([...], [...]);
});- SQL Input - Color-coded SQL syntax with Prism.js
- PHP Output - Highlighted CakePHP code with line numbers
- Examples - All examples syntax highlighted
Results Tabs:
- Generated Code - The converted CakePHP code
- Debug (Parsed Structure) - Internal parser output for troubleshooting
Example Tabs:
- SELECT - 8 examples from simple to complex
- INSERT - 3 examples including bulk
- UPDATE - 4 examples including CASE and multi-table
- DELETE - 3 examples with complex WHERE
- Advanced - 4 examples (window functions, CTEs, nested subqueries)
Each example includes:
- SQL code with syntax highlighting
- "Try It" button - Loads SQL into the converter
- Collapsible sections for cleaner UI
- Copy your SQL query from your application
- Paste into the SQL textarea
- Click "Convert to CakePHP"
- Review the generated code
- Copy to clipboard and paste into your Table class
- Browse the tabbed examples
- Click "Try It" on an example similar to your use case
- See how SQL patterns translate to CakePHP
- Modify the SQL and re-convert to experiment
- Find raw SQL queries in your legacy application
- Convert them one-by-one using the tool
- Review generated code for best practices
- Test in your application
- Refactor further as needed
✅ Good for:
- Quick prototyping
- Learning Query Builder syntax
- Converting legacy SQL
- Understanding complex query patterns
- Starting point for refactoring
- Multi-table queries (consider associations)
- Complex expressions (test thoroughly)
- Performance-critical queries (may need optimization)
The generator suggests association alternatives when appropriate:
// Manual join (generated)
->leftJoin(['Posts' => 'posts'], 'posts.user_id = users.id')
// TIP: If you have a Posts association, consider using:
// ->contain(['Posts']) or ->leftJoinWith('Posts')The converter automatically normalizes table aliases:
SQL:
SELECT authors.id AS Authors__id, authors.name
FROM authors AS AuthorsGenerated (simplified):
->select(['Authors.id', 'Authors.name'])
// TIP: ORM-style aliases detected. Consider using:
// ->contain() or ->leftJoinWith() for automatic aliasing- Focus: MySQL syntax
- Future: PostgreSQL, SQLite support planned (Phase 5)
- Multi-table DELETE (CakePHP limitation)
- Some vendor-specific functions
- Very complex nested expressions (may need manual adjustment)
- User-defined functions (UDFs)
- Window functions (limited CakePHP 5.x native support)
- CTEs (suggests subquery alternatives)
- Some date/time functions (provides guidance)
- Check SQL syntax is valid
- Ensure query type is supported (SELECT, INSERT, UPDATE, DELETE)
- Review error messages if any
- Use the Debug tab to see parsed structure
- Check for SQL syntax quirks (comments, special characters)
- Try simplifying the query first
- Break into smaller parts
- Convert step-by-step
- Use Debug tab to understand parsing
- Manually combine generated code
SQL:
SELECT
CONCAT(first_name, ' ', last_name) AS full_name,
UPPER(email) AS email_upper,
YEAR(created) AS signup_year
FROM users
WHERE active = 1
ORDER BY created DESC
LIMIT 10Generated:
$query = $this->find()
->select(function (\Cake\ORM\Query\SelectQuery $query) {
return [
'full_name' => $query->func()->concat(['first_name', "' '", 'last_name']),
'email_upper' => $query->func()->upper('email'),
'signup_year' => $query->func()->year(['created'])
];
})
->where(['active' => 1])
->orderBy(['created' => 'DESC'])
->limit(10);SQL:
SELECT
u.id,
u.name,
COUNT(p.id) AS post_count,
COALESCE(SUM(p.views), 0) AS total_views
FROM users u
LEFT JOIN posts p ON p.user_id = u.id
WHERE u.active = 1
GROUP BY u.id
HAVING COUNT(p.id) > 5
ORDER BY total_views DESCGenerated:
$query = $this->find()
->select(function (\Cake\ORM\Query\SelectQuery $query) {
return [
'u.id',
'u.name',
'post_count' => $query->func()->count('p.id'),
'total_views' => $query->func()->coalesce([
$query->func()->sum('p.views'),
0
])
];
})
->leftJoin(['p' => 'posts'], 'p.user_id = u.id')
->where(['u.active' => 1])
->groupBy(['u.id'])
->having(['COUNT(p.id) >' => 5])
->orderBy(['total_views' => 'DESC']);SQL:
SELECT id, name, 'user' AS type FROM users WHERE active = 1
UNION
SELECT id, name, 'admin' AS type FROM admins WHERE active = 1
ORDER BY nameGenerated:
$query1 = $this->find()
->select(['id', 'name', 'type' => '\'user\''])
->where(['active' => 1]);
$query2 = $this->find()
->select(['id', 'name', 'type' => '\'admin\''])
->from('admins')
->where(['active' => 1]);
$query = $query1->union($query2)
->orderBy(['name' => 'ASC']);Automatically handles nested functions:
SELECT COALESCE(SUM(amount), 0) AS totalGenerates:
'total' => $query->func()->coalesce([
$query->func()->sum('amount'),
0
])Properly handles CASE across multiple lines:
SELECT
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 80 THEN 'B'
WHEN score >= 70 THEN 'C'
ELSE 'F'
END AS grade
FROM studentsGenerates both entity-based and query-based approaches:
INSERT INTO users (username, email) VALUES
('alice', 'alice@example.com'),
('bob', 'bob@example.com')Provides saveMany() and ->values() options.
- SqlParser - Parses raw SQL into structured arrays
- QueryBuilderGenerator - Converts parsed structure to CakePHP code
- ConditionParser - Handles complex WHERE/HAVING conditions
- Uses
/smodifier for multiline expressions (CASE, etc.) - Uses
/ifor case-insensitive matching - Handles nested parentheses and quotes
Automatically detects field types:
column- Simple field referenceaggregate- COUNT, SUM, etc.string_func- CONCAT, TRIM, etc.date_func- NOW, YEAR, etc.case- CASE expressionsmath- Mathematical expressionswindow_func- Window functions
- CakePHP Query Builder Docs - Official documentation
Found a bug or have a feature request? The SQL converter is actively maintained!
Key areas for contribution:
- Database dialect support (PostgreSQL, SQLite)
- Additional SQL functions
- UI improvements
- Test coverage for edge cases

