Integration of Doctrine ORM for Nette Framework.
Install package using composer.
composer require nettrine/orm
Register prepared compiler extension in your config.neon
file.
extensions:
nettrine.orm: Nettrine\ORM\DI\OrmExtension
Note
This is just ORM, for DBAL please use nettrine/dbal.
nettrine.orm:
managers:
default:
connection: default
mapping:
App:
directories: [%appDir%/Database]
namespace: App/Database
Here is the list of all available options with their types.
nettrine.orm:
managers:
<name>:
connection: <string>
entityManagerDecoratorClass: <class>
configurationClass: <class>
proxyDir: <path>
autoGenerateProxyClasses: <boolean>
proxyNamespace: <string>
metadataDriverImpl: <service>
entityNamespaces: <mixed[]>
customStringFunctions: <mixed[]>
customNumericFunctions: <mixed[]>
customDatetimeFunctions: <mixed[]>
customHydrationModes: <string[]>
classMetadataFactoryName: <string>
defaultRepositoryClassName: <string>
namingStrategy: <class-string>
quoteStrategy: <class-string>
entityListenerResolver: <class-string>
repositoryFactory: <class-string>
defaultQueryHints: <mixed[]>
filters:
<name>:
class: <string>
enabled: <boolean>
mapping:
<name>:
type: <attributes|xml>
directories: <string[]>
namespace: <string>
defaultCache: <class-string|service>
queryCache: <class-string|service>
resultCache: <class-string|service>
hydrationCache: <class-string|service>
metadataCache: <class-string|service>
secondLevelCache:
enable: <boolean>
cache: <class-string|service>
logger: <class-string|service>
regions:
<name>:
lifetime: <int>
lockLifetime: <int>
For example:
# See more in nettrine/dbal
nettrine.dbal:
debug:
panel: %debugMode%
connections:
default:
driver: pdo_pgsql
host: localhost
port: 5432
user: root
password: root
dbname: nettrine
nettrine.orm:
managers:
default:
connection: default
mapping:
App:
directories: [%appDir%/Database]
namespace: App\Database
Tip
Take a look at real Nettrine ORM configuration example at contributte/doctrine-project.
By default, this extension will try to autoconfigure itself.
- proxyDir:
%tempDir%/proxies
, if%tempDir%
is not defined,, you have to define it manually. - autoGenerateProxyClasses:
%debugMode%
, if%debugMode%
is not defined, you have to define it manually.0
means that the proxy classes must be generated manually.1
means that the proxy classes are generated automatically.2
means that the proxy classes are generated automatically when the proxy file does not exist.3
means that the proxy classes are generated automatically usingeval()
(useful for debugging).4
means that the proxy classes are generated automatically when the proxy file does not exist or when the proxied file changed.
EntityManager is a central access point to ORM functionality. It is a wrapper around ObjectManager and holds the metadata and configuration of the ORM.
EntityManagerDecorator
You can use entityManagerDecoratorClass
to decorate EntityManager.
nettrine.orm:
managers:
default:
connection: default
entityManagerDecoratorClass: App\MyEntityManagerDecorator
Close & Reset
If you hit The EntityManager is closed.
exception, you can use reset
method to reopen it.
$managerRegistry = $container->getByType(Doctrine\Persistence\ManagerRegistry::class);
$managerRegistry->resetManager(); // default
$managerRegistry->resetManager('second');
Warning
Resetting the manager is a dangerous operation. It is also black magic, because you cannot just create a new EntityManager instance,
you have to reset the current one using internal methods (reflection & binding).
Class responsible for this operation is Nettrine\ORM\ManagerRegistry
.
Tip
Take a look at more information in official Doctrine documentation:
A Doctrine ORM can automatically cache query results and metadata. The feature is optional though, and by default, no cache is configured.
You can enable the result cache by setting the defaultCache
configuration option to an instance of a cache driver or metadataCache
, queryCache
, resultCache
, hydrationCache
separately.
Warning
Cache adapter must implement Psr\Cache\CacheItemPoolInterface
interface.
Use any PSR-6 + PSR-16 compatible cache library like symfony/cache
or nette/caching
.
In the simplest case, you can define only defaultCache
for all caches.
nettrine.orm:
managers:
default:
# Create cache manually
defaultCache: App\CacheService(%tempDir%/cache/orm)
# Use registered cache service
defaultCache: @cacheService
Or you can define each cache separately.
nettrine.orm:
managers:
default:
queryCache: App\CacheService(%tempDir%/cache/orm-query)
resultCache: App\CacheService(%tempDir%/cache/orm-result)
hydrationCache: App\CacheService(%tempDir%/cache/orm-hydration)
metadataCache: App\CacheService(%tempDir%/cache/orm-metadata)
Second level cache is a bit different. Be sure you know what you are doing, lear more in official Doctrine documentation.
nettrine.orm:
managers:
default:
secondLevelCache:
enable: true
cache: App\CacheService(%tempDir%/cache/orm-slc)
logger: App\LoggerService()
regions:
region1:
lifetime: 3600
lockLifetime: 60
region2:
lifetime: 86000
lockLifetime: 60
If you like symfony/cache
you can use it as well.
nettrine.orm:
managers:
default:
# Use default cache
defaultCache: Symfony\Component\Cache\Adapter\FilesystemAdapter(namespace: doctrine-orm, defaultLifetime: 0, directory: %tempDir%/cache/orm)
# Or use separate caches
queryCache: Symfony\Component\Cache\Adapter\FilesystemAdapter(namespace: doctrine-orm-query, defaultLifetime: 0, directory: %tempDir%/cache/orm-query)
resultCache: Symfony\Component\Cache\Adapter\FilesystemAdapter(namespace: doctrine-orm-result, defaultLifetime: 0, directory: %tempDir%/cache/orm-result)
hydrationCache: Symfony\Component\Cache\Adapter\FilesystemAdapter(namespace: doctrine-orm-hydration, defaultLifetime: 0, directory: %tempDir%/cache/orm-hydration)
metadataCache: Symfony\Component\Cache\Adapter\FilesystemAdapter(namespace: doctrine-orm-metadata, defaultLifetime: 0, directory: %tempDir%/cache/orm-metadata)
If you like nette/caching
you can use it as well. Be aware that nette/caching
is not PSR-6 + PSR-16 compatible, you need contributte/psr16-caching
.
nettrine.orm:
managers:
default:
defaultCache: Contributte\Psr6\CachePool(
Nette\Caching\Cache(
Nette\Caching\Storages\FileStorage(%tempDir%/cache)
doctrine/dbal
)
)
Important
You should always use cache for production environment. It can significantly improve performance of your application. Pick the right cache adapter for your needs. For example from symfony/cache:
FilesystemAdapter
- if you want to cache data on diskArrayAdapter
- if you want to cache data in memoryApcuAdapter
- if you want to cache data in memory and share it between requestsRedisAdapter
- if you want to cache data in memory and share it between requests and serversChainAdapter
- if you want to cache data in multiple storages
There are several ways how to map entities to Doctrine ORM. This library supports attributes and xml out of the box.
Since PHP 8.0, we can use #[attributes] for entity mapping.
<?php declare(strict_types=1);
namespace App\Database;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'customer')]
class Customer
{
#[ORM\Column(length: 32, unique: true, nullable: false)]
protected string $username;
#[ORM\Column(columnDefinition: 'CHAR(2) NOT NULL')]
protected string $country;
}
Configuration for attribute mapping looks like this:
nettrine.orm:
managers:
default:
connection: default
mapping:
App:
directories: [%appDir%/Database]
namespace: App\Database
The XML mapping driver enables you to provide the ORM metadata in form of XML documents. It requires the dom extension in order to be able to validate your mapping documents against its XML Schema.
Tip
Take a look at more information in official Doctrine documentation:
<doctrine-mapping
xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd
">
...
</doctrine-mapping>
Configuration for XML mapping looks like this:
nettrine.orm:
managers:
default:
connection: default
mapping:
App:
type: xml
directories: [%appDir%/Database]
namespace: App\Database
You can use MappingHelper
to add multiple mappings at once. This is useful when you have multiple modules with entities.
Create your own compiler extension and use MappingHelper
to add mappings.
It's a good practice if you have separated modules in your applications.
<?php declare(strict_types=1);
namespace App\Model\DI;
use Nette\DI\CompilerExtension;use Nettrine\ORM\DI\Helpers\MappingHelper;
class DoctrineMappingExtension extends CompilerExtension
{
public function beforeCompile(): void
{
MappingHelper::of($this)
->addAttribute($connection = 'default', $namespace = 'App\Model\Database', $path = __DIR__ . '/../app/Model/Database')
->addAttribute('default', 'Forum\Modules\Database', __DIR__ . '/../../modules/Forum/Database')
->addXml('default', 'Gallery1\Modules\Database', __DIR__ . '/../../modules/Gallery1/Database')
->addXml('default', 'Gallery2\Modules\Database', __DIR__ . '/../../modules/Gallery2/Database')
}
}
Do not forget to register your extension in config.neon
.
extensions:
category: App\Model\DI\DoctrineMappingExtension
Tip
Doctrine ORM needs DBAL. You can use doctrine/dbal
or nettrine/dbal.
composer require nettrine/dbal
extensions:
nettrine.dbal: Nettrine\DBAL\DI\DbalExtension
nettrine.orm: Nettrine\ORM\DI\OrmExtension
Tip
Doctrine DBAL needs Symfony Console to work. You can use symfony/console
or contributte/console.
composer require contributte/console
extensions:
console: Contributte\Console\DI\ConsoleExtension(%consoleMode%)
nettrine.orm: Nettrine\ORM\DI\OrmExtension
Since this moment when you type bin/console
, there'll be registered commands from Doctrine DBAL.
You can use PHPStan to analyze your code.
- Install PHPStan and Doctrine extension.
composer require --dev phpstan/phpstan phpstan/phpstan-doctrine
- Create ORM loader for PHPStan, e.q.
phpstan-doctrine.php
.
<?php declare(strict_types = 1);
require __DIR__ . '/../vendor/autoload.php';
return App\Bootstrap::boot()
->createContainer()
->getByType(Doctrine\ORM\EntityManagerInterface::class);
- Configure PHPStan in
phpstan.neon
.
includes:
- vendor/phpstan/phpstan-doctrine/extension.neon
parameters:
level: 9
phpVersion: 80200
tmpDir: %currentWorkingDirectory%/var/tmp/phpstan
fileExtensions:
- php
- phpt
paths:
- app
doctrine:
objectManagerLoader: phpstan-doctrine.php
- And run PHPStan.
vendor/bin/phpstan analyse -c phpstan.neon
-
Are you looking for custom types? You can register custom types in DBAL, see Nettrine DBAL.
-
You have to configure entity mapping (for example attributes), otherwise you will get
It's a requirement to specify a Metadata Driver
error.
Tip
Take a look at more examples in contributte/doctrine.