Doctrine/ORM (opens new window) to Nette Framework.

# Content

# Setup

Install package

composer require nettrine/orm
1

Register extension

extensions:
  nettrine.orm: Nettrine\ORM\DI\OrmExtension
1
2

# Relying

Take advantage of enpowering this package with 3 extra packages:

  • doctrine/dbal
  • doctrine/cache
  • symfony/console

# doctrine/dbal

This package relies on doctrine/dbal, use prepared nettrine/dbal (opens new window) integration.

composer require nettrine/dbal
1
extensions:
  nettrine.dbal: Nettrine\DBAL\DI\DbalExtension
1
2

Doctrine ORM (opens new window) needs Doctrine DBAL (opens new window) to be configured. If you register nettrine/dbal extension it will detect it automatically.

Doctrine DBAL provides powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction.

# doctrine/cache

This package relies on doctrine/cache, use prepared nettrine/cache (opens new window) integration.

composer require nettrine/cache
1
extensions:
  nettrine.cache: Nettrine\Cache\DI\CacheExtension
1
2

Doctrine ORM (opens new window) needs Doctrine Cache (opens new window) to be configured. If you register nettrine/cache extension it will detect it automatically.

CacheExtension sets up cache for all important parts: queryCache, resultCache, hydrationCache, metadataCache and secondLevelCache.

This is the default configuration, it uses the autowired driver.

extensions:
  nettrine.orm: Nettrine\ORM\DI\OrmExtension
  nettrine.orm.cache: Nettrine\ORM\DI\OrmCacheExtension
1
2
3

You can also specify a single driver or change the nettrine.orm.cache.defaultDriver for all of them.

nettrine.orm.cache:
  defaultDriver: App\DefaultOrmCacheDriver
  queryCache: App\SpecialDriver
  resultCache: App\SpecialOtherDriver
  hydrationCache: App\SpecialDriver('foo')
  metadataCache: @cacheDriver
1
2
3
4
5
6

secondLevelCache uses autowired driver (or defaultDriver, if specified) for CacheConfiguration setup, but you can also replace it with custom CacheConfiguration.

nettrine.orm.cache:
  secondLevelCache: @cacheConfigurationFactory::create('bar')
1
2

# symfony/console

This package relies on symfony/console, use prepared contributte/console (opens new window) integration.

composer require contributte/console
1
extensions:
  console: Contributte\Console\DI\ConsoleExtension(%consoleMode%)

  nettrine.orm: Nettrine\ORM\DI\OrmExtension
  nettrine.orm.console: Nettrine\ORM\DI\OrmConsoleExtension(%consoleMode%)
1
2
3
4
5

Since this moment when you type bin/console, there'll be registered commands from Doctrine DBAL.

Console Commands

# Configuration

Schema definition

nettrine.orm:
 configuration:
   proxyDir: <path>
   autoGenerateProxyClasses: <boolean>
   proxyNamespace: <string>
   metadataDriverImpl: <service>
   entityNamespaces: <mixed[]>
   customStringFunctions: <mixed[]>
   customNumericFunctions: <mixed[]>
   customDatetimeFunctions: <mixed[]>
   customHydrationModes: <string[]>
   classMetadataFactoryName: <string>
   defaultRepositoryClassName: <string>
   namingStrategy: <class>
   quoteStrategy: <class>
   entityListenerResolver: <class>
   repositoryFactory: <class>
   defaultQueryHints: <mixed[]>
   filters:
     <string>:
       class: <string>
       enabled: <boolean>

 entityManagerDecoratorClass: <class>
 configurationClass: <class>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Under the hood

Minimal configuration could look like this:

nettrine.orm:
  configuration:
    autoGenerateProxyClasses: %debugMode%
1
2
3

Take a look at real Nettrine ORM configuration example at contributte/webapp-project (opens new window).

Side notes

  1. The compiler extensions would be so big that we decided to split them into more separate files / compiler extensions.

  2. At this time we support only 1 connection, the default connection. If you need more connections (more databases?), please open an issue or send a PR. Thanks.

  3. Are you looking for custom types? You can register custom types in DBAL, see Nettrine DBAL (opens new window).

  4. You have to configure entity mapping (see below), otherwise you will get It's a requirement to specify a Metadata Driver error.

# Mapping

Doctrine ORM needs to know where your entities are located and how they are described (mapping).

Additional metadata provider needs to be registered. We provide bridges for these drivers:

  • attributes (Nettrine\ORM\DI\OrmAttributesExtension)
  • annotations (Nettrine\ORM\DI\OrmAnnotationsExtension)
  • xml (Nettrine\ORM\DI\OrmXmlExtension)

# Attributes

