For a recent project, we had to connect and query a Microsoft SQL database from a Symfony project. Since we use Docker to run the apps in separate containers, we added a new container to simulate the MSSQL database.
We added the following code to our docker-compose.yml
file in the root of our Symfony project:
version: '2'
services:
mssql:
image: 'microsoft/mssql-server-linux:latest'
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=<password>
ports:
- '1433:1433'
volumes:
- mssqldata:/var/opt/mssql/data
mssql-setup:
build: ./mssql-setup
image: 'activelamp/iamucla-mssql-setup'
environment:
- SA_PASSWORD=<password>
links:
- mssql:api-mssql
volumes:
mssqldata: {}
In the mssql-setup
folder in the root of our Symfony project, we have another Dockerfile
with the following contents to set up the database:
FROM ubuntu:16.04
# Install update instance
RUN apt-get -y update \
&& apt-get install curl -y \
&& apt-get install apt-transport-https \
&& apt-get install -y locales \
&& echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \
&& locale-gen \
&& curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
&& curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | tee /etc/apt/sources.list.d/msprod.list \
&& apt-get -y update \
&& ACCEPT_EULA=Y apt-get install -y mssql-tools unixodbc-dev
# Create sqlsetup directory
RUN mkdir -p /usr/src/sqlsetup
WORKDIR /usr/src/sqlsetup
# Bundle sqlsetup source
COPY . /usr/src/sqlsetup
# Grant permissions for the import-data script to be executable
RUN chmod +x /usr/src/sqlsetup/setup-db.sh
CMD /bin/bash ./setup-db.sh
As you can see in the above code, we execute a script called setup-db.sh
. This imports the setup.sql
file for creating the needed tables (both files are also located in the mssql-setup
folder):
#wait for the SQL Server to come up
sleep 30s
#run the setup script to create the DB and the schema in the DB
/opt/mssql-tools/bin/sqlcmd -S api-mssql -U sa -P $SA_PASSWORD -d master -i setup.sql
In the beginning of this project, we started off using the LeaseWeb DoctrinePdoDblib driver but quickly noticed that this driver wasn't returning the expected error messages coming from MSSQL and it also did not return any message strings returned from custom Stored Procedures. That's when we made the switch to use the official Microsoft SQL drivers for PHP.
But this required some changes to our root Dockerfile
so we could install these drivers in our Symfony container.
FROM php:7.1.16-apache
RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \
&& chmod ugo+x /usr/local/bin/composer
.....
RUN apt-get update && apt-get install -y \
vim \
git \
unzip \
wget \
curl \
libmcrypt-dev \
libcurl4-openssl-dev \
mysql-client \
nodejs \
libxml2-dev \
libldb-dev libldap2-dev \
build-essential \
freetds-bin \
freetds-dev \
apt-transport-https
#Microsoft Drivers for PHP for SQL Server: https://github.com/Microsoft/msphpsql
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
&& curl https://packages.microsoft.com/config/debian/8/prod.list > /etc/apt/sources.list.d/mssql-release.list \
&& apt-get install -y locales \
&& echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \
&& locale-gen
RUN apt-get -y update
RUN ACCEPT_EULA=Y apt-get install -y msodbcsql
RUN apt-get install -y unixodbc-dev
RUN pear config-set php_ini `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` system
RUN pecl install sqlsrv
RUN pecl install pdo_sqlsrv
RUN echo "extension=sqlsrv.so\nextension=pdo_sqlsrv.so" > /usr/local/etc/php/conf.d/symfony.ini
.....
As you can see in the above snippet, we had to install the sqlsrv
and pdo_sqlsrv
drivers and enable these extensions in our symfony.ini
file.
The next step was to define the connection in our Symfony config.yml
file as follows:
# Doctrine Configuration
doctrine:
dbal:
connections:
mssql:
driver: pdo_sqlsrv
host: '%mssql_database_host%'
port: '%mssql_database_port%'
dbname: '%mssql_database_name%'
user: '%mssql_database_user%'
password: '%mssql_database_password%'
charset: UTF8
wrapper_class: AppBundle\Connections\ConnectionMSSQL
As you can see, we defined a wrapper_class in our connection, this ConnectionMSSQL
class extends the Doctrine\DBAL\Connection
. In this ConnectionMSSQL
class, you can write all your functions to query the DB, e.g.:
public function findUserById($id)
{
$sql = 'SELECT * FROM my_users
WHERE UPPER(id) = UPPER(:id)';
return $this->fetchAll(
$sql, [
'id' => strtoupper($id),
]
);
}