diff --git a/.doctrine-project.json b/.doctrine-project.json
new file mode 100644
index 0000000..98c76cc
--- /dev/null
+++ b/.doctrine-project.json
@@ -0,0 +1,14 @@
+{
+ "active": true,
+ "name": "PHPCR Bundle",
+ "slug": "phpcr-bundle",
+ "docsSlug": "doctrine-phpcr-bundle",
+ "versions": [
+ {
+ "name": "3.x",
+ "branchName": "3.x",
+ "slug": "latest",
+ "current": true
+ }
+ ]
+}
diff --git a/README.md b/README.md
index f0b2869..ec3a613 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,7 @@ adds features of its own like multilanguage.
## Documentation
-For information, see [Symfony CMF Documentation](http://symfony.com/doc/master/cmf/index.html),
-specifically [The Database Layer: PHPCR-ODM](http://symfony.com/doc/master/cmf/book/database_layer.html)
-and [DoctrinePHPCRBundle](http://symfony.com/doc/master/cmf/bundles/phpcr_odm/introduction.html).
+For information on PHPCR-ODM, see [Doctrine Documentation](https://www.doctrine-project.org/projects/phpcr-odm.html),
+and [DoctrinePHPCRBundle](https://www.doctrine-project.org/projects/doctrine-phpcr-bundle.html).
-PHPCR-ODM in general is documented in the [Doctrine PHPCR-ODM documentation](http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/).
+Read more about PHPCR, the storage layer behind PHPCR-ODM: [PHPCR documentation](https://phpcr.readthedocs.io/en/latest/).
diff --git a/doc/configuration.rst b/doc/configuration.rst
new file mode 100644
index 0000000..113fce5
--- /dev/null
+++ b/doc/configuration.rst
@@ -0,0 +1,720 @@
+Configuration Reference
+=======================
+
+The DoctrinePHPCRBundle can be configured under the ``doctrine_phpcr`` key in
+your application configuration. When using XML, you can use the
+``http://doctrine-project.org/schema/symfony-dic/odm/phpcr`` namespace.
+
+
+Configuration
+-------------
+
+``session``
+~~~~~~~~~~~
+
+.. tip::
+
+ You can also configure multiple session. See
+ :doc:`multiple_sessions` for details.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ backend:
+ type: X
+ # optional parameters for Jackalope
+ parameters:
+ jackalope.factory: Jackalope\Factory
+ jackalope.check_login_on_server: false
+ jackalope.disable_stream_wrapper: false
+ jackalope.auto_lastmodified: true
+ # see below for how to configure the backend of your choice
+ workspace: default
+ username: admin
+ password: admin
+ # tweak options for Jackalope (all versions)
+ options:
+ jackalope.fetch_depth: 1
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+ Jackalope\Factory
+ false
+ false
+ true
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'backend' => [
+ 'type' => 'X',
+ 'parameters' => [
+ 'jackalope.factory' => 'Jackalope\Factory',
+ 'jackalope.check_login_on_server' => false,
+ 'jackalope.disable_stream_wrapper' => false,
+ 'jackalope.auto_lastmodified' => true,
+ ],
+ ],
+ 'workspace' => 'default',
+ 'username' => 'admin',
+ 'password' => 'admin',
+ 'options' => [
+ 'jackalope.fetch_depth' => 1,
+ ],
+ ],
+ ]);
+
+``workspace``
+"""""""""""""
+
+**type**: ``string`` **required**
+
+Defines the PHPCR workspace to use for this PHPCR session.
+
+.. tip::
+
+ Every PHPCR implementation should provide the workspace called *default*,
+ but you can choose a different one. There is the
+ ``doctrine:phpcr:workspace:create`` command to initialize a new workspace.
+ See also :ref:`bundles-phpcr-odm-commands`.
+
+``username and password``
+"""""""""""""""""""""""""
+
+**type**: ``string`` **default**: ``null``
+
+These credentials are used on the PHPCR layer for the
+``PHPCR\SimpleCredentials``. They are optional for jackalope doctrine-dbal.
+
+Do not confuse these credentials with the username and password used by
+Doctrine DBAL to connect to the underlying RDBMS where the data
+is actually stored.
+
+``backend type``
+""""""""""""""""
+
+**type**: ``string`` **default**: ``jackrabbit``
+
+This designates the PHPCR implementation. Valid options are
+
+* ``jackrabbit``;
+* ``doctrinedbal``;
+* ``prismic``.
+
+``backend parameters``
+""""""""""""""""""""""
+
+If you are using one of the Jackalope backends, you can set a couple of
+parameters. This section explains the general parameters that are
+available with all Jackalope backends. You can also
+:ref:`activate logging and profiling `.
+
+``jackalope.factory``
+.....................
+
+**type**: ``string or object`` **default**: ``Jackalope\Factory``
+
+Use a custom factory class for Jackalope objects.
+
+``jackalope.check_login_on_server``
+...................................
+
+**type**: ``boolean`` **default**: ``false``
+
+If set to ``false``, skip initial check whether repository exists. You will
+only notice connectivity problems on the first attempt to use the repository.
+
+``jackalope.disable_stream_wrapper``
+....................................
+
+**type**: ``boolean`` **default**: ``false``
+
+If set to ``true``, streams are read immediately instead of on first access.
+If you run into problems with streams this might be useful for debugging.
+Otherwise you probably don't want to disable the wrappers, or all binaries
+will be loaded each time their containing document is loaded, resulting in a
+severe performance penalty.
+
+``jackalope.auto_lastmodified``
+...............................
+
+**type**: ``boolean`` **default**: ``true``
+
+Whether to automatically update nodes having ``mix:lastModified``.
+See `last modified listener cookbook entry`_.
+
+``backend curl_options``
+""""""""""""""""""""""""
+
+If you are using one of the Jackalope Jackrabbit backend, you can set
+the curl options which are described in the php-documentation
+`curl-setopt`_.
+
+PHPCR Session with Jackalope Jackrabbit
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ backend:
+ type: jackrabbit
+ url: http://localhost:8080/server/
+ parameters:
+ # general parameters and options
+ # ...
+ # optional parameters specific to Jackalope Jackrabbit
+ jackalope.default_header: "X-ID: %serverid%"
+ jackalope.jackrabbit_expect: true
+ jackalope.jackrabbit_version: "2.18.3"
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ X-ID: %serverid%
+ true
+ 2.18.3
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'backend' => [
+ 'type' => 'jackrabbit',
+ 'url' => 'http://localhost:8080/server/',
+ 'parameters' => [
+ 'jackalope.default_header' => 'X-ID: %serverid%',
+ 'jackalope.jackrabbit_expect' => true,
+ 'jackalope.jackrabbit_version' => '2.18.3',
+ ],
+ ],
+ ],
+ ]);
+
+``url``
+"""""""
+
+**type**: ``string``, **required**
+
+The configuration needs the ``url`` parameter to point to your Jackrabbit.
+This looks like http://localhost:8080/server/
+
+``jackalope.default_header``
+""""""""""""""""""""""""""""
+
+**type**: ``string``, **default**: ``null``
+
+Set a default header to send on each request to the backend.
+This is useful when using a load balancer between the webserver and jackrabbit,
+to identify sessions.
+
+``jackalope.jackrabbit_expect``
+"""""""""""""""""""""""""""""""
+
+**type**: ``boolean``, **default**: ``false``
+
+Send the ``Expect: 100-continue`` header on larger PUT and POST requests.
+Disabled by default to avoid issues with proxies and load balancers.
+
+``jackalope.jackrabbit_version``
+""""""""""""""""""""""""""""
+
+**type**: ``string``, **default**: ``null``
+
+.. versionadded:: 1.4.2
+
+ This configuration has been added in jackalope-jackrabbit version 1.4.2.
+
+Set the version of the Jackrabbit server to allow the client to offer better functionality if possible.
+For example, full UTF8 support including emojis in node and property names is possible when the Jackrabbit version is 2.18.0 or better.
+
+PHPCR Session with Jackalope Doctrine DBAL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This type uses Jackalope with a Doctrine database abstraction layer transport
+to provide PHPCR without any installation requirements beyond any of the RDBMS
+supported by Doctrine.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ backend:
+ type: doctrinedbal
+ # if no explicit connection is specified, the default connection is used.
+ connection: default
+ # to configure caching
+ caches:
+ meta: doctrine_cache.providers.phpcr_meta
+ nodes: doctrine_cache.providers.phpcr_nodes
+ query: doctrine_cache.providers.phpcr_query
+ parameters:
+ # ... general parameters and options
+
+ # optional parameters specific to Jackalope Doctrine Dbal
+ jackalope.disable_transactions: false
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'backend' => [
+ 'type' => 'doctrinedbal',
+ 'connection' => 'default',
+ 'caches' => [
+ 'meta' => 'doctrine_cache.providers.phpcr_meta',
+ 'nodes' => 'doctrine_cache.providers.phpcr_nodes',
+ 'query' => 'doctrine_cache.providers.phpcr_query',
+ ],
+ 'parameters' => [
+ // ... general parameters and options
+
+ // optional parameters specific to Jackalope Doctrine Dbal
+ 'jackalope.disable_transactions' => false,
+ ],
+ ],
+ ],
+ ]);
+
+``connection``
+""""""""""""""
+
+**type**: ``string``, **default**: ``default``
+
+Specify the Doctrine DBAL connection name to use if you don't want to use the
+default connection. The name must be one of the names of the ``doctrine.dbal``
+section of your Doctrine configuration, see the `Symfony Doctrine documentation`_.
+
+``jackalope.disable_transactions``
+""""""""""""""""""""""""""""""""""
+
+**type**: ``boolean``, **default**: ``false``
+
+Set to ``true`` to disable transactions. If transactions are enabled but not
+actively used, every save operation is wrapped into a transaction.
+
+Only allowed for doctrine-dbal because jackrabbit does not support
+transactions.
+
+.. _reference-configuration-phpcr-odm-logging:
+
+Logging and Profiling
+~~~~~~~~~~~~~~~~~~~~~
+
+When using any of the Jackalope PHPCR implementations, you can activate logging
+to log to the symfony log, or profiling to show information in the Symfony
+debug toolbar:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ backend:
+ # ...
+ logging: true
+ profiling: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.yml
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'backend' => [
+ // ...
+ 'logging' => true,
+ 'profiling' => true,
+ ],
+ ],
+ ]);
+
+Doctrine PHPCR-ODM Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This configuration section manages the Doctrine PHPCR-ODM system. If you do
+not configure anything here, the ODM services will not be loaded.
+
+.. tip::
+
+ If you want to only use plain PHPCR without the PHPCR-ODM, you can simply
+ not configure the ``odm`` section to avoid loading the services at all.
+ Note that most CMF bundles by default use PHPCR-ODM documents and thus
+ need ODM enabled.
+
+.. tip::
+
+ You can also configure multiple document managers. See
+ :doc:`multiple_sessions` for details.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ odm:
+ configuration_id: ~
+ auto_mapping: true
+ mappings:
+ # An array of mapping, which may be a bundle name or an unique name
+ :
+ mapping: true
+ type: ~
+ dir: ~
+ alias: ~
+ prefix: ~
+ is_bundle: ~
+ auto_generate_proxy_classes: "%kernel.debug%"
+ proxy_dir: "%kernel.cache_dir%/doctrine/PHPCRProxies"
+ proxy_namespace: PHPCRProxies
+ namespaces:
+ translation:
+ alias: phpcr_locale
+
+ metadata_cache_driver:
+ type: array
+ host: ~
+ port: ~
+ instance_class: ~
+ class: ~
+ id: ~
+ namespace: ~
+
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ mapping="true"
+ type="null"
+ dir="null"
+ alias="null"
+ prefix="null"
+ is-bundle="null"
+ />
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'odm' => [
+ 'configuration_id' => null,
+ 'auto_mapping' => true,
+ 'auto_generate_proxy_classes' => '%kernel.debug%',
+ 'proxy-dir' => '%kernel.cache_dir%/doctrine/PHPCRProxies',
+ 'proxy_namespace' => 'PHPCRProxies',
+ 'namespaces' => [
+ 'translation' => [
+ 'alias' => 'phpcr_locale',
+ ],
+ ],
+ 'mappings' => [
+ // An array of mapping, which may be a bundle name or an unique name
+ '' => [
+ 'mapping' => true,
+ 'type' => null,
+ 'dir' => null,
+ 'alias' => null,
+ 'prefix' => null,
+ 'is-bundle' => null,
+ ],
+ ],
+ 'metadata_cache_driver' => [
+ 'type' => 'array',
+ 'host' => null,
+ 'port' => null,
+ 'instance_class' => null,
+ 'class' => null,
+ 'id' => null,
+ 'namespace' => null,
+ ],
+ ],
+ ]);
+
+``configuration_id``
+""""""""""""""""""""
+
+**type**: ``string``, **default**: ``doctrine_phpcr.odm.configuration``
+
+The service to use as base for building the PHPCR-ODM configuration.
+
+``auto_mapping``
+""""""""""""""""
+
+**type**: ``boolean``, **default**: ``true``
+
+When enabled, bundles will be automatically loaded and attempted to resolve
+mappings by convention in
+``/Resources/config/doctrine/.phpcr.xml`` resp. ``*.phpcr.yml``
+to configure mappings for documents you provide in the ``/Document``
+folder. Otherwise you need to manually configure the mappings section.
+
+``auto_generate_proxy_classes``
+"""""""""""""""""""""""""""""""
+
+**type**: ``boolean``, **default**: ``%kernel.debug%``
+
+When disabled, you need to run the ``cache:warmup`` command in order to have
+the proxy classes generated after you modified a document.
+
+``proxy_dir``
+"""""""""""""
+
+**type**: ``string``, **default**: ``%kernel.cache_dir%/doctrine/PHPCRProxies``
+
+Change folder where proxy classes are generated.
+
+``proxy_namespace``
+"""""""""""""""""""
+
+**type**: ``string``, **default**: ``PHPCRProxies``
+
+Change namespace for generated proxy classes.
+
+``namespaces``
+""""""""""""""
+
+This configuration section is intended to allow you to customize the
+PHPCR namespaces used by PHPCR-ODM. Currently it is only possible to
+set the alias used by the translation strategy.
+
+``mappings``
+""""""""""""
+
+Explicitly define document mappings by configuration. For modern Symfony
+applications that do not use a bundle, it is necessary to configure mappings.
+For bundles, if ``auto_mapping`` is enabled, you don't usually need to.
+
+.. tip::
+
+ When ``auto_mapping`` is disabled, you need to explicitly list the
+ bundles handled by this document manager. Usually its fine to just list
+ the bundle names without any actual configuration.
+
+.. tip::
+
+ DoctrinePhpcrBundle is integrated with symfony/doctrine-bridge (in the same
+ way that `Doctrine ORM`_ does), relying on the bridge to process mapping
+ configuration options. Therefore, the mapping options work nearly the same
+ across two bundles.
+
+There are several configuration options that you can control as part of
+a mapping definition:
+
+``mapping``
+ A boolean value and it is usually ``true``. Set it to ``true`` to
+ declare it as a mapping and allow the document manager to pick it
+ up.
+
+``type``
+ One of ``attribute``, ``xml``, ``yml``, ``php`` or ``staticphp``.
+ This specifies which type of metadata type your mapping uses.
+
+``dir``
+ Path to the mapping or document files (depending on the driver). If this path
+ is relative, it is assumed to be relative to the bundle root. This only works
+ if the name of your mapping is a bundle name. If you want to use this option
+ to specify absolute paths, you should prefix the path with the kernel
+ parameters that exist in the DIC (for example ``%kernel.root_dir%``).
+
+``prefix``
+ A common namespace prefix that all documents of this mapping share. This
+ prefix should never conflict with prefixes of other defined mappings
+ otherwise some of your documents cannot be found by Doctrine. This option
+ defaults to the bundle namespace + ``Document``, for example for an
+ application bundle called ``AcmeHelloBundle`` prefix would be
+ ``Acme\HelloBundle\Document``.
+
+``alias``
+ Doctrine offers a way to alias document namespaces to simpler, shorter names
+ to be used in DQL queries or for Repository access. When using a bundle, the
+ alias defaults to the bundle name.
+
+``is_bundle``
+ This option is a derived value from ``dir`` and by default is set to true if
+ dir is relative proved by a ``file_exists()`` check that returns false. It
+ is false if the existence check returns true. In this case, an absolute path
+ was specified and the metadata files are most likely in a directory outside
+ of a bundle.
+
+
+``metadata_cache_driver``
+"""""""""""""""""""""""""
+
+Configure a cache driver for the Doctrine metadata. This is the same as for
+`Doctrine ORM`_.
+
+The ``namespace`` value is useful if you are using one primary caching server
+for multiple sites that have similar code in their respective ``vendor/``
+directories. By default, Symfony will try to generate a unique namespace
+value for each application but if code is very similar between two
+applications, it is very easy to have two applications share the same
+namespace. This option also prevents Symfony from needing to re-build
+application cache on each Composer update on a newly generated namespace.
+
+General Settings
+~~~~~~~~~~~~~~~~
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ jackrabbit_jar: /path/to/jackrabbit.jar
+ dump_max_line_length: 120
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'jackrabbit_jar' => '/path/to/jackrabbit.jar',
+ 'dump_max_line_length' => 120,
+ ]);
+
+``jackrabbit_jar``
+""""""""""""""""""
+
+**type**: ``string`` **default**: ``null``
+
+Absolute path to the jackrabbit jar file. If this is set, you can use the
+``doctrine:phpcr:jackrabbit`` console command to start and stop Jackrabbit.
+
+``dump_max_line_length``
+""""""""""""""""""""""""
+
+**type**: ``integer`` **default**: ``120``
+
+For tuning the output of the ``doctrine:phpcr:dump`` command.
+
+.. _`Symfony Doctrine documentation`: https://symfony.com/doc/current/doctrine.html
+.. _`last modified listener cookbook entry`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/cookbook/last-modified.html
+.. _`Doctrine ORM`: https://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers
+.. _`curl-setopt`: http://php.net/manual/de/function.curl-setopt.php
diff --git a/doc/events.rst b/doc/events.rst
new file mode 100644
index 0000000..c470fc5
--- /dev/null
+++ b/doc/events.rst
@@ -0,0 +1,101 @@
+.. index::
+ single: Events; DoctrinePHPCRBundle
+
+Doctrine PHPCR-ODM Events
+=========================
+
+Doctrine PHPCR-ODM provides an event system allowing to react to all
+important operations that documents have during their lifecycle. Please
+see the `Doctrine PHPCR-ODM event system documentation`_ for a full
+list of supported events.
+
+The DoctrinePHPCRBundle provides dependency injection support for the
+event listeners and event subscribers.
+
+Dependency Injection Tags
+-------------------------
+
+You can tag services to listen to Doctrine PHPCR-ODM events. It works the same
+way as for `Doctrine ORM events`_. The only differences are:
+
+* use the tag name ``doctrine_phpcr.event_listener`` resp.
+ ``doctrine_phpcr.event_subscriber`` instead of ``doctrine.event_listener``;
+* expect the argument to be of class
+ ``Doctrine\Common\Persistence\Event\LifecycleEventArgs``.
+
+To tag a service as event listener and another service as event subscriber,
+use this configuration:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/services.yml
+ services:
+ app.phpcr_search_indexer:
+ class: App\EventListener\SearchIndexer
+ tags:
+ - { name: doctrine_phpcr.event_listener, event: postPersist }
+
+ app.phpcr_listener:
+ class: App\EventListener\MyListener
+ tags:
+ - { name: doctrine_phpcr.event_subscriber }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ use App\EventListener\SearchIndexer;
+ use App\EventListener\MyListener;
+
+ $container
+ ->register(
+ 'app.phpcr_search_indexer',
+ SearchIndexer::class
+ )
+ ->addTag('doctrine_phpcr.event_listener', [
+ 'event' => 'postPersist',
+ ])
+ ;
+
+ $container
+ ->register(
+ 'app.phpcr_listener',
+ MySubscriber::class
+ )
+ ->addTag('doctrine_phpcr.event_subscriber')
+ ;
+
+.. tip::
+
+ Doctrine event subscribers (both ORM and PHPCR-ODM) can **not** return a
+ flexible array of methods to call like the `Symfony event subscriber`_.
+ Doctrine event subscribers must return a simple array of the event
+ names they subscribe to. Doctrine will then expect methods on the
+ subscriber with the names of the subscribed events, just as when using an
+ event listener.
+
+You can find more information and examples of the doctrine event system
+in "`How to Register Event Listeners and Subscribers`_" of the core documentation.
+
+.. _`Doctrine PHPCR-ODM event system documentation`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/events.html
+.. _`Symfony event subscriber`: https://symfony.com/doc/current/components/event_dispatcher/introduction.html#using-event-subscribers
+.. _`Doctrine ORM events`: https://symfony.com/doc/current/doctrine/event_listeners_subscribers.html
+.. _`How to Register Event Listeners and Subscribers`: https://symfony.com/doc/current/doctrine/event_listeners_subscribers.html
diff --git a/doc/fixtures_initializers.rst b/doc/fixtures_initializers.rst
new file mode 100644
index 0000000..a55b721
--- /dev/null
+++ b/doc/fixtures_initializers.rst
@@ -0,0 +1,306 @@
+.. index::
+ single: Initializers; DoctrinePHPCRBundle
+ single: Fixtures; DoctrinePHPCRBundle
+ single: Migrators; DoctrinePHPCRBundle
+
+Maintaining Data in the Repository
+==================================
+
+PHPCR-ODM provides *initializers* that ensure a repository is ready for
+production use, *migrators* to programmatically load data and
+*fixture loading* for handling testing and demo fixtures.
+
+.. _phpcr-odm-repository-initializers:
+
+Repository Initializers
+-----------------------
+
+The Initializer is the PHPCR equivalent of the ORM schema tools. It is used to
+let your application PHPCR node types and to create required base paths in the
+repository.
+
+.. note::
+
+ The concept of base paths is needed because there are no separate "tables"
+ as in a relational database, but one tree containing all data. To be able
+ to add a document, you need to ensure the parent path is already present
+ in the repository.
+
+Initializers have to implement the
+``Doctrine\Bundle\PHPCRBundle\Initializer\InitializerInterface``. If you don't
+need any special logic and want to create plain PHPCR nodes and not documents,
+you can simply define services with ``GenericInitializer``. The generic
+Initializer expects a name to identify the Initializer, an array of repository
+paths it will create if they do not exist and an optional string defining
+namespaces and primary / mixin node types in the CND language that should be
+registered with the repository.
+
+A service to use the generic Initializer looks like this:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/services.yml
+ app.phpcr_initializer:
+ class: Doctrine\Bundle\PHPCRBundle\Initializer\GenericInitializer
+ arguments:
+ - App Basepaths
+ - ["/my/content", "/my/menu"]
+ - "%app.cnd%"
+ tags:
+ - { name: "doctrine_phpcr.initializer" }
+
+ .. code-block:: xml
+
+
+
+ App Basepaths
+
+ /my/content
+ /my/menu
+
+ %app.cnd%
+
+
+
+ .. code-block:: php
+
+ // app/config/services.php
+ use Doctrine\Bundle\PHPCRBundle\Initializer\GenericInitializer;
+ use Symfony\Component\DependencyInjection\Definition
+
+ // ...
+
+ $definition = new Definition(
+ GenericInitializer::class, [
+ 'App Basepaths',
+ ['/my/content', '/my/menu'],
+ '%app.cnd%',
+ ]
+ );
+ $definition->addTag('doctrine_phpcr.initializer');
+ $container->setDefinition('app.phpcr_initializer', $definition);
+
+You can execute your Initializers using the following command:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:repository:init
+
+.. note::
+ The load data fixtures command automatically executes the Initializers
+ after purging the database, before executing the fixtures.
+
+The generic Initializer only creates PHPCR nodes. If you want to create
+specific documents, you need your own Initializer. The interesting method
+to overwrite is the ``init`` method. It is passed the ``ManagerRegistry``,
+from which you can retrieve the PHPCR session but also the document manager::
+
+ // src/App/Initializer/SiteInitializer.php
+ namespace App\Initializer;
+
+ use App\Documents\Site;
+ use Doctrine\Bundle\PHPCRBundle\Initializer\InitializerInterface;
+ use Doctrine\Bundle\PHPCRBundle\ManagerRegistry;
+ use PHPCR\SessionInterface;
+ use PHPCR\Util\NodeHelper;
+
+ class SiteInitializer implements InitializerInterface
+ {
+ private $basePath;
+
+ public function __construct($basePath = '/cms')
+ {
+ $this->basePath = $basePath;
+ }
+
+ public function init(ManagerRegistry $registry)
+ {
+ $dm = $registry->getManagerForClass(Site::class);
+ if ($dm->find(null, $this->basePath)) {
+ return;
+ }
+
+ $site = new Site();
+ $site->setId($this->basePath);
+ $dm->persist($site);
+ $dm->flush();
+
+ $session = $registry->getConnection();
+ // create the 'cms', 'pages', and 'posts' nodes
+ NodeHelper::createPath($session, '/cms/pages');
+ NodeHelper::createPath($session, '/cms/posts');
+ NodeHelper::createPath($session, '/cms/routes');
+
+ $session->save();
+ }
+
+ public function getName()
+ {
+ return 'Site Initializer';
+ }
+ }
+
+Define a service for your Initializer as follows:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ services:
+ # ...
+ app.phpcr_initializer_site:
+ class: App\Initializer\SiteInitializer
+ tags:
+ - { name: doctrine_phpcr.initializer }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+
+ // ...
+ $container
+ ->register(
+ 'app.phpcr_initializer_site',
+ 'App\Initializer\SiteInitializer'
+ )
+ ->addTag('doctrine_phpcr.initializer', ['name' => 'doctrine_phpcr.initializer']
+ ;
+
+Migration Loading
+-----------------
+
+The DoctrinePHPCRBundle also ships with a simple command to run migration
+scripts. Migrations should implement the
+``Doctrine\Bundle\PHPCRBundle\Migrator\MigratorInterface`` and registered as a
+service with a ``doctrine_phpcr.migrator`` tag contains an ``alias`` attribute
+uniquely identifying the migrator. There is an optional
+``Doctrine\Bundle\PHPCRBundle\Migrator\AbstractMigrator`` class to use as a
+basis.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/services.yml
+ app.migration:
+ class: App\Migration\Migration
+ arguments:
+ - { "%app.content_basepath%", "%app.menu_basepath%" }
+ tags:
+ - { name: "doctrine_phpcr.migrator", alias: "app.migration" }
+
+ .. code-block:: xml
+
+
+
+
+
+
+ %app.content_basepath%
+ %app.menu_basepath%
+
+
+
+
+
+
+ .. code-block:: php
+
+ use App\Migration\Migration;
+ use Symfony\Component\DependencyInjection\Definition;
+
+ // ...
+ $definition = new Definition(Migration::class, [
+ [
+ '%app.content_basepath%',
+ '%app.menu_basepath%',
+ ],
+ ]);
+ $definition->addTag('doctrine_phpcr.migrator', ['alias' => 'app.migration']);
+
+ $container->setDefinition('app.migration', $definition);
+
+To find out available migrations run:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:migrator:migrate
+
+Then pass in the name of the migrator to run it, optionally passing in an
+``--identifier``, ``--depth`` or ``--session`` argument. The later argument
+determines which session name to set on the migrator, while the first two
+arguments will simply be passed to the ``migrate()`` method. You can find an
+example migrator in the SimpleCmsBundle.
+
+.. tip::
+
+ A simple alternative if you do not need to reproduce the result can be to
+ export part of your repository and re-import it on the target server. This
+ is described in :ref:`phpcr-odm-backup-restore`.
+
+.. _phpcr-odm-repository-fixtures:
+
+Fixture Loading
+---------------
+
+To use the ``doctrine:phpcr:fixtures:load`` command, you additionally need to
+install the `DoctrineFixturesBundle`_ which brings the
+`Doctrine data-fixtures`_ into Symfony.
+
+Fixtures work the same way they work for Doctrine ORM. You write fixture
+classes implementing ``Doctrine\Common\DataFixtures\FixtureInterface``. If you
+place them in ``\DataFixtures\PHPCR``, they will be auto detected if you
+don't specify a path in the command.
+
+A simple example fixture class looks like this::
+
+ // src/App/DataFixtures/PHPCR/LoadPageData.php
+ namespace App\DataFixtures\PHPCR;
+
+ use Doctrine\Common\Persistence\ObjectManager;
+ use Doctrine\Common\DataFixtures\FixtureInterface;
+ use Doctrine\ODM\PHPCR\DocumentManager;
+
+ class LoadPageData implements FixtureInterface
+ {
+ public function load(ObjectManager $manager)
+ {
+ if (!$manager instanceof DocumentManager) {
+ $class = get_class($manager);
+ throw new \RuntimeException("Fixture requires a PHPCR ODM DocumentManager instance, instance of '$class' given.");
+ }
+
+ // ... create and persist your data here
+ }
+ }
+
+For more on fixtures, see the documentation of the `DoctrineFixturesBundle`_.
+
+.. _`DoctrineFixturesBundle`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
+.. _`Doctrine data-fixtures`: https://github.com/doctrine/data-fixtures
diff --git a/doc/forms.rst b/doc/forms.rst
new file mode 100644
index 0000000..eb1bd34
--- /dev/null
+++ b/doc/forms.rst
@@ -0,0 +1,183 @@
+.. index::
+ single: Form Types; DoctrinePHPCRBundle
+
+Doctrine PHPCR-ODM Form Types
+=============================
+
+This bundle provides some handy form types for PHPCR and PHPCR-ODM specific
+cases, along with a type guesser that uses these types.
+
+There is also a validator constraint for PHPCR-ODM documents.
+
+Form Types
+----------
+
+.. tip::
+
+ When editing associative multivalue fields, have a look at the
+ BurgovKeyValueFormBundle_.
+
+phpcr_document
+~~~~~~~~~~~~~~
+
+This form type is suitable to edit associations of PHPCR-ODM documents. It
+works for ReferenceOne, ReferenceMany and Referrers but also for
+ParentDocument associations. Make sure to set the ``multiple`` option
+for ReferenceMany and Referrers, and to not set it for the others.
+
+.. note::
+
+ While ``Children`` is also an association, it makes no sense to edit it
+ with this form type. Children are automatically attached to their parent.
+ ``MixedReferrers`` could be shown as a ``disabled`` field but never edited,
+ because this association is immutable.
+
+This form type is equivalent to the ``entity`` form type provided by Symfony
+for Doctrine ORM. It has the same options as the ``entity`` type, including
+that the option for the document manager is called ``em``.
+
+A simple example of using the ``phpcr_document`` form type looks as follows::
+
+ use App\Document\TargetClass;
+
+ $form
+ ->add(
+ 'speakers',
+ 'phpcr_document',
+ [
+ 'property' => 'title',
+ 'class' => TargetClass::class,
+ 'multiple' => true,
+ ]
+ )
+ ;
+
+This will produce a multiple choice select field with the value of
+``getTitle`` called on each instance of ``TargetClass`` found in the
+content repository. Alternatively, you can set the ``choices`` option
+to a list of allowed managed documents. Please refer to the
+`Symfony documentation on the entity form type`_ for more details,
+including how you can configure a query.
+
+If you are using SonataDoctrinePHPCRAdminBundle_, you might want to look into
+``sonata_type_collection``. That form type allows to edit related
+documents (references as well as children) in-line and also to create
+and remove them on the fly.
+
+phpcr_odm_reference_collection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. caution::
+
+ This form type was deprecated in DoctrinePHPCRBundle 1.1 and will be
+ removed in DoctrinePHPCRBundle 1.2. You should use the `phpcr_document`_
+ type instead, which can do the same but better.
+
+This form type handles editing ``ReferenceMany`` collections on PHPCR-ODM
+documents. It is a choice field with an added ``referenced_class`` required
+option that specifies the class of the referenced target document.
+
+To use this form type, you also need to specify the list of possible reference
+targets as an array of PHPCR-ODM ids or PHPCR paths.
+
+The minimal code required to use this type looks as follows::
+
+ use App\Document\Article;
+
+ $dataArr = [
+ '/some/phpcr/path/item_1' => 'first item',
+ '/some/phpcr/path/item_2' => 'second item',
+ ];
+
+ $formMapper
+ ->with('form.group_general')
+ ->add('myCollection', 'phpcr_odm_reference_collection', [
+ 'choices' => $dataArr,
+ 'referenced_class' => Article::class,
+ ])
+ ->end();
+
+.. tip::
+
+ When building an admin interface with the SonataDoctrinePHPCRAdminBundle_
+ there is also the ``sonata_type_model``, which is more powerful, allowing to
+ add to the referenced documents on the fly.
+
+phpcr_reference
+~~~~~~~~~~~~~~~
+
+The ``phpcr_reference`` represents a PHPCR Property of type REFERENCE or
+WEAKREFERENCE within a form. The input will be rendered as a text field
+containing either the PATH or the UUID as per the configuration. The form will
+resolve the path or id back to a PHPCR node to set the reference.
+
+This type extends the ``text`` form type. It adds an option
+``transformer_type`` that can be set to either ``path`` or ``uuid``.
+
+
+Validator Constraint
+--------------------
+
+The bundle provides a ``ValidPhpcrOdm`` constraint validator you can use to
+check if your document ``Id`` or ``Nodename`` and ``Parent`` fields are
+correct.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # src/App/Resources/config/validation.yml
+ App\Document\Author:
+ constraints:
+ - Doctrine\Bundle\PHPCRBundle\Validator\Constraints\ValidPhpcrOdm
+
+ .. code-block:: php
+
+ // src/App/Document/Author.php
+
+ // ...
+ use Doctrine\Bundle\PHPCRBundle\Validator\Constraints as OdmAssert;
+
+ #[OdmAssert\ValidPhpcrOdm]
+ class Author
+ {
+ // ...
+ }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // src/App/Document/Author.php
+
+ // ...
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+ use Doctrine\Bundle\PHPCRBundle\Validator\Constraints as OdmAssert;
+
+ #[OdmAssert\ValidPhpcrOdm]
+ class Author
+ {
+ // ...
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata)
+ {
+ $metadata->addConstraint(new OdmAssert\ValidPhpcrOdm());
+ }
+ }
+
+.. _BurgovKeyValueFormBundle: https://github.com/Burgov/KeyValueFormBundle
+.. _`Symfony documentation on the entity form type`: https://symfony.com/doc/current/reference/forms/types/entity.html
+.. _SonataDoctrinePHPCRAdminBundle: https://sonata-project.org/bundles/doctrine-phpcr-admin/master/doc/index.html
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..bd00e80
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,14 @@
+DoctrinePHPCRBundle
+===================
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction
+ models
+ events
+ forms
+ fixtures_initializers
+ multilang
+ multiple_sessions
+ configuration
diff --git a/doc/introduction.rst b/doc/introduction.rst
new file mode 100644
index 0000000..92847cb
--- /dev/null
+++ b/doc/introduction.rst
@@ -0,0 +1,568 @@
+.. index::
+ single: PHPCR; Bundles
+ single: DoctrinePHPCRBundle
+
+DoctrinePHPCRBundle
+===================
+
+The `DoctrinePHPCRBundle`_ provides integration with the PHP content
+repository and optionally with Doctrine PHPCR-ODM to provide the ODM document
+manager in symfony.
+
+Out of the box, this bundle supports the following PHPCR implementations:
+
+* `Jackalope`_ (Jackrabbit, Doctrine DBAL and prismic transports)
+
+.. tip::
+
+ This reference only explains the Symfony integration of PHPCR and
+ PHPCR-ODM. To learn how to use PHPCR, refer to `the PHPCR website`_ and
+ for Doctrine PHPCR-ODM to the `PHPCR-ODM documentation`_.
+
+Setup
+-----
+
+Requirements
+~~~~~~~~~~~~
+
+* When using **jackalope-jackrabbit**: Java, Apache Jackalope and ``libxml``
+ version >= 2.7.0 (due to a `bug in libxml`_)
+* When using **jackalope-doctrine-dbal with MySQL**: MySQL >= 5.1.5
+ (as you need the xml function ``ExtractValue``)
+
+Installation
+------------
+
+You can install this bundle `with composer`_ using the
+`doctrine/phpcr-bundle`_ package. You need a concrete implementation of
+the PHPCR API. For this example, we assume that you require Jackalope Doctrine
+DBAL. See the `PHPCR-ODM documentation` for alternatives.
+
+If you want to use PHPCR-ODM, you additionally need to require
+``doctrine/phpcr-odm``.
+
+.. code-block:: javascript
+
+ require: {
+ ...
+ "jackalope/jackalope-doctrine-dbal": "1.2.*",
+ "doctrine/phpcr-odm": "1.2.*",
+ "doctrine/phpcr-bundle": "1.2.*",
+ ...
+ }
+
+Besides the ``DoctrinePHPCRBundle`` you also need to instantiate the base
+``DoctrineBundle`` in your kernel::
+
+ // app/AppKernel.php
+
+ // ...
+ class AppKernel extends Kernel
+ {
+ public function registerBundles()
+ {
+ $bundles = [
+ // ...
+ new Doctrine\Bundle\PHPCRBundle\DoctrinePHPCRBundle(),
+ new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
+ ];
+
+ // ...
+ }
+
+ // ...
+ }
+
+Configuration
+-------------
+
+PHPCR Session Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The session needs a PHPCR implementation specified in the ``backend`` section
+by the ``type`` field, along with configuration options to bootstrap the
+implementation. The examples here assume that you are using Jackalope Doctrine
+DBAL. The full documentation is in the :doc:`configuration reference `.
+
+To use Jackalope Doctrine DBAL, you need to configure a database connection
+with the DoctrineBundle. For detailed information, see the
+`Symfony Doctrine documentation`_. A simple example is:
+
+.. code-block:: yaml
+
+ # app/config/parameters.yml
+ parameters:
+ database_driver: pdo_mysql
+ database_host: localhost
+ database_name: test_project
+ database_user: root
+ database_password: password
+
+ # ...
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine:
+ dbal:
+ driver: "%database_driver%"
+ host: "%database_host%"
+ dbname: "%database_name%"
+ user: "%database_user%"
+ password: "%database_password%"
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $configuration->loadFromExtension('doctrine', [
+ 'dbal' => [
+ 'driver' => '%database_driver%',
+ 'host' => '%database_host%',
+ 'dbname' => '%database_name%',
+ 'user' => '%database_user%',
+ 'password' => '%database_password%',
+ ],
+ ]);
+
+Jackalope Doctrine DBAL provides a PHPCR implementation without any
+installation requirements beyond any of the RDBMS supported by Doctrine.
+Once you set up Doctrine DBAL, you can configure Jackalope:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ backend:
+ type: doctrinedbal
+ # connection: default
+
+ # requires DoctrineCacheBundle
+ # caches:
+ # meta: doctrine_cache.providers.phpcr_meta
+ # nodes: doctrine_cache.providers.phpcr_nodes
+ # enable logging
+ logging: true
+ # enable profiling in the debug toolbar.
+ profiling: true
+ workspace: default
+ username: admin
+ password: admin
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'backend' => [
+ 'type' => 'doctrinedbal',
+ //'connection': 'default',
+ 'logging' => true,
+ 'profiling' => true,
+ //'caches' => [
+ // 'meta' => 'doctrine_cache.providers.phpcr_meta'
+ // 'nodes' => 'doctrine_cache.providers.phpcr_nodes'
+ //],
+ ],
+ 'workspace' => 'default',
+ 'username' => 'admin',
+ 'password' => 'admin',
+ ],
+ ]);
+
+Now make sure the database exists and initialize it:
+
+.. code-block:: bash
+
+ # without Doctrine ORM
+ php bin/console doctrine:database:create
+ php bin/console doctrine:phpcr:init:dbal
+
+.. tip::
+
+ You can also use a different doctrine dbal connection instead of the
+ default. Specify the dbal connection name in the ``connection`` option of
+ the ``backend`` configuration.
+
+ It is recommended to use a separate connection to a separate database if
+ you also use Doctrine ORM or direct DBAL access to data, rather than
+ mixing this data with the tables generated by Jackalope Doctrine Dbal. If
+ you have a separate connection, you need to pass the alternate connection
+ name to the ``doctrine:database:create`` command with the ``--connection``
+ option. For Doctrine PHPCR commands, this parameter is not needed as you
+ configured the connection to use.
+
+If you are using Doctrine ORM on the same connection, the schema is integrated
+into ``doctrine:schema:create|update|drop`` and also `DoctrineMigrationsBundle`_
+so that you can create migrations.
+
+.. code-block:: bash
+
+ # Using Doctrine ORM
+ php bin/console doctrine:database:create
+ php bin/console doctrine:schema:create
+
+.. note::
+
+ To use the cache, install and configure the DoctrineCacheBundle and uncomment
+ the cache meta and nodes settings.
+
+Doctrine PHPCR-ODM Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This configuration section manages the document mapper system that converts
+your PHPCR nodes to domain model objects. If you do not configure anything
+here, the ODM services will not be loaded.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ odm:
+ auto_mapping: true
+ auto_generate_proxy_classes: "%kernel.debug%"
+ mappings:
+ App:
+ mapping: true
+ type: attribute
+ dir: '%kernel.root_dir%/Document'
+ alias: App
+ prefix: App\Document\
+ is_bundle: false
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'odm' => [
+ 'auto_mapping' => true,
+ 'auto_generate_proxy_classes' => '%kernel.debug%',
+ 'mappings' => [
+ # Configure document mappings
+ 'App' => [
+ 'mapping' => true,
+ 'type' => 'attribute',
+ 'dir' => '%kernel.root_dir%/Document',
+ 'alias' => 'App',
+ 'prefix' => 'App\Document\',
+ 'is-bundle' => false,
+ ],
+ ],
+ ],
+ ]);
+
+When ``auto_mapping`` is enabled, bundles will be automatically loaded and
+attempted to resolve mappings
+
+.. tip::
+
+ For bundles, unless you disable ``auto_mapping``, you can place your
+ documents in the ``Document`` folder inside your bundles and use
+ attribute or name the mapping files following this convention:
+ ``/Resources/config/doctrine/.phpcr.xml`` or
+ ``*.phpcr.yml``.
+
+If ``auto_generate_proxy_classes`` is false, you need to run the
+``cache:warmup`` command in order to have the proxy classes generated after
+you modified a document. This is usually done in production to gain some performance.
+
+For applications, it is usually required to define ``mappings``. In a standard
+minimal setup, an ``App`` definition as shown in above example is required,
+which maps ``App\Document\`` documents in the ``src/Document`` directory.
+
+See :doc:`configuration`, for complete details.
+
+
+Registering System Node Types
+"""""""""""""""""""""""""""""
+
+PHPCR-ODM uses a `custom node type`_ to track meta information without
+interfering with your content. There is a command that makes it trivial to
+register this type and the PHPCR namespace, as well as all base paths of
+bundles:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:repository:init
+
+You only need to run this command once when you created a new repository. (But
+nothing goes wrong if you run it on each deployment for example.)
+
+Profiling and Performance of Jackalope
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using any of the Jackalope PHPCR implementations, you can activate logging
+to log to the symfony log, or profiling to show information in the Symfony
+debug toolbar:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ backend:
+ # ...
+ logging: true
+ profiling: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.yml
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'backend' => [
+ // ...
+ 'logging' => true,
+ 'profiling' => true,
+ ],
+ ],
+ ]);
+
+Now that you can see the effects of changes, you can try if adjusting the global
+fetch depth reduces the number and duration for queries. Set the option
+``jackalope.fetch_depth`` to something bigger than 0 to have Jackalope pre-fetch
+children or whole subtrees. This can reduce the number of queries needed, but
+watch out for longer queries because more data is fetched.
+
+When using Jackalope Doctrine DBAL, it is highly recommended to activate the
+caching options.
+
+Note that you can also set the fetch-depth on the session on the fly for
+specific calls, or use the fetch-depth option on children mappings of your
+documents.
+
+The parameter ``jackalope.check_login_on_server`` can be set to false to save
+an initial call to the database to check if the connection works.
+
+Services
+--------
+
+There are 3 main services provided by this bundle:
+
+* ``Doctrine\Bundle\PHPCRBundle\ManagerRegistry``- The ``ManagerRegistry``
+ instance with references to all sessions and document manager instances;
+* ``PHPCR\SessionInterface`` - the PHPCR session. If you configured
+ multiple sessions, this will be the default session;
+* ``Doctrine\ODM\PHPCR\DocumentManagerInterface`` - the PHPCR-ODM document
+ manager. If you configured multiple managers, this will be the default
+ manager.
+
+.. _bundles-phpcr-odm-commands:
+
+Doctrine PHPCR Commands
+-----------------------
+
+All commands about PHPCR are prefixed with ``doctrine:phpcr`` and you can use
+the --session argument to use a non-default session if you configured several
+PHPCR sessions.
+
+Some of these commands are specific to a backend or to the ODM. Those commands
+will only be available if such a backend is configured.
+
+Use ``php bin/console help `` to see all options each of the commands
+has.
+
+* **doctrine:phpcr:document:migrate-class**: Command to migrate document classes;
+* **doctrine:phpcr:fixtures:load**: Load data fixtures to your PHPCR database;
+* **doctrine:phpcr:init:dbal**: Prepare the database for Jackalope Doctrine-Dbal;
+* **doctrine:phpcr:jackrabbit**: Start and stop the Jackrabbit server;
+* **doctrine:phpcr:mapping:info**: Shows basic information about all mapped documents;
+* **doctrine:phpcr:migrator:migrate**: Migrates PHPCR data;
+* **doctrine:phpcr:node-type:list**: List all available node types in the repository;
+* **doctrine:phpcr:node-type:register**: Register node types in the PHPCR repository;
+* **doctrine:phpcr:node:dump**: Dump subtrees of the content repository;
+* **doctrine:phpcr:node:move**: Moves a node from one path to another;
+* **doctrine:phpcr:node:remove**: Remove content from the repository;
+* **doctrine:phpcr:node:touch**: Create or modify a node;
+* **doctrine:phpcr:nodes:update**: Command to manipulate the nodes in the workspace;
+* **doctrine:phpcr:repository:init**: Initialize the PHPCR repository;
+* **doctrine:phpcr:workspace:create**: Create a workspace in the configured repository;
+* **doctrine:phpcr:workspace:export**: Export nodes from the repository,
+ either to the JCR system view format or the document view format;
+* **doctrine:phpcr:workspace:import**: Import xml data into the repository,
+ either in JCR system view format or arbitrary xml;
+* **doctrine:phpcr:workspace:list**: List all available workspaces in the configured repository;
+* **doctrine:phpcr:workspace:purge**: Remove all nodes from a workspace;
+* **doctrine:phpcr:workspace:query**: Execute a JCR SQL2 statement.
+
+.. note::
+
+ To use the ``doctrine:phpcr:fixtures:load`` command, you additionally need
+ to install the `DoctrineFixturesBundle`_ and its dependencies. See
+ :ref:`phpcr-odm-repository-fixtures` for how to use fixtures.
+
+Some Example Command Runs
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Running `SQL2 queries`_ against the repository:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:workspace:query "SELECT title FROM [nt:unstructured] WHERE NAME() = 'home'"
+
+Dumping nodes under ``/cms/simple`` including their properties:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:node:dump /cms/simple --props
+
+.. _phpcr-odm-backup-restore:
+
+Simple Backup and Restore
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To export all repository data into a file, you can use:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:workspace:export --path /cms /path/to/backup.xml
+
+.. note::
+
+ You always want to specify a path to export. Without any path you will
+ export the root node of the repository, which will be imported later as
+ ``jcr:root``.
+
+To restore this backup you can run:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:workspace:import /path/to/backup.xml
+
+Note that you can also export and import parts of your repository by choosing a
+different path on export and specifying the ``--parentpath`` option to the
+import.
+
+If you already have data in your repository that you want to replace, you can
+remove the target node first:
+
+.. code-block:: bash
+
+ $ php bin/console doctrine:phpcr:node:remove /cms
+
+Read On
+-------
+
+* :doc:`models`
+* :doc:`events`
+* :doc:`forms`
+* :doc:`fixtures_initializers`
+* :doc:`multilang`
+* :doc:`multiple_sessions`
+* :doc:`configuration`
+
+.. _`PHPCR-ODM documentation`: https://www.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/index.html
+.. _`DoctrinePHPCRBundle`: https://github.com/doctrine/DoctrinePHPCRBundle
+.. _`Symfony Doctrine documentation`: https://symfony.com/doc/current/doctrine.html
+.. _`Jackalope`: http://jackalope.github.io/
+.. _`the PHPCR website`: https://phpcr.github.io/
+.. _`bug in libxml`: https://bugs.php.net/bug.php?id=36501
+.. _`with composer`: https://getcomposer.org
+.. _`doctrine/phpcr-bundle`: https://packagist.org/packages/doctrine/phpcr-bundle
+.. _`custom node type`: https://github.com/doctrine/phpcr-odm/wiki/Custom-node-type-phpcr%3Amanaged
+.. _`DoctrineMigrationsBundle`: https://symfony.com/bundles/DoctrineMigrationsBundle/current/index.html
+.. _`DoctrineFixturesBundle`: https://symfony.com/bundles/DoctrineFixturesBundle/current/index.html
+.. _`SQL2 queries`: http://www.h2database.com/jcr/grammar.html
diff --git a/doc/models.rst b/doc/models.rst
new file mode 100644
index 0000000..46c245f
--- /dev/null
+++ b/doc/models.rst
@@ -0,0 +1,342 @@
+.. index::
+ single: PHPCR; Bundles
+ single: DoctrinePHPCRBundle
+
+Modeling Data with PHPCR-ODM
+============================
+
+The Doctrine PHPCR-ODM is a doctrine object-mapper on top of the
+`PHP Content Repository`_ (PHPCR), which is a PHP adaption of the
+`JSR-283 specification`_. The most important feature of PHPCR is the tree
+structure to store the data. All data is stored in items of a tree, called
+nodes. You can think of this like a file system, that makes it perfect to use
+in a CMS.
+
+On top of the tree structure, PHPCR also adds features like searching,
+versioning and access control.
+
+Doctrine PHPCR-ODM has the same API as the other Doctrine libraries, like the
+`Doctrine ORM`_. The Doctrine PHPCR-ODM adds another great feature to PHPCR:
+multi-language support.
+
+A Simple Example: A Task
+------------------------
+
+The easiest way to get started with the PHPCR-ODM is to see it in action. In
+this section, you are going to create a ``Task`` object and learn how to
+persist it.
+
+Creating a Document Class
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Without thinking about Doctrine or PHPCR-ODM, you can create a ``Task`` object
+in PHP::
+
+ // src/App/Document/Task.php
+ namespace use App\Document;
+
+ class Task
+ {
+ protected $description;
+
+ protected $done = false;
+ }
+
+This class - often called a "document" in PHPCR-ODM, meaning *a basic class
+that holds data* - is simple and helps fulfill the business requirement of
+needing tasks in your application. This class can't be persisted to
+Doctrine PHPCR-ODM yet - it's just a simple PHP class.
+
+.. note::
+
+ A Document is analogous to the term ``Entity`` employed by the Doctrine
+ ORM. To have the mapping happen automatically, place your documents in the
+ ``Document`` namespace within your application.
+
+Add Mapping Information
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Doctrine allows you to work with PHPCR in a much more interesting way than
+just fetching data back and forth as an array. Instead, Doctrine allows you to
+persist entire objects to PHPCR and fetch entire *objects* out of PHPCR.
+This works by mapping a PHP class and its properties to the PHPCR tree.
+
+For Doctrine to be able to do this, you just have to create "metadata", or
+configuration that tells Doctrine exactly how the ``Task`` document and its
+properties should be *mapped* to PHPCR. This metadata can be specified in a
+number of different formats including YAML, XML or directly inside the ``Task``
+class via attributes:
+
+.. configuration-block::
+
+ .. code-block:: php
+
+ // src/App/Document/Task.php
+ namespace App\Document;
+
+ use Doctrine\ODM\PHPCR\Mapping\Attributes as PHPCR;
+
+ #[PHPCR\Document]
+ class Task
+ {
+ #[PHPCR\Id]
+ private $id;
+
+ #[PHPCR\Field(type: 'string')]
+ private $description;
+
+ #[PHPCR\Field(type: 'boolean')]
+ private $done = false;
+
+ #[PHPCR\ParentDocument]
+ private $parentDocument;
+ }
+
+ .. code-block:: yaml
+
+ # src/App/Resources/config/doctrine/Task.phpcr.yml
+ App\Document\Task:
+ id: id
+
+ fields:
+ description: string
+ done: boolean
+
+ parent_document: parentDocument
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+After this, you have to create getters and setters for the properties.
+
+.. note::
+
+ This Document uses the parent document and a node name to determine its
+ position in the tree. Because there isn't any name set, it is generated
+ automatically. If you want to use a specific node name, such as a
+ slugified version of the title, you need to add a property mapped as
+ ``Nodename``.
+
+ A Document must have an id property. This represents the full path (parent
+ path + name) of the Document. This will be set by Doctrine by default and
+ it is not recommend to use the id to determine the location of a Document.
+
+ For more information about identifier generation strategies, refer to the
+ `doctrine documentation`_
+
+.. tip::
+
+ You may want to implement ``Doctrine\ODM\PHPCR\HierarchyInterface`` to
+ expose the hierarchy in a standardized way.
+
+.. seealso::
+
+ You can also check out Doctrine's `Basic Mapping Documentation`_ for all
+ details about mapping information. If you use attributes, you'll need to
+ prepend all attributes with ``PHPCR\``, which is the name of the imported
+ namespace (e.g. ``#[PHPCR\Document(..)]``), this is not shown in Doctrine's
+ documentation. You'll also need to include the
+ ``use Doctrine\ODM\PHPCR\Mapping\Attributes as PHPCR;`` statement to
+ import the PHPCR attributes prefix.
+
+Persisting Documents to PHPCR
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that you have a mapped ``Task`` document, complete with getter and setter
+methods, you are ready to persist data to PHPCR. For a simple example, lets do
+this from inside a controller::
+
+ // src/App/Controller/DefaultController.php
+
+ // ...
+ use App\Document\Task;
+ use Doctrine\ODM\PHPCR\DocumentManagerInterface;
+ use Symfony\Component\HttpFoundation\Response;
+
+ // ...
+ public function createAction(DocumentManagerInterface $documentManager)
+ {
+ $rootTask = $documentManager->find(null, '/tasks');
+
+ $task = new Task();
+ $task->setDescription('Finish CMF project');
+ $task->setParentDocument($rootTask);
+
+ $documentManager->persist($task);
+
+ $documentManager->flush();
+
+ return new Response('Created task "'.$task->getDescription().'"');
+ }
+
+Take a look at the previous example in more detail:
+
+* **line 8** We use symfony controller injection with autowiring to get the
+ *document manager*. This service is responsible for storing and fetching
+ objects to and from PHPCR.
+* **line 10** This line loads the root document for the tasks, as each PHPCR
+ document needs to have a parent. To create this root document, you can
+ configure a :ref:`Repository Initializer `,
+ which will be executed when running ``doctrine:phpcr:repository:init``.
+* **lines 12-14** In this section, you instantiate and work with the ``$task``
+ object like any other, normal PHP object.
+* **line 16** The ``persist()`` method tells Doctrine to "manage" the ``$task``
+ object. This does not actually cause a query to be made to PHPCR (yet).
+* **line 20** When the ``flush()`` method is called, Doctrine looks through all
+ of the objects that it is managing to see if they need to be stored to PHPCR.
+ In this example, the ``$task`` object has not been saved yet, so the document
+ manager makes a query to PHPCR to add it.
+
+When creating or updating objects, the workflow is always the same. In the
+next section, you'll see how Doctrine is smart enough to update documents if
+they already exist in PHPCR.
+
+Fetching Objects from PHPCR
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Fetching an object back out of PHPCR is even easier. For example, suppose
+you've configured a route to display a specific task by name::
+
+ use App\Document\Task;
+ use Doctrine\ODM\PHPCR\DocumentManagerInterface;
+
+ public function showAction(DocumentManagerInterface $documentManager, $name)
+ {
+ $repository = $documentManager->getRepository(Task::class);
+ $task = $repository->find('/tasks/'.$name);
+
+ if (!$task) {
+ throw $this->createNotFoundException('No task found with name '.$name);
+ }
+
+ return new Response('['.($task->isDone() ? 'x' : ' ').'] '.$task->getDescription());
+ }
+
+To retrieve objects from the document repository using both the ``find`` and
+``findMany`` methods and all helper methods of a class-specific repository. In
+PHPCR, it's often unknown for developers which node has the data for a specific
+document, in that case you should use the document manager to find the nodes
+(for instance, when you want to get the root document). In example above, we
+know they are ``Task`` documents and so we can use the repository.
+
+The repository contains all sorts of helpful methods::
+
+ // query by the id (full path)
+ $task = $repository->find($id);
+
+ // query for one task matching be name and done
+ $task = $repository->findOneBy(['name' => 'foo', 'done' => false]);
+
+ // query for all tasks matching the name, ordered by done
+ $tasks = $repository->findBy(
+ ['name' => 'foo'],
+ ['done' => 'ASC']
+ );
+
+.. tip::
+
+ If you use the repository class, you can also create a custom repository
+ for a specific document. This helps with "Separation of Concern" when using more
+ complex queries. This is similar to how it's done in Doctrine ORM, for
+ more information read "`Custom Repository Classes`_" in the core
+ documentation.
+
+.. tip::
+
+ You can also query objects by using the Query Builder provided by
+ Doctrine PHPCR-ODM. For more information, read
+ `the QueryBuilder documentation`_.
+
+Updating an Object
+~~~~~~~~~~~~~~~~~~
+
+Once you've fetched an object from Doctrine, updating it is easy. Suppose you
+have a route that maps a task ID to an update action in a controller::
+
+ use App\Document\Task;
+ use Doctrine\ODM\PHPCR\DocumentManagerInterface;
+
+ public function updateAction(DocumentManagerInterface $documentManager, $name)
+ {
+ $repository = $documentManager->getRepository(Task::class);
+ $task = $repository->find('/tasks/'.$name);
+
+ if (!$task) {
+ throw $this->createNotFoundException('No task found for name '.$name);
+ }
+
+ if (!$task->isDone()) {
+ $task->setDone(true);
+ }
+
+ $documentManager->flush();
+
+ return new Response('[x] '.$task->getDescription());
+ }
+
+Updating an object involves just three steps:
+
+#. fetching the object from Doctrine;
+#. modifying the object;
+#. calling ``flush()`` on the document manager
+
+Notice that calling ``$documentManger->persist($task)`` isn't necessary.
+Recall that this method simply tells Doctrine to manage or "watch" the
+``$task`` object. In this case, since you fetched the ``$task`` object from
+Doctrine, it's already managed.
+
+Deleting an Object
+~~~~~~~~~~~~~~~~~~
+
+Deleting an object is very similar, but requires a call to the ``remove()``
+method of the document manager after you fetched the document from PHPCR::
+
+ $documentManager->remove($task);
+ $documentManager->flush();
+
+As you might expect, the ``remove()`` method notifies Doctrine that you'd like
+to remove the given document from PHPCR. The actual delete operation
+however, is not actually executed until the ``flush()`` method is called.
+
+Summary
+-------
+
+With Doctrine, you can focus on your objects and how they're useful in your
+application and worry about database persistence second. This is because
+Doctrine allows you to use any PHP object to hold your data and relies on
+mapping metadata information to map an object's data to a particular database
+table.
+
+And even though Doctrine revolves around a simple concept, it's incredibly
+powerful, allowing you to `create complex queries`_ and
+:doc:`subscribe to events ` that allow you to take different actions as
+objects go through their persistence lifecycle.
+
+.. _`PHP Content Repository`: http://phpcr.github.io/
+.. _`JSR-283 specification`: https://jcp.org/en/jsr/detail?id=283
+.. _`Doctrine ORM`: https://symfony.com/doc/current/doctrine.html
+.. _`doctrine documentation`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/basic-mapping.html#basicmapping-identifier-generation-strategies
+.. _`Basic Mapping Documentation`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/basic-mapping.html
+.. _`the QueryBuilder documentation`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/query-builder.html
+.. _`create complex queries`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/query-builder.html
+.. _`Custom Repository Classes`: https://symfony.com/doc/current/doctrine/repository.html
diff --git a/doc/multilang.rst b/doc/multilang.rst
new file mode 100644
index 0000000..bec5413
--- /dev/null
+++ b/doc/multilang.rst
@@ -0,0 +1,197 @@
+.. index::
+ single: Multi-Language; DoctrinePHPCRBundle
+
+Doctrine PHPCR-ODM Multi-Language Support
+=========================================
+
+PHPCR-ODM can handle translated documents. All translations of the same
+document are considered the same document. Only one language version can be
+loaded at the same time.
+
+Note that the CMF routing component does not use translated routes but has a
+`separate route per language`_.
+
+To use the multi-language features of PHPCR-ODM you need to enable locales in
+the configuration.
+
+Translation Configuration
+-------------------------
+
+To use translated documents, you need to configure the available languages:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ odm:
+ # ...
+ locales:
+ en: [de, fr]
+ de: [en, fr]
+ fr: [en, de]
+ locale_fallback: hardcoded
+ default_locale: fr
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ de
+ fr
+
+
+
+ en
+ fr
+
+
+
+ en
+ de
+
+
+ fr
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'odm' => [
+ // ...
+ 'locales' => [
+ 'en' => ['de', 'fr'],
+ 'de' => ['en', 'fr'],
+ 'fr' => ['en', 'de'],
+ ],
+ 'locale_fallback' => 'hardcoded',
+ 'default_locale' => 'fr',
+ ]
+ ]);
+
+The ``locales`` is a list of alternative locales to look up if a document
+is not translated to the requested locale.
+
+The default locale is used for the standard locale chooser strategy and
+hence will be the default locale in the document manager. Specifying the
+default locale is optional. If you do not specify a default locale then the
+first locale listed is used as the default locale.
+
+This bundle provides a request listener that gets activated when any locales
+are configured. This listener updates PHPCR-ODM to use the locale Symfony
+determined for this request, if that locale is in the list of keys defined
+under ``locales``.
+
+Fallback strategies
+~~~~~~~~~~~~~~~~~~~
+
+There are several strategies to adjust the fallback order for the selected
+locale based on the accepted languages of the request (determined by Symfony
+from the ``Accept-Language`` HTML header). All of them will never add any
+locales that where not configured in the ``locales`` to avoid a request
+injecting unexpected things into your repository:
+
+* ``hardcoded``: This strategy does not update the fallback order from
+ the request;
+* ``replace``: takes the accepted locales from the request and updates the
+ fallback order with them, removing any locales not found in the request;
+* ``merge``: does the same as ``replace`` but then adds locales not found in
+ the request but on the ``locales`` configuration back to the end of the
+ fallback list. This reorders the locales without losing any of them. This is
+ the default strategy.
+
+Translated documents
+--------------------
+
+To make a document translated, you need to define the ``translator`` attribute
+on the document mapping, and you need to map the ``locale`` field. Then you can
+use the ``translated`` attribute on all fields that should be different
+depending on the locale.
+
+.. configuration-block::
+
+ .. code-block:: php
+
+ // src/App/Documents/Article.php
+ namespace App\Documents\Article;
+
+ use Doctrine\ODM\PHPCR\Mapping\Attributes as PHPCR;
+
+ #[PHPCR\Document(translator: 'attribute')]
+ class Article
+ {
+ /**
+ * The language this document currently is in
+ */
+ #[PHPCR\Locale]
+ private $locale;
+
+ /**
+ * Untranslated property
+ */
+ #[PHPCR\Date]
+ private $publishDate;
+
+ /**
+ * Translated property
+ */
+ #[PHPCR\Field(type: 'string', translated: true)]
+ private $topic;
+
+ /**
+ * Language specific image
+ */
+ #[PHPCR\Binary(translated: true)]
+ private $image;
+ }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: yaml
+
+ App\Documents\Article:
+ translator: attribute
+ locale: locale
+ fields:
+ publishDate:
+ type: date
+ topic:
+ type: string
+ translated: true
+ image:
+ type: binary
+ translated: true
+
+Unless you explicitly interact with the multi-language features of PHPCR-ODM,
+documents are loaded in the request locale and saved in the locale they where
+loaded. (This could be a different locale, if the PHPCR-ODM did not find the
+requested locale and had to fall back to an alternative locale.)
+
+.. tip::
+
+ For more information on multilingual documents, see the
+ `PHPCR-ODM documentation on multi-language`_.
+
+.. _`PHPCR-ODM documentation on multi-language`: http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/reference/multilang.html
+.. _`separate route per language`: https://symfony.com/bundles/CMFRoutingBundle/current/routing-component/dynamic.html
diff --git a/doc/multiple_sessions.rst b/doc/multiple_sessions.rst
new file mode 100644
index 0000000..f71cf8c
--- /dev/null
+++ b/doc/multiple_sessions.rst
@@ -0,0 +1,348 @@
+.. index::
+ single: Multisession; DoctrinePHPCRBundle
+
+Configuring multiple sessions for PHPCR-ODM
+===========================================
+
+If you need more than one PHPCR backend, you can define ``sessions`` as child
+of the ``session`` information. Each session has a name and the configuration
+following the same schema as what is directly in ``session``. You can also
+overwrite which session to use as ``default_session``. Once you have multiple
+sessions, you can also configure multiple document managers with those
+sessions.
+
+.. tip::
+
+ Autowiring always gives you the default session and the default document
+ manager. When working with multiple sessions and managers, you need to
+ explicitly specify the services. For the document managers, you can also
+ go through the manager registry (see at the end of this page).
+
+.. _bundles-phpcr-odm-multiple-phpcr-sessions:
+
+Multiple PHPCR Sessions
+-----------------------
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ doctrine_phpcr:
+ session:
+ default_session: ~
+ sessions:
+ :
+ workspace: ... # Required
+ username: ~
+ password: ~
+ backend:
+ # ...
+ options:
+ # ...
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'default_session' => null,
+ 'sessions' => [
+ '' => [
+ 'workspace' => '...', // Required
+ 'username' => null,
+ 'password' => null,
+ 'backend' => [
+ // ...
+ ],
+ 'options' => [
+ // ...
+ ],
+ ],
+ ],
+ ],
+ ]);
+
+Multiple Document Managers
+--------------------------
+
+If you are using the ODM, you will also want to configure multiple document
+managers.
+
+Inside the odm section, you can add named entries in the
+``document_managers``. To use the non-default session, specify the session
+attribute.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ odm:
+ default_document_manager: ~
+ document_managers:
+ :
+ session:
+ # ... configuration as above
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'odm' => [
+ 'default_document_manager' => null,
+ 'document_managers' => [
+ '' => [
+ 'session' => '',
+ // ... configuration as above
+ ],
+ ],
+ ],
+ ]);
+
+Bringing it all together
+------------------------
+
+The following full example uses the default manager for ``AppBundle``
+and the documents provided by the CMF. Additionally, it has a website
+and DMS manager that connects to the Jackrabbit of Magnolia CMS. That
+manager looks for models in the MagnoliaBundle.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ doctrine_phpcr:
+ # configure the PHPCR sessions
+ session:
+ sessions:
+ default:
+ backend: "%phpcr_backend%"
+ workspace: "%phpcr_workspace%"
+ username: "%phpcr_user%"
+ password: "%phpcr_pass%"
+
+ website:
+ backend:
+ type: jackrabbit
+ url: "%magnolia_url%"
+ workspace: website
+ username: "%magnolia_user%"
+ password: "%magnolia_pass%"
+
+ dms:
+ backend:
+ type: jackrabbit
+ url: "%magnolia_url%"
+ workspace: dms
+ username: "%magnolia_user%"
+ password: "%magnolia_pass%"
+
+ # enable the ODM layer
+ odm:
+ auto_generate_proxy_classes: "%kernel.debug%"
+ document_managers:
+ default:
+ session: default
+ mappings:
+ AppBundle: ~
+ CmfContentBundle: ~
+ CmfMenuBundle: ~
+ CmfRoutingBundle: ~
+
+ website:
+ session: website
+ configuration_id: magnolia.odm_configuration
+ mappings:
+ MagnoliaBundle: ~
+
+ dms:
+ session: dms
+ configuration_id: magnolia.odm_configuration
+ mappings:
+ MagnoliaBundle: ~
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('doctrine_phpcr', [
+ 'session' => [
+ 'sessions' => [
+ 'default' => [
+ 'backend' => '%phpcr_backend%',
+ 'workspace' => '%phpcr_workspace%',
+ 'username' => '%phpcr_user%',
+ 'password' => '%phpcr_pass%',
+ ],
+ 'website' => [
+ 'backend' => [
+ 'type' => 'jackrabbit',
+ 'url' => '%magnolia_url%',
+ ],
+ 'workspace' => 'website',
+ 'username' => '%magnolia_user%',
+ 'password' => '%magnolia_pass%',
+ ],
+ 'dms' => [
+ 'backend' => [
+ 'type' => 'jackrabbit',
+ 'url' => '%magnolia_url%',
+ ],
+ 'workspace' => 'dms',
+ 'username' => '%magnolia_user%',
+ 'password' => '%magnolia_pass%',
+ ],
+ ],
+ ],
+
+ // enable the ODM layer
+ 'odm' => [
+ 'auto_generate_proxy_classes' => '%kernel.debug%',
+ 'document_managers' => [
+ 'default' => [
+ 'session' => 'default',
+ 'mappings' => [
+ 'AppBundle' => null,
+ 'CmfContentBundle' => null,
+ 'CmfMenuBundle' => null,
+ 'CmfRoutingBundle' => null,
+ ],
+ ],
+ 'website' => [
+ 'session' => 'website',
+ 'configuration_id' => 'magnolia.odm_configuration',
+ 'mappings' => [
+ 'MagnoliaBundle' => null,
+ ],
+ ],
+ 'dms' => [
+ 'session' => 'dms',
+ 'configuration_id' => 'magnolia.odm_configuration',
+ 'mappings' => [
+ 'MagnoliaBundle' => null,
+ ],
+ ],
+ ],
+ ],
+ ]);
+
+
+You can access the managers through the manager registry available in the
+service ``Doctrine\Bundle\PHPCRBundle\ManagerRegistry``::
+
+ use Doctrine\Bundle\PHPCRBundle\ManagerRegistry;
+
+ /** @var $container \Symfony\Component\DependencyInjection\ContainerInterface */
+
+ // get the named manager from the registry
+ $dm = $container->get(ManagerRegistry::class)->getManager('website');
+
+ // get the manager for a specific document class
+ $dm = $container->get(ManagerRegistry::class)->getManagerForClass('CmfContentBundle:StaticContent');
+
+Additionally, each manager is available as a service in the DI container.
+The service name pattern is ``doctrine_phpcr.odm._document_manager`` so for
+example the website manager is called
+``doctrine_phpcr.odm.website_document_manager``.