realestateconz/mssql-bundle
realestateconz/mssql-bundle is a Symfony bundle for Microsoft SQL Server integration, providing service configuration and DBAL/PDO wiring so applications can connect to MSSQL databases through Symfony's standard database tooling. It helps projects use MSSQL alongside Doctrine.
This is a Symfony 2-era bundle that exists to wire the Microsoft SQL Server PDO drivers (pdo_sqlsrv / sqlsrv) into Doctrine. It is thin — realistically a DI extension plus a DBAL driver class — and it is abandoned: Symfony 2 is EOL, there are zero dependents, and the last meaningful activity predates Doctrine DBAL 2.5 shipping its own pdo_sqlsrv driver. Treat it as a reference implementation rather than a long-term dependency.
Minimum steps:
msodbcsql17 on Debian/Ubuntu, MSI on Windows.pecl install sqlsrv pdo_sqlsrv (match the version to your PHP; PHP 7.0–7.4 era only).php -m | grep -i sqlsrv and php -r 'var_dump(new PDO("sqlsrv:Server=host;Database=db", $u, $p));' before touching Symfony.composer require realestateconz/mssql-bundle (expect constraint conflicts on modern PHP — you are almost certainly on PHP 5.6/7.x and Symfony 2.8/3.4).AppKernel::registerBundles() — namespace will be something like new RealEstateNZ\MssqlBundle\MssqlBundle(); confirm against the repo's composer.json autoload.psr-4 block.doctrine.dbal at it: set driver_class to the bundle's driver FQCN (look under Driver/ in the repo), or simply driver: pdo_sqlsrv on DBAL ≥ 2.5 and skip the bundle entirely.First use case: a Symfony app that already talks to MySQL/Postgres and needs to read from a legacy SQL Server estate — reporting tables, ERP views, a CRM database. Start read-only. Do not attempt to make this your primary write database on day one.
Where to look first: the bundle's Driver/ folder and its DependencyInjection/Configuration.php. Those two files tell you everything the bundle actually does; if they are ~50 lines combined, you can inline the config and drop the dependency.
Two-connection, one-app pattern. The dominant real-world shape is a primary Doctrine connection plus a secondary SQL Server connection:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
host: '%env(DB_HOST)%'
# ...
mssql:
driver: pdo_sqlsrv # or the bundle's driver_class
host: '%env(MSSQL_HOST)%'
port: 1433
dbname: '%env(MSSQL_DB)%'
user: '%env(MSSQL_USER)%'
password: '%env(MSSQL_PASSWORD)%'
charset: UTF-8
options:
# driver-specific; verify against sqlsrv docs for your version
LoginTimeout: 5
Then reach it explicitly and never let it drift into the default EM:
$conn = $this->get('doctrine.dbal.mssql_connection');
$rows = $conn->fetchAllAssociative('SELECT TOP 100 * FROM dbo.Orders WHERE Status = ?', ['OPEN']);
Keep the ORM off SQL Server. Map SQL Server tables as plain DBAL queries or, at most, as a separate entity manager with mappings pointed at a read-only bundle. Doctrine's SchemaTool diffing against SQL Server is unreliable (see Gotchas), and ORM writes will fight identity/OUTPUT semantics. A dedicated ReportRepository that takes Connection in its constructor is the pattern that survives:
final class OrderReportRepository
{
public function __construct(private Connection $mssql) {}
public function openOrders(int $limit): array
{
return $this->mssql->fetchAllAssociative(
sprintf('SELECT TOP %d OrderId, Customer, Total FROM dbo.Orders WHERE Status = :s',
$limit),
['s' => 'OPEN']
);
}
}
Read-only views are your integration contract. Have the SQL Server DBA expose stable dbo.vw_* views; map those, not base tables. It decouples you from their schema churn and lets you grant the Symfony user db_datareader only.
Batch/pagination across dialects. doctrine/orm's paginator emits LIMIT/OFFSET, which SQL Server only understands as OFFSET ... FETCH from 2012 onward and which DBAL's platform may not translate correctly on the older DBAL this bundle targets. Write pagination by hand with ROW_NUMBER() or OFFSET/FETCH in raw SQL.
Integration testing. Run the SQL Server connection against a real instance (Docker mcr.microsoft.com/mssql/server:2019-latest) in CI — SQLite/mock connections hide every one of the type and collation bugs below. Guard integration tests with a group so unit runs stay fast.
driver: pdo_sqlsrv is built in. The bundle's only value is its driver_class; copying that one class into your own src/Doctrine/ removes an abandoned dependency and the Symfony 2 coupling. Check the repo's Driver/*.php before deciding — if it just extends Doctrine\DBAL\Driver\PDOSqlsrv\Driver, delete the dependency.pdo_sqlsrv compiled against one ODBC major version silently fails against another. Pin the PECL extension and the MS ODBC package together in your Dockerfile, and fail fast at container boot with a php -r 'new PDO(...)' healthcheck.SET NOCOUNT ON. Stored procedures that don't set it return extra row-count result sets, and pdo_sqlsrv surfaces confusing "no results" symptoms. Add it at the top of every proc you call.nvarchar; PHP strings bind as varchar unless told otherwise, mangling non-ASCII. Pass PDO::SQLSRV_ENCODING_UTF8 in the connection options, and never build literals — always bind parameters.[dbo].[Orders]) vs Doctrine's double quotes. If you use quoteIdentifier(), confirm the platform in play is the SQL Server one, not the default.SQL_Latin1_General_CP1_CS_AS database makes WHERE Code = 'abc' miss 'ABC'. This bites hardest when the app was prototyped against a case-insensitive dev DB.DATETIME vs DATETIME2 and PHP's timezone. datetime has 3.33ms rounding and no timezone; converting to \DateTime in PHP then serializing will drift. Keep temporal values as strings at the DBAL boundary when precision matters.doctrine:schema:validate / migrations:diff against SQL Server produces false positives (indexes, defaults, nvarchar(max)). Manage DDL by hand in versioned .sql files and treat the DB as externally owned.doctrine.dbal.logging: true in dev and watch the profiler; if queries don't appear, the secondary connection isn't being wrapped by the data collector — log manually via $conn->getConfiguration()->setSQLLogger(...) (DBAL 2 API). When a query returns false with no exception, you're on PDO::ERRMODE_SILENT; force ERRMODE_EXCEPTION in options.Doctrine\DBAL\Driver implementation registered through doctrine.dbal.connections.*.driver_class so it stays swappable when you later migrate to sqlsrv (non-PDO) for bulk-load performance.pdo_sqlsrv needs no bundle at all. Budget the work as "delete the bundle, keep the YAML" — the config block above transfers almost verbatim.How can I help you explore Laravel packages today?