Since PHP 8.0, we can use #[attributes] (opens new window) for entity mapping.

<?php

namespace App\Model\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;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Use OrmAttributesExtension as the bridge to the AttributeDriver. This is the default configuration.

extensions:
  nettrine.orm: Nettrine\ORM\DI\OrmExtension
  nettrine.orm.attributes: Nettrine\ORM\DI\OrmAttributesExtension

nettrine.orm.attributes:
  mapping: [
    namespace: path
  ]
  excluded: []
1
2
3
4
5
6
7
8
9

Example configuration for entity located at app/Model/Database folder.

nettrine.orm.attributes:
  mapping:
   App\Model\Database: %appDir%/Model/Database
1
2
3

# Annotations

Are you using @annotations (opens new window) in your entities?

<?php

namespace App\Model\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;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

This feature relies on doctrine/annotations, use prepared nettrine/annotations (opens new window) integration.

composer require nettrine/annotations
1
extensions:
  nettrine.annotations: Nettrine\Annotations\DI\AnnotationsExtension
1
2

You will also appreciate ORM => Annotations bridge, use OrmAnnotationsExtension. This is the default configuration, it uses an autowired cache driver. Please note that OrmAnnotationsExtension must be registered after AnnotationsExtension. Ordering is crucial!

extensions:
  # Common
  nettrine.annotations: Nettrine\Annotations\DI\AnnotationsExtension

  # ORM
  nettrine.orm: Nettrine\ORM\DI\OrmExtension
  nettrine.orm.annotations: Nettrine\ORM\DI\OrmAnnotationsExtension

nettrine.orm.annotations:
  mapping: [
    namespace: path
  ]
  excluded: []
1
2
3
4
5
6
7
8
9
10
11
12
13

Example configuration for entity located at app/Model/Database folder.

nettrine.orm.annotations:
  mapping:
   App\Model\Database: %appDir%/Model/Database
1
2
3

# XML

Are you using XML mapping (opens new window) for your entities?

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
      xmlns:xsi="http://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>
1
2
3
4
5
6
7
8

You will also appreciate ORM => XML bridge, use OrmXmlExtension. This is the default configuration:

extensions:
  nettrine.orm: Nettrine\ORM\DI\OrmExtension
  nettrine.orm.xml: Nettrine\ORM\DI\OrmXmlExtension

nettrine.orm.xml:
  mapping: [
    namespace: path
  ]
  fileExtension: .dcm.xml
  simple: false
1
2
3
4
5
6
7
8
9
10

Using simple you will enable SimplifiedXmlDriver (opens new window).

# Helpers

MappingHelper

You can use the predefined MappingHelper helper class in your compiler extensions. Be careful, you have to call it in beforeCompile phase.

use Nette\DI\CompilerExtension;
use Nettrine\ORM\DI\Helpers\MappingHelper;

class CategoryExtension extends CompilerExtension
{

  public function beforeCompile(): void
  {
    MappingHelper::of($this)
        ->addAnnotation('App\Model\Database', __DIR__ . '/../app/Model/Database')
        ->addAnnotation('Forum\Modules\Database', __DIR__ . '/../../modules/Forum/Database')
        ->addXml('Gallery1\Modules\Database', __DIR__ . '/../../modules/Gallery1/Database')
        ->addXml('Gallery2\Modules\Database', __DIR__ . '/../../modules/Gallery2/Database', $simple = TRUE)
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Examples

# 1. Manual example

composer require nettrine/annotations nettrine/cache nettrine/migrations nettrine/fixtures nettrine/dbal nettrine/orm
1
# Extension > Nettrine
# => order is crucial
#
extensions:
  # Common
  nettrine.annotations: Nettrine\Annotations\DI\AnnotationsExtension
  nettrine.cache: Nettrine\Cache\DI\CacheExtension
  nettrine.migrations: Nettrine\Migrations\DI\MigrationsExtension
  nettrine.fixtures: Nettrine\Fixtures\DI\FixturesExtension

  # DBAL
  nettrine.dbal: Nettrine\DBAL\DI\DbalExtension
  nettrine.dbal.console: Nettrine\DBAL\DI\DbalConsoleExtension

  # ORM
  nettrine.orm: Nettrine\ORM\DI\OrmExtension
  nettrine.orm.cache: Nettrine\ORM\DI\OrmCacheExtension
  nettrine.orm.console: Nettrine\ORM\DI\OrmConsoleExtension
  nettrine.orm.annotations: Nettrine\ORM\DI\OrmAnnotationsExtension
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 2. Example projects

We've made a few skeletons with preconfigured Nettrine nad Contributte packages.

# 3. Example playground

# Other

This repository is inspired by these packages.

Thank you folks.