PHP Doctrine: How to execute raw SQL queries (3 approaches)

Updated: January 13, 2024 By: Guest Contributor Post a comment

Introduction

Doctrine is a powerful PHP ORM (Object-Relational Mapper) that provides a layer of abstraction over the database layer, representing data as objects rather than rows and columns of a table. Despite its incredible capabilities for manipulating data via object-oriented representations, there are scenarios when writing raw SQL could be unavoidable or more efficient.

Executing raw SQL queries can sometimes be the last resort for complex operations when using an ORM such as Doctrine in a PHP application. This detailed tutorial will guide you through the process of executing raw SQL within the context of Doctrine, enabling you to leverage powerful database features when necessary and maintain the flexibility that raw SQL can provide, all while keeping the benefits an ORM has to offer.

Understanding Doctrine’s Architecture

Doctrine consists of two main parts: the ORM layer and the DBAL (Database Abstraction Layer). While the ORM works with entities and repositories, the DBAL underneath provides database access and functions, including the execution of raw SQL. This guide will focus on using the DBAL for executing raw SQL.

Setting Up Your Environment

Firstly, ensure that you have a development environment with PHP and Composer installed. If you’re working on a new project, the following steps will help you get started with Doctrine:

1. Install the Doctrine ORM package using Composer:

composer require doctrine/orm

2. Configure the database connection settings in your bootstrap or configuration file. The connection array includes the database driver, the path to your database, and other pertinent settings:

<?php
// bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;

require_once "vendor/autoload.php";

// Create a simple "default" Doctrine ORM configuration for Annotations
$isDevMode = true;

$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode);
// database configuration parameters
$conn = array(
    'driver'   => 'pdo_mysql',
    'user'     => 'your_db_username',
    'password' => 'your_db_password',
    'dbname'   => 'your_db_name',
);

// obtaining the entity manager
$entityManager = EntityManager::create($conn, $config);
?>

3 Approaches to Executing Raw SQL Queries

Now that you have your environment set up, let’s move on to executing raw SQL queries through the Doctrine DBAL. There are primarily three ways to achieve this:

Using the Connection’s executeQuery method

The DBAL’s Connection class provides an executeQuery method that can be used to execute raw SQL queries that don’t require parameters. Here’s an example of its usage:

<?php
$query = 'SELECT * FROM users';
$stmt = $entityManager->getConnection()->executeQuery($query);
while ($row = $stmt->fetch()) {
    echo $row['username'] . "\n";
}
?>

Using the Connection’s prepare and execute methods

For queries that need parameters, use the prepare method, which prevents SQL injection via placeholders and then calls the execute method with an array of parameters. For example:

<?php
$query = 'SELECT * FROM users WHERE id = :id';
$stmt = $entityManager->getConnection()->prepare($query);
$stmt->bindValue('id', $userId);
$stmt->execute();
while ($row = $stmt->fetch()) {
    echo $row['username'] . "\n";
}
?>

Using the QueryBuilder for complex dynamic queries

Sometimes you need to create more dynamic queries. Doctrine’s QueryBuilder comes in handy here, providing a way to programmatically build SQL queries. Although this might not count as a raw SQL query, it provides the flexibility of raw queries with the safety of Doctrine’s abstraction. For instance:

<?php
$queryBuilder = $entityManager->getConnection()->createQueryBuilder();
$result = $queryBuilder
    ->select('*')
    ->from('users')
    ->where('id = :id')
    ->setParameter('id', $userId)
    ->execute()
    ->fetchAll();
?>

Considerations and Best Practices

While using raw SQL can solve certain issues, it’s important to consider the following:

  • SQL Injection: Always use prepared statements or the QueryBuilder to safeguard against SQL injection attacks.
  • Database Portability: Writing raw SQL might lock you to a specific database vendor unless you use standard SQL that is compatible across different databases.
  • Performance: Make sure that the performance gains you anticipate actually materialize. The Doctrine ORM is quite efficient and refactoring to raw SQL doesn’t always lead to better performance.

Conclusion

Doctrine’s flexible architecture allows developers to use raw SQL queries when necessary. It ensures that while you enjoy the benefits of working with an ORM, you’re not constrained by it. Use the methods of the DBAL’s Connection class for simple queries, or opt for the QueryBuilder when you require complex, dynamic queries. Just remember to always account for security and maintain the database-agnostic nature of your application as much as possible.