A simple and easy to use, maximum performance database layer with connection to Doctrine ORM, which allows you to use all the advantages of OOP and also has full support for Nette 3.
This package automatically installs Doctrine to your project (also sets everything up in the configuration) and runs stably.
- Full Doctrine ORM integration with Nette Framework 3.x
- Automatic configuration and setup via DI extensions
- Advanced Tracy debug panel with SQL query profiling
- Built-in UUID and UUID Binary identifier traits
- Custom DQL functions (RAND, ROUND, GEODISTANCE, MATCH AGAINST)
- Multiple caching strategies (APCu, SQLite3, Filesystem)
- Slow query logging and analysis
- Entity inheritance support with discriminator mapping utilities
- Blue Screen exception panels for detailed error debugging
+------------------+ +-------------------+ +------------------+
| Nette DI |---->| DatabaseExtension |---->| EntityManager |
| Container | | OrmExtension | | (Doctrine) |
+------------------+ | OrmAnnotations | +------------------+
+-------------------+ |
| v
+-------------------+ +------------------+
| ConnectionFactory |<----| DBAL Connection |
+-------------------+ +------------------+
| |
+-------------------+ v
| Cache Provider | +------------------+
| (APCu/SQLite/FS) | | Tracy QueryPanel |
+-------------------+ +------------------+
| Extension | Purpose |
|---|---|
DatabaseExtension |
Main extension for database connection configuration |
OrmExtension |
Doctrine ORM configuration (proxy classes, naming strategies) |
OrmAnnotationsExtension |
Entity mapping and annotation reader setup |
OrmConsoleExtension |
CLI commands integration |
DbalConsoleExtension |
DBAL CLI commands |
| Service | Description |
|---|---|
EntityManager |
Extended Doctrine EntityManager with fluent interface and error handling |
DoctrineHelper |
Utilities for entity inheritance, discriminator mapping, and type remapping |
Repository |
Enhanced base repository with findPairs() and findByConditions() methods |
ConnectionFactory |
Creates and configures DBAL connections with custom types |
| Trait | Type | Description |
|---|---|---|
Identifier |
int |
Auto-increment integer ID |
IdentifierUnsigned |
int (unsigned) |
Auto-increment unsigned integer ID |
UuidIdentifier |
string |
UUID v4 stored as string (36 chars) |
UuidBinaryIdentifier |
binary |
UUID v4 stored as binary (16 bytes) for better performance |
| Function | Description |
|---|---|
RAND() |
Random number generation |
ROUND(value, precision) |
Number rounding |
GEODISTANCE(lat1, lng1, lat2, lng2) |
Geographic distance calculation in km |
MATCH(column) AGAINST(value) |
MySQL fulltext search |
| Provider | Best For |
|---|---|
ApcuCache |
Production with APCu extension |
SQLite3Cache |
Production without APCu |
FilesystemCache |
Development/fallback |
ArrayCache |
Testing |
It's best to use Composer for installation, and you can also find the package on Packagist and GitHub.
To install, simply use the command:
$ composer require baraja-core/doctrineYou can use the package manually by creating an instance of the internal classes, or register a DIC extension to link the services directly to the Nette Framework.
- PHP 8.0 or higher
- PDO extension
- Nette Framework 3.x
- Optional: APCu extension (recommended for production caching)
- Optional: SQLite3 extension (fallback caching)
A model configuration can be found in the common.neon file inside the root of the package.
In the project's common.neon you have to define the database credentials using the baraja.database extension:
baraja.database:
connection:
host: 127.0.0.1
dbname: sandbox
user: root
password: rootThe following connection options are available:
| Option | Type | Description |
|---|---|---|
url |
string | DSN connection URL |
pdo |
string | Existing PDO instance |
memory |
string | In-memory database |
driver |
string | DBAL driver (default: pdo_mysql) |
driverClass |
string | Custom driver class |
driverOptions |
array | Driver-specific options |
unix_socket |
string | Unix socket path |
host |
string | Database host |
port |
int | Database port |
dbname |
string | Database name |
servicename |
string | Oracle service name |
user |
string | Username |
password |
string | Password |
charset |
string | Character set (default: UTF8) |
portability |
int | Portability mode |
fetchCase |
int | Fetch case mode |
persistent |
bool | Persistent connection |
types |
array | Custom DBAL types |
typesMapping |
array | Type mappings |
wrapperClass |
string | Custom connection wrapper |
serverVersion |
string | Server version hint |
The package supports the DB_URI environment variable for connection configuration:
DB_URI=mysql://user:password@host:3306/dbname
Additionally, you can use DB_NAME to override the database name from the URI.
In default settings Doctrine uses the MySql driver.
You can switch to PostgreSQL by specifying the driver class:
baraja.database:
connection:
driverClass: Doctrine\DBAL\Driver\PDO\PgSQL\Driverpdo_mysql- MySQL (default)pdo_pgsql- PostgreSQLpdo_sqlite- SQLitepdo_sqlsrv- Microsoft SQL Serverpdo_oci- Oracle
In order for Doctrine to know which classes are entities and which application logic, it is necessary to set up a mapping.
For mapping, it is necessary to set the introductory part of the namespace entities and the directory where they occur:
orm.annotations:
paths:
App\Baraja\Entity: %rootDir%/app/model/EntityYou can exclude specific directories from scanning:
orm.annotations:
excludePaths:
- %rootDir%/app/model/Entity/DeprecatedCustom annotations can be ignored:
orm.annotations:
ignore:
- myCustomAnnotationConfigure the annotation cache driver:
orm.annotations:
defaultCache: filesystem # Options: apcu, array, filesystem
debug: false # Enable for developmentImportant warning:
The value of the
%rootDir%,%appDir%,%wwwDir%,%vendorDir%and%tempDir%parameters may be corrupted when running schema generation in CLI mode. To resolve this mistake, please install Package Manager and call the command as acomposer dump.
The package provides several traits for entity identification. Insert one trait to define the ID in your entities:
<?php
declare(strict_types=1);
namespace App\Entity;
use Baraja\Doctrine\Identifier\Identifier;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Article
{
use Identifier;
#[ORM\Column(type: 'string')]
private string $title;
}<?php
declare(strict_types=1);
namespace App\Entity;
use Baraja\Doctrine\UUID\UuidIdentifier;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class User
{
use UuidIdentifier;
#[ORM\Column(type: 'string')]
private string $email;
}For better performance, use binary UUID storage:
<?php
declare(strict_types=1);
namespace App\Entity;
use Baraja\Doctrine\UUID\UuidBinaryIdentifier;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Product
{
use UuidBinaryIdentifier;
#[ORM\Column(type: 'string')]
private string $name;
}UUID will be generated automatically in PHP using the Ramsey UUID library.
TIP: Read more about UUID binary performance (Czech language)
This package implements a bridge to automatically execute Doctrine commands.
php www/index.php o:s:u -f --dump-sqlThe command o:s:u means orm:schema-tool:update.
Options:
-for--force- Execute changes in SQL--dump-sql- Renders the list of SQL commands that will be executed
php www/index.php o:v| Command | Description |
|---|---|
orm:schema-tool:create |
Create database schema |
orm:schema-tool:update |
Update database schema |
orm:schema-tool:drop |
Drop database schema |
orm:validate-schema |
Validate entity mappings |
orm:info |
Show basic information about all mapped entities |
dbal:run-sql |
Execute arbitrary SQL directly |
If everything works fine, the command will create the table core__database_slow_query which is defined in this package and is ready for logging slow queries.
TIP: If you are using Package Manager, you can simply call the
composer dumpcommand.
The package provides an enhanced EntityManager with fluent interface:
<?php
use Baraja\Doctrine\EntityManager;
class UserService
{
public function __construct(
private EntityManager $entityManager,
) {
}
public function createUser(string $email): User
{
$user = new User;
$user->setEmail($email);
$this->entityManager
->persist($user)
->flush();
return $user;
}
public function findUser(int $id): ?User
{
return $this->entityManager->find(User::class, $id);
}
}The EntityManager provides better error handling and returns $this for fluent chaining:
$entityManager
->persist($entity1)
->persist($entity2)
->flush();Add event listeners directly:
$entityManager->addEventListener(['postPersist'], new MyEventListener());The package includes an enhanced Repository class with additional methods:
Get key-value pairs for select boxes:
$repository = $entityManager->getRepository(Category::class);
// Returns ['1' => 'Electronics', '2' => 'Books', ...]
$pairs = $repository->findPairs('name');
// With custom key column
$pairs = $repository->findPairs('name', 'slug');Get values with custom conditions:
$repository->findByConditions('id', [
'e.active = 1',
'e.createdAt > :date',
]);The DoctrineHelper service provides utilities for working with entity inheritance and metadata:
<?php
use Baraja\Doctrine\DoctrineHelper;
class ProductService
{
public function __construct(
private DoctrineHelper $doctrineHelper,
) {
}
public function getProductVariants(string $productClass): array
{
// Get all discriminator variants of an entity
return $this->doctrineHelper->getEntityVariants($productClass);
}
public function getTableName(string $entityClass): string
{
// Get real database table name
return $this->doctrineHelper->getTableNameByEntity($entityClass);
}
public function upgradeProductType(Product $product): ?SpecialProduct
{
// Remap entity to a more specific type
return $this->doctrineHelper->remapEntityToBestType($product);
}
}| Method | Description |
|---|---|
getEntityVariants() |
Get all discriminator map variants |
getBestOfType() |
Get the most embedded (deepest) entity type |
getTableNameByEntity() |
Get database table name from entity class |
getRootEntityName() |
Get root entity class in inheritance chain |
getDiscriminatorByEntity() |
Get discriminator value for entity |
remapEntityToBestType() |
Elevate entity to best available type |
remapEntity() |
Remap entity from one type to another |
Generate random numbers for sorting:
$query = $entityManager->createQuery('
SELECT e FROM App\Entity\Product e
ORDER BY RAND()
');Round numeric values:
$query = $entityManager->createQuery('
SELECT e, ROUND(e.price, 2) as roundedPrice
FROM App\Entity\Product e
');Calculate geographic distance between two points (in kilometers):
$query = $entityManager->createQuery('
SELECT e, GEODISTANCE(e.latitude, e.longitude, :lat, :lng) as distance
FROM App\Entity\Store e
ORDER BY distance ASC
')
->setParameter('lat', 50.0755)
->setParameter('lng', 14.4378);MySQL fulltext search:
$query = $entityManager->createQuery('
SELECT e
FROM App\Entity\Article e
WHERE MATCH(e.title, e.content) AGAINST(:search) > 0
')
->setParameter('search', 'search term');Add your own custom functions:
use Baraja\Doctrine\DatabaseExtension;
DatabaseExtension::addCustomNumericFunction('MY_FUNC', MyFunction::class);Add custom DBAL types:
use Baraja\Doctrine\DatabaseExtension;
DatabaseExtension::addCustomType('my_type', MyType::class);baraja.database:
types:
my_type: App\Doctrine\Type\MyType| Type | Class |
|---|---|
uuid |
UuidType - UUID as string |
uuid-binary |
UuidBinaryType - UUID as binary |
The package automatically selects the best available cache:
- APCu (preferred for production)
- SQLite3 (fallback)
- Filesystem (development)
baraja.database:
cache: apcu # Options: apcu, sqlite, or custom classbaraja.database:
cache: App\Cache\MyCustomCacheThis package contains the most advanced native tools for debugging your application and SQL queries.
- View all performed SQL queries
- Click directly on the place of query invocation in IDE
- Watch time graphs on the output
- Analyze slow queries
- Write query types displayed separately (SELECT, INSERT, UPDATE, DELETE)
- Transaction highlighting
- Color-coded query duration
| Duration | Color |
|---|---|
| < 5 ms | Default |
| 5-15 ms | Light orange |
| 15-75 ms | Orange |
| 75-150 ms | Dark orange |
| 150-300 ms | Light red |
| 300-500 ms | Red |
| > 500 ms | Dark red |
The package includes advanced logic for debugging corrupted entities and queries directly through Tracy Bluescreen:
- Driver Exception - Shows SQL query, parameters, and connection troubleshooting
- Query Exception - Displays available fields when accessing non-existent columns
- Mapping Exception - Shows entity file location and annotations
Slow queries are automatically logged to the core__database_slow_query table:
| Column | Type | Description |
|---|---|---|
id |
uuid | Unique identifier |
query |
text | The SQL query |
duration |
float | Execution time in ms |
hash |
string | Query hash for grouping |
inserted_date |
datetime | When the query was logged |
When Doctrine is used poorly, it can be unnecessarily slow.
For more details (in Czech language): https://ondrej.mirtes.cz/doctrine-2-neni-pomala
This package uses best-practices to increase the performance:
- Proxy Classes -
autoGenerateProxyClassesis set tofalsefor production - Metadata Caching - Automatic APCu/SQLite3 caching
- Query Caching - DQL query parsing is cached
- Result Caching - Configurable result cache
For maximum performance, consider using Redis:
// See: https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.htmlUsing UuidBinaryIdentifier instead of UuidIdentifier provides:
- 50% less storage space (16 bytes vs 36 characters)
- Faster indexing and lookups
- Better cache efficiency
Advanced ORM settings via orm extension:
orm:
proxyDir: %tempDir%/proxies
proxyNamespace: App\Proxy
autoGenerateProxyClasses: false
namingStrategy: Doctrine\ORM\Mapping\UnderscoreNamingStrategy
defaultRepositoryClassName: App\Repository\BaseRepository
entityNamespaces:
App: App\Entity| Option | Default | Description |
|---|---|---|
configurationClass |
Configuration |
Custom configuration class |
entityManagerClass |
EntityManager |
Custom EntityManager class |
proxyDir |
%tempDir%/proxies |
Proxy classes directory |
autoGenerateProxyClasses |
null |
Auto-generate proxy classes |
proxyNamespace |
Baraja\Doctrine\Proxy |
Proxy classes namespace |
metadataDriverImpl |
null |
Custom metadata driver |
entityNamespaces |
[] |
Entity namespace aliases |
customStringFunctions |
[] |
Custom DQL string functions |
customNumericFunctions |
[] |
Custom DQL numeric functions |
customDatetimeFunctions |
[] |
Custom DQL datetime functions |
customHydrationModes |
[] |
Custom hydration modes |
classMetadataFactoryName |
null |
Custom metadata factory |
defaultRepositoryClassName |
null |
Default repository class |
namingStrategy |
UnderscoreNamingStrategy |
Column naming strategy |
quoteStrategy |
null |
Quote strategy |
entityListenerResolver |
null |
Entity listener resolver |
repositoryFactory |
null |
Custom repository factory |
defaultQueryHints |
[] |
Default query hints |
Register Doctrine event subscribers via DI:
services:
- App\Subscriber\TimestampSubscriber:
tags: [doctrine.subscriber]<?php
namespace App\Subscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Events;
class TimestampSubscriber implements EventSubscriber
{
public function getSubscribedEvents(): array
{
return [Events::prePersist, Events::preUpdate];
}
public function prePersist($args): void
{
// Set created timestamp
}
public function preUpdate($args): void
{
// Set updated timestamp
}
}Configure globally ignored annotation names:
baraja.database:
propertyIgnoreAnnotations:
- sample
- endpointName
- editable
- myCustomAnnotationVerify your database is running and connection details are correct:
- Check
localhostvs127.0.0.1 - Verify port number (MySQL default: 3306)
- Check firewall settings
For MySQL 8.0+, you may need to change the authentication method:
ALTER USER 'myuser' IDENTIFIED WITH mysql_native_password BY 'mypassword';When using DigitalOcean managed databases:
- Port must be explicitly defined
- Verify your IP is allowed in the firewall
Run schema update:
php www/index.php o:s:u -fextensions:
dbal.console: Baraja\Doctrine\DBAL\DI\DbalConsoleExtension
orm: Baraja\Doctrine\ORM\DI\OrmExtension
orm.console: Baraja\Doctrine\ORM\DI\OrmConsoleExtension
orm.annotations: Baraja\Doctrine\ORM\DI\OrmAnnotationsExtension
baraja.database: Baraja\Doctrine\DatabaseExtension
baraja.database:
connection:
host: %database.host%
dbname: %database.dbname%
user: %database.user%
password: %database.password%
charset: UTF8
cache: apcu
propertyIgnoreAnnotations:
- sample
orm:
proxyDir: %tempDir%/proxies
namingStrategy: Doctrine\ORM\Mapping\UnderscoreNamingStrategy
orm.annotations:
paths:
App\Entity: %appDir%/model/Entity
defaultCache: filesystem
debug: %debugMode%Jan Barasek - https://baraja.cz
baraja-core/doctrine is licensed under the MIT license. See the LICENSE file for more details.
