The start of something beautiful
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
Copyright (c) 2011 Fabien Potencier, Doctrine Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Doctrine Bundle
|
||||
|
||||
Doctrine DBAL & ORM Bundle for the Symfony Framework.
|
||||
|
||||
[](https://github.com/doctrine/DoctrineBundle/actions/workflows/continuous-integration.yml) [](https://codecov.io/gh/doctrine/DoctrineBundle)
|
||||
|
||||
## What is Doctrine?
|
||||
|
||||
The Doctrine Project is the home of a selected set of PHP libraries primarily focused on providing persistence
|
||||
services and related functionality. Its prize projects are a Object Relational Mapper and the Database Abstraction
|
||||
Layer it is built on top of. You can read more about the projects below or view a list of all projects.
|
||||
|
||||
Object relational mapper (ORM) for PHP that sits on top of a powerful database abstraction layer (DBAL).
|
||||
One of its key features is the option to write database queries in a proprietary object oriented SQL dialect
|
||||
called Doctrine Query Language (DQL), inspired by Hibernates HQL. This provides developers with a powerful
|
||||
alternative to SQL that maintains flexibility without requiring unnecessary code duplication.
|
||||
|
||||
DBAL is a powerful database abstraction layer with many features for database schema introspection,
|
||||
schema management and PDO abstraction.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation is rendered on [the symfony.com website](https://symfony.com/doc/current/reference/configuration/doctrine.html).
|
||||
The source of the documentation is available in the docs folder.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
UPGRADE FROM 2.9 to 2.10
|
||||
========================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### Preparing for a new `report_fields_where_declared` mapping driver mode
|
||||
|
||||
Doctrine ORM 2.16+ makes a change to how the annotations and attribute mapping drivers report fields inherited from parent classes. For details, see https://github.com/doctrine/orm/pull/10455. It will trigger a deprecation notice unless the new mode is activated. In ORM 3.0, the new mode will be the only one.
|
||||
|
||||
The new mode ~does not~ should not make a difference for regular, valid use cases, but may lead to `MappingException`s for users with certain configurations that were not meant to be supported by the ORM in the first place. To avoid surprising users (even when their configuration is invalid) during a 2.16 _minor_ version upgrade, the transition to this new mode was implemented as an opt-in. This way, you can try and deal with the change any time you see fit.
|
||||
|
||||
In version 2.10+ of this bundle, a new configuration setting `report_fields_where_declared` was added at the entity manager configuration level. Set it to `true` to switch the mapping driver for the corresponding entity manager to the new mode. It is only relevant for mapping configurations using attributes or annotations.
|
||||
|
||||
Unless you set it to `true`, Doctrine ORM will emit deprecation messages mentioning this new setting.
|
||||
|
||||
### Preparing for the XSD validation for XML drivers
|
||||
|
||||
Doctrine ORM 2.14+ adds support for validating the XSD of XML mapping files. In ORM 3.0, this validation will be mandatory.
|
||||
|
||||
As the ecosystem is known to rely on custom elements in the XML mapping files that are forbidden when validating the XSD (for instance when using `gedmo/doctrine-extensions`), this validation is opt-in thanks to a `validate_xml_mapping` setting at the entity manager configuration level.
|
||||
|
||||
Unless you set it to `true`, Doctrine ORM will emit deprecation messages mentioning the XSD validation.
|
||||
|
||||
### Deprecations
|
||||
|
||||
- `Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface` has been deprecated. Use the `#[AsDoctrineListener]` attribute instead.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
UPGRADE FROM 2.11 to 2.12
|
||||
========================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### Controller resolver auto mapping default configuration will be changed
|
||||
|
||||
The default value of `doctrine.orm.controller_resolver.auto_mapping` will be changed from `true` to `false` in 3.0.
|
||||
|
||||
Auto mapping uses any route parameter that matches with a field name of the Entity to resolve as criteria in a find by query.
|
||||
|
||||
If you are relying on this functionality, you will need to configure it explicitly to silence the deprecation notice.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
UPGRADE FROM 2.12 to 2.13
|
||||
========================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### Controller resolver auto mapping deprecated
|
||||
|
||||
The controller resolver auto mapping functionality has been deprecated with Symfony 7.1, and is replaced with explicit mapped route parameters. Enabling the auto mapper by default using this bundle is now deprecated as well.
|
||||
|
||||
Auto mapping uses any route parameter that matches with a field name of the Entity to resolve as criteria in a find by query.
|
||||
|
||||
If you are relying on this functionality, you can update your code to use explicit mapped route parameters instead.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
UPGRADE FROM 2.x to 3.0
|
||||
=======================
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### Controller resolver auto mapping can no longer be configured
|
||||
|
||||
The `doctrine.orm.controller_resolver.auto_mapping` option now only accepts `false` as value, to disallow the usage of the controller resolver auto mapping feature by default. The configuration option will be fully removed in 4.0.
|
||||
|
||||
Auto mapping used any route parameter that matches with a field name of the Entity to resolve as criteria in a find by query. This method has been deprecated in Symfony 7.1 and is replaced with mapped route parameters.
|
||||
|
||||
If you were relying on this functionality, you will need to update your code to use explicit mapped route parameters instead.
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
* The `commented` configuration option for types is no longer supported and
|
||||
deprecated.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"name": "doctrine/doctrine-bundle",
|
||||
"description": "Symfony DoctrineBundle",
|
||||
"license": "MIT",
|
||||
"type": "symfony-bundle",
|
||||
"keywords": [
|
||||
"DBAL",
|
||||
"ORM",
|
||||
"Database",
|
||||
"Persistence"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Doctrine Project",
|
||||
"homepage": "https://www.doctrine-project.org/"
|
||||
}
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org",
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"doctrine/cache": "^1.11 || ^2.0",
|
||||
"doctrine/dbal": "^3.7.0 || ^4.0",
|
||||
"doctrine/persistence": "^2.2 || ^3",
|
||||
"doctrine/sql-formatter": "^1.0.1",
|
||||
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/config": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/console": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/deprecation-contracts": "^2.1 || ^3",
|
||||
"symfony/doctrine-bridge": "^5.4.19 || ^6.0.7 || ^7.0",
|
||||
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/polyfill-php80": "^1.15",
|
||||
"symfony/service-contracts": "^1.1.1 || ^2.0 || ^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1 || ^2",
|
||||
"doctrine/coding-standard": "^12",
|
||||
"doctrine/deprecations": "^1.0",
|
||||
"doctrine/orm": "^2.17 || ^3.0",
|
||||
"friendsofphp/proxy-manager-lts": "^1.0",
|
||||
"phpunit/phpunit": "^9.5.26",
|
||||
"psalm/plugin-phpunit": "^0.18.4",
|
||||
"psalm/plugin-symfony": "^5",
|
||||
"psr/log": "^1.1.4 || ^2.0 || ^3.0",
|
||||
"symfony/phpunit-bridge": "^6.1 || ^7.0",
|
||||
"symfony/property-info": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/proxy-manager-bridge": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/security-bundle": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/stopwatch": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/string": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/twig-bridge": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/validator": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/var-exporter": "^5.4 || ^6.2 || ^7.0",
|
||||
"symfony/web-profiler-bundle": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/yaml": "^5.4 || ^6.0 || ^7.0",
|
||||
"twig/twig": "^1.34 || ^2.12 || ^3.0",
|
||||
"vimeo/psalm": "^5.15"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": ">=3.0",
|
||||
"doctrine/orm": "<2.17 || >=4.0",
|
||||
"twig/twig": "<1.34 || >=2.0 <2.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pdo": "*",
|
||||
"doctrine/orm": "The Doctrine ORM integration is optional in the bundle.",
|
||||
"symfony/web-profiler-bundle": "To use the data collector."
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Bundle\\DoctrineBundle\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Bundle\\DoctrineBundle\\Tests\\": "tests",
|
||||
"Fixtures\\": "tests/DependencyInjection/Fixtures"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"symfony/flex": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd",
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||
}
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="doctrine.dbal.configuration.class">Doctrine\DBAL\Configuration</parameter>
|
||||
<parameter key="doctrine.data_collector.class">Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector</parameter>
|
||||
<parameter key="doctrine.dbal.connection.event_manager.class">Symfony\Bridge\Doctrine\ContainerAwareEventManager</parameter>
|
||||
<parameter key="doctrine.dbal.connection_factory.class">Doctrine\Bundle\DoctrineBundle\ConnectionFactory</parameter>
|
||||
<parameter key="doctrine.dbal.events.mysql_session_init.class">Doctrine\DBAL\Event\Listeners\MysqlSessionInit</parameter>
|
||||
<parameter key="doctrine.dbal.events.oracle_session_init.class">Doctrine\DBAL\Event\Listeners\OracleSessionInit</parameter>
|
||||
<parameter key="doctrine.class">Doctrine\Bundle\DoctrineBundle\Registry</parameter>
|
||||
<parameter key="doctrine.entity_managers" type="collection"></parameter>
|
||||
<parameter key="doctrine.default_entity_manager"></parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="Doctrine\DBAL\Connection" alias="database_connection" public="false" />
|
||||
|
||||
<service id="data_collector.doctrine" class="%doctrine.data_collector.class%" public="false">
|
||||
<tag name="data_collector" template="@Doctrine/Collector/db.html.twig" id="db" priority="250" />
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument>true</argument>
|
||||
<argument type="service" id="doctrine.debug_data_holder" on-invalid="null"/>
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.connection_factory" class="%doctrine.dbal.connection_factory.class%">
|
||||
<argument>%doctrine.dbal.connection_factory.types%</argument>
|
||||
<argument type="service" id="doctrine.dbal.connection_factory.dsn_parser" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.connection_factory.dsn_parser" class="Doctrine\DBAL\Tools\DsnParser">
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.connection" class="Doctrine\DBAL\Connection" abstract="true">
|
||||
<factory service="doctrine.dbal.connection_factory" method="createConnection" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.connection.event_manager" class="%doctrine.dbal.connection.event_manager.class%" public="false" abstract="true">
|
||||
<argument type="service" id="service_container" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.connection.configuration" class="%doctrine.dbal.configuration.class%" public="false" abstract="true" />
|
||||
|
||||
<service id="doctrine" class="%doctrine.class%" public="true">
|
||||
<argument type="service" id="service_container" />
|
||||
<argument>%doctrine.connections%</argument>
|
||||
<argument>%doctrine.entity_managers%</argument>
|
||||
<argument>%doctrine.default_connection%</argument>
|
||||
<argument>%doctrine.default_entity_manager%</argument>
|
||||
<tag name="kernel.reset" method="reset" />
|
||||
</service>
|
||||
<service id="Doctrine\Persistence\ManagerRegistry" alias="doctrine" public="false" />
|
||||
<service id="Doctrine\Common\Persistence\ManagerRegistry" alias="doctrine" public="false" />
|
||||
|
||||
<service id="doctrine.twig.doctrine_extension" class="Doctrine\Bundle\DoctrineBundle\Twig\DoctrineExtension" public="false">
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.schema_asset_filter_manager" class="Doctrine\Bundle\DoctrineBundle\Dbal\SchemaAssetsFilterManager" public="false" abstract="true">
|
||||
<!-- schema assets filters -->
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.well_known_schema_asset_filter" class="Doctrine\Bundle\DoctrineBundle\Dbal\BlacklistSchemaAssetFilter" public="false">
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
||||
<!-- commands -->
|
||||
<service id="doctrine.database_create_command" class="Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand">
|
||||
<argument type="service" id="doctrine" />
|
||||
|
||||
<tag name="console.command" command="doctrine:database:create" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.database_drop_command" class="Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand">
|
||||
<argument type="service" id="doctrine" />
|
||||
|
||||
<tag name="console.command" command="doctrine:database:drop" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.query_sql_command" class="Doctrine\Bundle\DoctrineBundle\Command\Proxy\RunSqlDoctrineCommand">
|
||||
<argument type="service" id="Doctrine\Bundle\DoctrineBundle\Dbal\ManagerRegistryAwareConnectionProvider" on-invalid="null" />
|
||||
|
||||
<tag name="console.command" command="doctrine:query:sql" />
|
||||
</service>
|
||||
|
||||
<service id="Doctrine\DBAL\Tools\Console\Command\RunSqlCommand">
|
||||
<argument type="service" id="Doctrine\Bundle\DoctrineBundle\Dbal\ManagerRegistryAwareConnectionProvider" on-invalid="null" />
|
||||
|
||||
<tag name="console.command" command="dbal:run-sql" />
|
||||
</service>
|
||||
|
||||
<service id="Doctrine\Bundle\DoctrineBundle\Controller\ProfilerController">
|
||||
<argument type="service" id="twig" />
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument type="service" id="profiler" />
|
||||
|
||||
<tag name="controller.service_arguments" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.idle_connection_listener" class="Symfony\Bridge\Doctrine\Middleware\IdleConnection\Listener">
|
||||
<argument type="service" id="doctrine.dbal.connection_expiries" />
|
||||
<argument type="service" id="service_container" />
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.dbal.default_schema_manager_factory" class="Doctrine\DBAL\Schema\DefaultSchemaManagerFactory" />
|
||||
<service id="doctrine.dbal.legacy_schema_manager_factory" class="Doctrine\DBAL\Schema\LegacySchemaManagerFactory" />
|
||||
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<!--
|
||||
The following service isn't prefixed by the "doctrine.orm" namespace in order for end-users to just use
|
||||
the "doctrine_transaction" shortcut in message buses middleware config
|
||||
-->
|
||||
<service id="messenger.middleware.doctrine_transaction" class="Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware" abstract="true" public="false">
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The following service isn't prefixed by the "doctrine.orm" namespace in order for end-users to just use
|
||||
the "doctrine_ping_connection" shortcut in message buses middleware config
|
||||
-->
|
||||
<service id="messenger.middleware.doctrine_ping_connection" class="Symfony\Bridge\Doctrine\Messenger\DoctrinePingConnectionMiddleware" abstract="true" public="false">
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The following service isn't prefixed by the "doctrine.orm" namespace in order for end-users to just use
|
||||
the "doctrine_close_connection" shortcut in message buses middleware config
|
||||
-->
|
||||
<service id="messenger.middleware.doctrine_close_connection" class="Symfony\Bridge\Doctrine\Messenger\DoctrineCloseConnectionMiddleware" abstract="true" public="false">
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The following service isn't prefixed by the "doctrine.orm" namespace in order for end-users to just use
|
||||
the "doctrine_open_transaction_logger" shortcut in message buses middleware config
|
||||
-->
|
||||
<service id="messenger.middleware.doctrine_open_transaction_logger" class="Symfony\Bridge\Doctrine\Messenger\DoctrineOpenTransactionLoggerMiddleware" abstract="true" public="false">
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument>null</argument>
|
||||
<argument type="service" id="logger" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager" class="Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber" public="false">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The services below will be removed conditionally in DoctrineExtension, if symfony/doctrine-messenger is not installed.
|
||||
-->
|
||||
<service id="messenger.transport.doctrine.factory" class="Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransportFactory" public="false">
|
||||
<argument type="service" id="doctrine" />
|
||||
<tag name="messenger.transport_factory" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.messenger.doctrine_schema_subscriber" class="Symfony\Bridge\Doctrine\SchemaListener\MessengerTransportDoctrineSchemaSubscriber">
|
||||
<argument type="tagged_iterator" tag="messenger.receiver" />
|
||||
<tag name="doctrine.event_subscriber" />
|
||||
</service>
|
||||
<service id="doctrine.orm.messenger.doctrine_schema_listener" class="Symfony\Bridge\Doctrine\SchemaListener\MessengerTransportDoctrineSchemaListener">
|
||||
<argument type="tagged_iterator" tag="messenger.receiver" />
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" />
|
||||
<tag name="doctrine.event_listener" event="onSchemaCreateTable" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="doctrine.dbal.connection_expiries" class="ArrayObject" />
|
||||
<service id="doctrine.dbal.logging_middleware" class="Doctrine\DBAL\Logging\Middleware" abstract="true">
|
||||
<argument type="service" id="logger" />
|
||||
<tag name="monolog.logger" channel="doctrine" />
|
||||
</service>
|
||||
<service id="doctrine.debug_data_holder" class="Doctrine\Bundle\DoctrineBundle\Middleware\BacktraceDebugDataHolder">
|
||||
<argument type="collection" />
|
||||
<tag name="kernel.reset" method="reset" />
|
||||
</service>
|
||||
<service id="doctrine.dbal.debug_middleware" class="Doctrine\Bundle\DoctrineBundle\Middleware\DebugMiddleware" abstract="true">
|
||||
<argument type="service" id="doctrine.debug_data_holder" />
|
||||
<argument type="service" id="debug.stopwatch" on-invalid="null" />
|
||||
</service>
|
||||
<service id="doctrine.dbal.idle_connection_middleware" class="Doctrine\Bundle\DoctrineBundle\Middleware\IdleConnectionMiddleware" abstract="true">
|
||||
<argument type="service" id="doctrine.dbal.connection_expiries" />
|
||||
<argument /> <!-- check timing -->
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="doctrine.orm.configuration.class">Doctrine\ORM\Configuration</parameter>
|
||||
<parameter key="doctrine.orm.entity_manager.class">Doctrine\ORM\EntityManager</parameter>
|
||||
<parameter key="doctrine.orm.manager_configurator.class">Doctrine\Bundle\DoctrineBundle\ManagerConfigurator</parameter>
|
||||
|
||||
<!-- cache -->
|
||||
<parameter key="doctrine.orm.cache.array.class">Doctrine\Common\Cache\ArrayCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.apc.class">Doctrine\Common\Cache\ApcCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcache.class">Doctrine\Common\Cache\MemcacheCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcache_host">localhost</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcache_port">11211</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcache_instance.class">Memcache</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcached.class">Doctrine\Common\Cache\MemcachedCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcached_host">localhost</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcached_port">11211</parameter>
|
||||
<parameter key="doctrine.orm.cache.memcached_instance.class">Memcached</parameter>
|
||||
<parameter key="doctrine.orm.cache.redis.class">Doctrine\Common\Cache\RedisCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.redis_host">localhost</parameter>
|
||||
<parameter key="doctrine.orm.cache.redis_port">6379</parameter>
|
||||
<parameter key="doctrine.orm.cache.redis_instance.class">Redis</parameter>
|
||||
<parameter key="doctrine.orm.cache.xcache.class">Doctrine\Common\Cache\XcacheCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.wincache.class">Doctrine\Common\Cache\WinCacheCache</parameter>
|
||||
<parameter key="doctrine.orm.cache.zenddata.class">Doctrine\Common\Cache\ZendDataCache</parameter>
|
||||
|
||||
<!-- metadata -->
|
||||
<parameter key="doctrine.orm.metadata.driver_chain.class">Doctrine\Persistence\Mapping\Driver\MappingDriverChain</parameter>
|
||||
<parameter key="doctrine.orm.metadata.annotation.class">Doctrine\ORM\Mapping\Driver\AnnotationDriver</parameter>
|
||||
<parameter key="doctrine.orm.metadata.xml.class">Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver</parameter>
|
||||
<parameter key="doctrine.orm.metadata.yml.class">Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver</parameter>
|
||||
<parameter key="doctrine.orm.metadata.php.class">Doctrine\ORM\Mapping\Driver\PHPDriver</parameter>
|
||||
<parameter key="doctrine.orm.metadata.staticphp.class">Doctrine\ORM\Mapping\Driver\StaticPHPDriver</parameter>
|
||||
<parameter key="doctrine.orm.metadata.attribute.class">Doctrine\ORM\Mapping\Driver\AttributeDriver</parameter>
|
||||
|
||||
<!-- cache warmer -->
|
||||
<parameter key="doctrine.orm.proxy_cache_warmer.class">Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer</parameter>
|
||||
|
||||
<!-- form field factory guesser -->
|
||||
<parameter key="form.type_guesser.doctrine.class">Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser</parameter>
|
||||
|
||||
<!-- validator -->
|
||||
<parameter key="doctrine.orm.validator.unique.class">Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator</parameter>
|
||||
<parameter key="doctrine.orm.validator_initializer.class">Symfony\Bridge\Doctrine\Validator\DoctrineInitializer</parameter>
|
||||
|
||||
<!-- security -->
|
||||
<parameter key="doctrine.orm.security.user.provider.class">Symfony\Bridge\Doctrine\Security\User\EntityUserProvider</parameter>
|
||||
|
||||
<!-- listeners -->
|
||||
<parameter key="doctrine.orm.listeners.resolve_target_entity.class">Doctrine\ORM\Tools\ResolveTargetEntityListener</parameter>
|
||||
<parameter key="doctrine.orm.listeners.attach_entity_listeners.class">Doctrine\ORM\Tools\AttachEntityListenersListener</parameter>
|
||||
|
||||
<!-- naming strategy -->
|
||||
<parameter key="doctrine.orm.naming_strategy.default.class">Doctrine\ORM\Mapping\DefaultNamingStrategy</parameter>
|
||||
<parameter key="doctrine.orm.naming_strategy.underscore.class">Doctrine\ORM\Mapping\UnderscoreNamingStrategy</parameter>
|
||||
|
||||
<!-- quote strategy -->
|
||||
<parameter key="doctrine.orm.quote_strategy.default.class">Doctrine\ORM\Mapping\DefaultQuoteStrategy</parameter>
|
||||
<parameter key="doctrine.orm.quote_strategy.ansi.class">Doctrine\ORM\Mapping\AnsiQuoteStrategy</parameter>
|
||||
|
||||
<!-- typed field mapper -->
|
||||
<parameter key="doctrine.orm.typed_field_mapper.default.class">Doctrine\ORM\Mapping\DefaultTypedFieldMapper</parameter>
|
||||
|
||||
<!-- entity listener resolver -->
|
||||
<parameter key="doctrine.orm.entity_listener_resolver.class">Doctrine\Bundle\DoctrineBundle\Mapping\ContainerEntityListenerResolver</parameter>
|
||||
|
||||
<!-- second level cache -->
|
||||
<parameter key="doctrine.orm.second_level_cache.default_cache_factory.class">Doctrine\ORM\Cache\DefaultCacheFactory</parameter>
|
||||
<parameter key="doctrine.orm.second_level_cache.default_region.class">Doctrine\ORM\Cache\Region\DefaultRegion</parameter>
|
||||
<parameter key="doctrine.orm.second_level_cache.filelock_region.class">Doctrine\ORM\Cache\Region\FileLockRegion</parameter>
|
||||
<parameter key="doctrine.orm.second_level_cache.logger_chain.class">Doctrine\ORM\Cache\Logging\CacheLoggerChain</parameter>
|
||||
<parameter key="doctrine.orm.second_level_cache.logger_statistics.class">Doctrine\ORM\Cache\Logging\StatisticsCacheLogger</parameter>
|
||||
<parameter key="doctrine.orm.second_level_cache.cache_configuration.class">Doctrine\ORM\Cache\CacheConfiguration</parameter>
|
||||
<parameter key="doctrine.orm.second_level_cache.regions_configuration.class">Doctrine\ORM\Cache\RegionsConfiguration</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="Doctrine\ORM\EntityManagerInterface" alias="doctrine.orm.entity_manager" public="false" />
|
||||
|
||||
<!--- Internal Annotation Metadata Reader Service alias, use annotation_reader service -->
|
||||
<service id="doctrine.orm.metadata.annotation_reader" alias="annotation_reader" public="false" />
|
||||
|
||||
<service id="doctrine.orm.proxy_cache_warmer" class="%doctrine.orm.proxy_cache_warmer.class%" public="false">
|
||||
<tag name="kernel.cache_warmer" />
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="form.type_guesser.doctrine" class="%form.type_guesser.doctrine.class%">
|
||||
<tag name="form.type_guesser" />
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="form.type.entity" class="Symfony\Bridge\Doctrine\Form\Type\EntityType">
|
||||
<tag name="form.type" alias="entity" />
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.configuration" class="%doctrine.orm.configuration.class%" abstract="true" public="false" />
|
||||
|
||||
<service id="doctrine.orm.entity_manager.abstract" class="%doctrine.orm.entity_manager.class%" abstract="true" lazy="true" />
|
||||
|
||||
<service id="doctrine.orm.container_repository_factory" class="Doctrine\Bundle\DoctrineBundle\Repository\ContainerRepositoryFactory" public="false">
|
||||
<argument type="service">
|
||||
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
</argument>
|
||||
</service>
|
||||
|
||||
<!-- The configurator cannot be a private service -->
|
||||
<service id="doctrine.orm.manager_configurator.abstract" class="%doctrine.orm.manager_configurator.class%" abstract="true">
|
||||
<argument type="collection" />
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
||||
<!-- validator -->
|
||||
<service id="doctrine.orm.validator.unique" class="%doctrine.orm.validator.unique.class%">
|
||||
<tag name="validator.constraint_validator" alias="doctrine.orm.validator.unique" />
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.validator_initializer" class="%doctrine.orm.validator_initializer.class%">
|
||||
<tag name="validator.initializer" />
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<!-- security -->
|
||||
<service id="doctrine.orm.security.user.provider" class="%doctrine.orm.security.user.provider.class%" abstract="true" public="false">
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<!-- listeners -->
|
||||
<service id="doctrine.orm.listeners.resolve_target_entity" class="%doctrine.orm.listeners.resolve_target_entity.class%" public="false" />
|
||||
<service id="doctrine.orm.listeners.doctrine_dbal_cache_adapter_schema_subscriber" class="Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaSubscriber">
|
||||
<argument type="collection" /> <!-- DoctrineDbalAdapter instances -->
|
||||
<tag name="doctrine.event_subscriber" />
|
||||
</service>
|
||||
<service id="doctrine.orm.listeners.doctrine_dbal_cache_adapter_schema_listener" class="Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaListener">
|
||||
<argument type="collection" /> <!-- DoctrineDbalAdapter instances -->
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" />
|
||||
</service>
|
||||
<service id="doctrine.orm.listeners.pdo_cache_adapter_doctrine_schema_subscriber" class="Symfony\Bridge\Doctrine\SchemaListener\PdoCacheAdapterDoctrineSchemaSubscriber">
|
||||
<argument type="collection" /> <!-- PdoAdapter instances -->
|
||||
<tag name="doctrine.event_subscriber" />
|
||||
</service>
|
||||
<service id="doctrine.orm.listeners.doctrine_token_provider_schema_subscriber" class="Symfony\Bridge\Doctrine\SchemaListener\RememberMeTokenProviderDoctrineSchemaSubscriber">
|
||||
<argument type="tagged_iterator" tag="security.remember_me_handler" />
|
||||
<tag name="doctrine.event_subscriber" />
|
||||
</service>
|
||||
<service id="doctrine.orm.listeners.doctrine_token_provider_schema_listener" class="Symfony\Bridge\Doctrine\SchemaListener\RememberMeTokenProviderDoctrineSchemaListener">
|
||||
<argument type="tagged_iterator" tag="security.remember_me_handler" />
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" />
|
||||
</service>
|
||||
<service id="doctrine.orm.listeners.pdo_session_handler_schema_listener" class="Symfony\Bridge\Doctrine\SchemaListener\PdoSessionHandlerSchemaListener">
|
||||
<argument type="service" id="session.handler" />
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" />
|
||||
</service>
|
||||
<service id="doctrine.orm.listeners.lock_store_schema_listener" class="Symfony\Bridge\Doctrine\SchemaListener\LockStoreSchemaListener">
|
||||
<argument type="tagged_iterator" tag="lock.store" />
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" />
|
||||
</service>
|
||||
|
||||
<!-- naming strategy -->
|
||||
<service id="doctrine.orm.naming_strategy.default" class="%doctrine.orm.naming_strategy.default.class%" public="false" />
|
||||
<service id="doctrine.orm.naming_strategy.underscore" class="%doctrine.orm.naming_strategy.underscore.class%" public="false" />
|
||||
<service id="doctrine.orm.naming_strategy.underscore_number_aware" class="%doctrine.orm.naming_strategy.underscore.class%" public="false">
|
||||
<argument type="constant">CASE_LOWER</argument>
|
||||
<argument>true</argument>
|
||||
</service>
|
||||
|
||||
<!-- quote strategy -->
|
||||
<service id="doctrine.orm.quote_strategy.default" class="%doctrine.orm.quote_strategy.default.class%" public="false" />
|
||||
<service id="doctrine.orm.quote_strategy.ansi" class="%doctrine.orm.quote_strategy.ansi.class%" public="false" />
|
||||
|
||||
<!-- typed field mapper -->
|
||||
<service id="doctrine.orm.typed_field_mapper.default" class="%doctrine.orm.typed_field_mapper.default.class%" public="false" />
|
||||
|
||||
<!-- custom id generators -->
|
||||
<service id="doctrine.ulid_generator" class="Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator">
|
||||
<argument type="service" id="ulid.factory" on-invalid="ignore" />
|
||||
<tag name="doctrine.id_generator" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.uuid_generator" class="Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator">
|
||||
<argument type="service" id="uuid.factory" on-invalid="ignore" />
|
||||
<tag name="doctrine.id_generator" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.command.entity_manager_provider" class="Doctrine\Bundle\DoctrineBundle\Orm\ManagerRegistryAwareEntityManagerProvider">
|
||||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.entity_value_resolver" class="Symfony\Bridge\Doctrine\ArgumentResolver\EntityValueResolver">
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument type="service" id="doctrine.orm.entity_value_resolver.expression_language" on-invalid="ignore" />
|
||||
<tag name="Symfony\Bridge\Doctrine\ArgumentResolver\EntityValueResolver" priority="110">controller.argument_value_resolver</tag>
|
||||
</service>
|
||||
|
||||
<service id="doctrine.orm.entity_value_resolver.expression_language" class="Symfony\Component\ExpressionLanguage\ExpressionLanguage" />
|
||||
|
||||
<!-- commands -->
|
||||
<service id="doctrine.cache_clear_metadata_command" class="Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:cache:clear-metadata" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.cache_clear_query_cache_command" class="Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:cache:clear-query" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.cache_clear_result_command" class="Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:cache:clear-result" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.cache_collection_region_command" class="Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:cache:clear-collection-region" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.mapping_convert_command" class="Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:mapping:convert" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.schema_create_command" class="Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:schema:create" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.schema_drop_command" class="Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:schema:drop" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.ensure_production_settings_command" class="Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:ensure-production-settings" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.clear_entity_region_command" class="Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:cache:clear-entity-region" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.mapping_info_command" class="Doctrine\ORM\Tools\Console\Command\InfoCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:mapping:info" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.clear_query_region_command" class="Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:cache:clear-query-region" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.query_dql_command" class="Doctrine\ORM\Tools\Console\Command\RunDqlCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:query:dql" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.schema_update_command" class="Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:schema:update" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.schema_validate_command" class="Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand">
|
||||
<argument type="service" id="doctrine.orm.command.entity_manager_provider" />
|
||||
<tag name="console.command" command="doctrine:schema:validate" />
|
||||
</service>
|
||||
|
||||
<service id="doctrine.mapping_import_command" class="Doctrine\Bundle\DoctrineBundle\Command\ImportMappingDoctrineCommand">
|
||||
<argument type="service" id="doctrine" />
|
||||
<argument>%kernel.bundles%</argument>
|
||||
|
||||
<tag name="console.command" command="doctrine:mapping:import" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,294 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<xsd:schema xmlns="http://symfony.com/schema/dic/doctrine"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://symfony.com/schema/dic/doctrine"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xsd:element name="config">
|
||||
<xsd:complexType>
|
||||
<xsd:all>
|
||||
<xsd:element name="dbal" type="dbal" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="orm" type="orm" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:all>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="named_scalar">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- DBAL configuration -->
|
||||
|
||||
<xsd:attributeGroup name="connection-config">
|
||||
<xsd:attribute name="driver" type="xsd:string" />
|
||||
<xsd:attribute name="driver-class" type="xsd:string" />
|
||||
<xsd:attribute name="wrapper-class" type="xsd:string" />
|
||||
<xsd:attribute name="keep-slave" type="xsd:string" />
|
||||
<xsd:attribute name="keep-replica" type="xsd:string" />
|
||||
<xsd:attribute name="platform-service" type="xsd:string" />
|
||||
<xsd:attribute name="auto-commit" type="xsd:string" />
|
||||
<xsd:attribute name="schema-filter" type="xsd:string" />
|
||||
<xsd:attribute name="logging" type="xsd:string" default="false" />
|
||||
<xsd:attribute name="profiling" type="xsd:string" default="false" />
|
||||
<xsd:attribute name="profiling-collect-backtrace" type="xsd:string" default="false" />
|
||||
<xsd:attribute name="profiling-collect-schema-errors" type="xsd:string" default="true" />
|
||||
<xsd:attribute name="server-version" type="xsd:string" />
|
||||
<xsd:attribute name="schema-manager-factory" type="xsd:string" />
|
||||
<xsd:attribute name="result-cache" type="xsd:string" />
|
||||
<xsd:attribute name="use-savepoints" type="xsd:boolean" />
|
||||
<xsd:attribute name="disable-type-comments" type="xsd:boolean" />
|
||||
<xsd:attributeGroup ref="driver-config" />
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:attributeGroup name="driver-config">
|
||||
<xsd:attribute name="url" type="xsd:string" />
|
||||
<xsd:attribute name="dbname" type="xsd:string" />
|
||||
<xsd:attribute name="host" type="xsd:string" />
|
||||
<xsd:attribute name="port" type="xsd:string" />
|
||||
<xsd:attribute name="user" type="xsd:string" />
|
||||
<xsd:attribute name="password" type="xsd:string" />
|
||||
<xsd:attribute name="override-url" type="xsd:boolean" />
|
||||
<xsd:attribute name="dbname-suffix" type="xsd:string" />
|
||||
<xsd:attribute name="application-name" type="xsd:string" />
|
||||
<xsd:attribute name="path" type="xsd:string" />
|
||||
<xsd:attribute name="unix-socket" type="xsd:string" />
|
||||
<xsd:attribute name="memory" type="xsd:string" />
|
||||
<xsd:attribute name="charset" type="xsd:string" />
|
||||
<xsd:attribute name="persistent" type="xsd:string" />
|
||||
<xsd:attribute name="protocol" type="xsd:string" />
|
||||
<xsd:attribute name="server" type="xsd:string" />
|
||||
<xsd:attribute name="service" type="xsd:string" />
|
||||
<xsd:attribute name="servicename" type="xsd:string" />
|
||||
<xsd:attribute name="session-mode" type="xsd:string" />
|
||||
<xsd:attribute name="default_dbname" type="xsd:string" />
|
||||
<xsd:attribute name="sslmode" type="xsd:string" />
|
||||
<xsd:attribute name="sslrootcert" type="xsd:string" />
|
||||
<xsd:attribute name="sslcert" type="xsd:string" />
|
||||
<xsd:attribute name="sslkey" type="xsd:string" />
|
||||
<xsd:attribute name="sslcrl" type="xsd:string" />
|
||||
<xsd:attribute name="pooled" type="xsd:string" />
|
||||
<xsd:attribute name="multiple-active-result-sets" type="xsd:string" />
|
||||
<xsd:attribute name="connectstring" type="xsd:string" />
|
||||
<xsd:attribute name="instancename" type="xsd:string" />
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:group name="connection-child-config">
|
||||
<xsd:choice>
|
||||
<xsd:element name="option" type="option" />
|
||||
<xsd:element name="mapping-type" type="named_scalar" />
|
||||
<xsd:element name="slave" type="replica" />
|
||||
<xsd:element name="replica" type="replica" />
|
||||
<xsd:element name="default-table-option" type="named_scalar" />
|
||||
</xsd:choice>
|
||||
</xsd:group>
|
||||
|
||||
<xsd:complexType name="dbal">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="connection" type="connection" />
|
||||
<xsd:element name="type" type="named_scalar" />
|
||||
<xsd:element name="driver-scheme" type="driver_scheme" />
|
||||
<xsd:group ref="connection-child-config" />
|
||||
</xsd:choice>
|
||||
|
||||
<xsd:attribute name="default-connection" type="xsd:string" />
|
||||
<xsd:attributeGroup ref="connection-config" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="driver_scheme">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="scheme" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="option">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="xsd:anyType">
|
||||
<xsd:attribute name="key" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="connection">
|
||||
<xsd:group ref="connection-child-config" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attributeGroup ref="connection-config" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="replica">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attributeGroup ref="driver-config" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- ORM configuration -->
|
||||
|
||||
<xsd:complexType name="mapping">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="dir" type="xsd:string" />
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="prefix" type="xsd:string" />
|
||||
<xsd:attribute name="is-bundle" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="orm">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="entity-manager" type="entity_manager" />
|
||||
<xsd:element name="resolve-target-entity" type="resolve_target_entity" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:group ref="entity-manager-child-config" />
|
||||
</xsd:choice>
|
||||
|
||||
<xsd:attribute name="default-entity-manager" type="xsd:string" />
|
||||
<xsd:attribute name="proxy-dir" type="xsd:string" />
|
||||
<xsd:attribute name="proxy-namespace" type="xsd:string" />
|
||||
<xsd:attribute name="auto-generate-proxy-classes" type="xsd:string" default="false" />
|
||||
<xsd:attribute name="enable-lazy-ghost-objects" type="xsd:boolean" />
|
||||
<xsd:attributeGroup ref="entity-manager-config" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="resolve_target_entity">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="interface" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="cache_driver_type">
|
||||
<xsd:restriction base="xsd:token">
|
||||
<xsd:enumeration value="pool"/>
|
||||
<xsd:enumeration value="service"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:complexType name="cache_driver">
|
||||
<xsd:attribute name="type" type="cache_driver_type" default="pool" />
|
||||
<xsd:attribute name="id" type="xsd:string" />
|
||||
<xsd:attribute name="pool" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="entity_listeners">
|
||||
<xsd:choice minOccurs="1">
|
||||
<xsd:element name="entity" type="entity_listeners_entity" minOccurs="1" maxOccurs="unbounded" />
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="entity_listeners_entity">
|
||||
<xsd:choice minOccurs="1">
|
||||
<xsd:element name="listener" type="entity_listeners_listener" minOccurs="1" maxOccurs="unbounded" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="class" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="entity_listeners_listener">
|
||||
<xsd:choice minOccurs="1">
|
||||
<xsd:element name="event" type="entity_listeners_event" minOccurs="1" maxOccurs="unbounded" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="class" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="entity_listeners_event">
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="method" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="entity_manager">
|
||||
<xsd:group ref="entity-manager-child-config" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attributeGroup ref="entity-manager-config" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:group name="entity-manager-child-config">
|
||||
<xsd:choice>
|
||||
<xsd:element name="mapping" type="mapping" />
|
||||
<xsd:element name="metadata-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="result-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="query-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="dql" type="dql" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="hydrator" type="named_scalar" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="filter" type="filter" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="entity-listeners" type="entity_listeners" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="second-level-cache" type="second-level-cache" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="schema-ignore-class" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="identity-generation-preference" type="identity_generation_preference" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:choice>
|
||||
</xsd:group>
|
||||
|
||||
<xsd:attributeGroup name="entity-manager-config">
|
||||
<xsd:attribute name="auto-mapping" type="xsd:string" />
|
||||
<xsd:attribute name="connection" type="xsd:string" />
|
||||
<xsd:attribute name="default-repository-class" type="xsd:string" />
|
||||
<xsd:attribute name="class-metadata-factory-name" type="xsd:string" />
|
||||
<xsd:attribute name="naming-strategy" type="xsd:string" />
|
||||
<xsd:attribute name="quote-strategy" type="xsd:string" />
|
||||
<xsd:attribute name="typed-field-mapper" type="xsd:string" />
|
||||
<xsd:attribute name="entity-listener-resolver" type="xsd:string" />
|
||||
<xsd:attribute name="repository-factory" type="xsd:string" />
|
||||
<xsd:attribute name="report-fields-where-declared" type="xsd:boolean" />
|
||||
<xsd:attribute name="validate-xml-mapping" type="xsd:boolean" />
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:complexType name="filter" mixed="true">
|
||||
<xsd:choice minOccurs="0">
|
||||
<xsd:element name="parameter" type="named_scalar" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="class" type="xsd:string" />
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="second-level-cache-region" mixed="true">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="service" type="xsd:string" />
|
||||
<xsd:attribute name="lifetime" type="xsd:integer" />
|
||||
<xsd:attribute name="lock-lifetime" type="xsd:integer" />
|
||||
<xsd:attribute name="cache-driver" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="second-level-cache-logger" mixed="true">
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="service" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="second-level-cache" mixed="true">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="logger" type="second-level-cache-logger" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="region" type="second-level-cache-region" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="region-cache-driver" type="cache_driver" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" default="true"/>
|
||||
<xsd:attribute name="log-enabled" type="xsd:boolean" default="true"/>
|
||||
<xsd:attribute name="factory" type="xsd:string" />
|
||||
<xsd:attribute name="query-validator" type="xsd:string" />
|
||||
<xsd:attribute name="region-lifetime" type="xsd:integer" />
|
||||
<xsd:attribute name="region-lock-lifetime" type="xsd:integer" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="dql">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="string-function" type="named_scalar" />
|
||||
<xsd:element name="numeric-function" type="named_scalar" />
|
||||
<xsd:element name="datetime-function" type="named_scalar" />
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="identity_generation_preference">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="platform" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Attribute;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* Service tag to autoconfigure event listeners.
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
||||
class AsDoctrineListener
|
||||
{
|
||||
public function __construct(
|
||||
public string $event,
|
||||
public ?int $priority = null,
|
||||
public ?string $connection = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Attribute;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* Service tag to autoconfigure entity listeners.
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
||||
class AsEntityListener
|
||||
{
|
||||
public function __construct(
|
||||
public ?string $event = null,
|
||||
public ?string $method = null,
|
||||
public ?bool $lazy = null,
|
||||
public ?string $entityManager = null,
|
||||
public ?string $entity = null,
|
||||
public ?int $priority = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Attribute;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
class AsMiddleware
|
||||
{
|
||||
/** @param string[] $connections */
|
||||
public function __construct(
|
||||
public array $connections = [],
|
||||
public ?int $priority = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\CacheWarmer;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use LogicException;
|
||||
use Symfony\Bundle\FrameworkBundle\CacheWarmer\AbstractPhpFileCacheWarmer;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
|
||||
use function is_file;
|
||||
|
||||
/** @final since 2.11 */
|
||||
class DoctrineMetadataCacheWarmer extends AbstractPhpFileCacheWarmer
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
private string $phpArrayFile;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, string $phpArrayFile)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->phpArrayFile = $phpArrayFile;
|
||||
|
||||
parent::__construct($phpArrayFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* It must not be optional because it should be called before ProxyCacheWarmer which is not optional.
|
||||
*/
|
||||
public function isOptional(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, ?string $buildDir = null): bool
|
||||
{
|
||||
// cache already warmed up, no needs to do it again
|
||||
if (is_file($this->phpArrayFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$metadataFactory = $this->entityManager->getMetadataFactory();
|
||||
if ($metadataFactory instanceof AbstractClassMetadataFactory && $metadataFactory->getLoadedMetadata()) {
|
||||
throw new LogicException('DoctrineMetadataCacheWarmer must load metadata first, check priority of your warmers.');
|
||||
}
|
||||
|
||||
$metadataFactory->setCache($arrayAdapter);
|
||||
$metadataFactory->getAllMetadata();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command;
|
||||
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Throwable;
|
||||
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Database tool allows you to easily create your configured databases.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class CreateDatabaseDoctrineCommand extends DoctrineCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('doctrine:database:create')
|
||||
->setDescription('Creates the configured database')
|
||||
->addOption('connection', 'c', InputOption::VALUE_OPTIONAL, 'The connection to use for this command')
|
||||
->addOption('if-not-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database already exists')
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command creates the default connections database:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
You can also optionally specify the name of a connection to create the database for:
|
||||
|
||||
<info>php %command.full_name% --connection=default</info>
|
||||
EOT);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$connectionName = $input->getOption('connection');
|
||||
if (empty($connectionName)) {
|
||||
$connectionName = $this->getDoctrine()->getDefaultConnectionName();
|
||||
}
|
||||
|
||||
$connection = $this->getDoctrineConnection($connectionName);
|
||||
|
||||
$ifNotExists = $input->getOption('if-not-exists');
|
||||
|
||||
$params = $connection->getParams();
|
||||
|
||||
if (isset($params['primary'])) {
|
||||
$params = $params['primary'];
|
||||
}
|
||||
|
||||
$hasPath = isset($params['path']);
|
||||
$name = $hasPath ? $params['path'] : ($params['dbname'] ?? false);
|
||||
if (! $name) {
|
||||
throw new InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be created.");
|
||||
}
|
||||
|
||||
// Need to get rid of _every_ occurrence of dbname from connection configuration as we have already extracted all relevant info from url
|
||||
/** @psalm-suppress InvalidArrayOffset Need to be compatible with DBAL < 4, which still has `$params['url']` */
|
||||
unset($params['dbname'], $params['path'], $params['url']);
|
||||
|
||||
if ($connection->getDatabasePlatform() instanceof PostgreSQLPlatform) {
|
||||
/** @psalm-suppress InvalidArrayOffset It's still available in DBAL 3.x that we need to support */
|
||||
$params['dbname'] = $params['default_dbname'] ?? 'postgres';
|
||||
}
|
||||
|
||||
$tmpConnection = DriverManager::getConnection($params, $connection->getConfiguration());
|
||||
$schemaManager = $tmpConnection->createSchemaManager();
|
||||
$shouldNotCreateDatabase = $ifNotExists && in_array($name, $schemaManager->listDatabases());
|
||||
|
||||
// Only quote if we don't have a path
|
||||
if (! $hasPath) {
|
||||
$name = $tmpConnection->getDatabasePlatform()->quoteSingleIdentifier($name);
|
||||
}
|
||||
|
||||
$error = false;
|
||||
try {
|
||||
if ($shouldNotCreateDatabase) {
|
||||
$output->writeln(sprintf('<info>Database <comment>%s</comment> for connection named <comment>%s</comment> already exists. Skipped.</info>', $name, $connectionName));
|
||||
} else {
|
||||
$schemaManager->createDatabase($name);
|
||||
$output->writeln(sprintf('<info>Created database <comment>%s</comment> for connection named <comment>%s</comment></info>', $name, $connectionName));
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$output->writeln(sprintf('<error>Could not create database <comment>%s</comment> for connection named <comment>%s</comment></error>', $name, $connectionName));
|
||||
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
|
||||
$error = true;
|
||||
}
|
||||
|
||||
$tmpConnection->close();
|
||||
|
||||
return $error ? 1 : 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Tools\EntityGenerator;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
/**
|
||||
* Base class for Doctrine console commands to extend from.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class DoctrineCommand extends Command
|
||||
{
|
||||
private ManagerRegistry $doctrine;
|
||||
|
||||
public function __construct(ManagerRegistry $doctrine)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->doctrine = $doctrine;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a doctrine entity generator
|
||||
*
|
||||
* @return EntityGenerator
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass ORM < 3 specific
|
||||
*/
|
||||
protected function getEntityGenerator()
|
||||
{
|
||||
$entityGenerator = new EntityGenerator();
|
||||
$entityGenerator->setGenerateAnnotations(false);
|
||||
$entityGenerator->setGenerateStubMethods(true);
|
||||
$entityGenerator->setRegenerateEntityIfExists(false);
|
||||
$entityGenerator->setUpdateEntityIfExists(true);
|
||||
$entityGenerator->setNumSpaces(4);
|
||||
$entityGenerator->setAnnotationPrefix('ORM\\');
|
||||
|
||||
return $entityGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a doctrine entity manager by symfony name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int|null $shardId
|
||||
*
|
||||
* @return EntityManager
|
||||
*/
|
||||
protected function getEntityManager($name, $shardId = null)
|
||||
{
|
||||
$manager = $this->getDoctrine()->getManager($name);
|
||||
|
||||
if ($shardId !== null) {
|
||||
throw new InvalidArgumentException('Shards are not supported anymore using doctrine/dbal >= 3');
|
||||
}
|
||||
|
||||
return $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a doctrine dbal connection by symfony name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Connection
|
||||
*/
|
||||
protected function getDoctrineConnection($name)
|
||||
{
|
||||
return $this->getDoctrine()->getConnection($name);
|
||||
}
|
||||
|
||||
/** @return ManagerRegistry */
|
||||
protected function getDoctrine()
|
||||
{
|
||||
return $this->doctrine;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command;
|
||||
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\DBAL\Schema\SQLiteSchemaManager;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Throwable;
|
||||
|
||||
use function file_exists;
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
use function unlink;
|
||||
|
||||
/**
|
||||
* Database tool allows you to easily drop your configured databases.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class DropDatabaseDoctrineCommand extends DoctrineCommand
|
||||
{
|
||||
public const RETURN_CODE_NOT_DROP = 1;
|
||||
|
||||
public const RETURN_CODE_NO_FORCE = 2;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('doctrine:database:drop')
|
||||
->setDescription('Drops the configured database')
|
||||
->addOption('connection', 'c', InputOption::VALUE_OPTIONAL, 'The connection to use for this command')
|
||||
->addOption('if-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database doesn\'t exist')
|
||||
->addOption('force', 'f', InputOption::VALUE_NONE, 'Set this parameter to execute this action')
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command drops the default connections database:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
The <info>--force</info> parameter has to be used to actually drop the database.
|
||||
|
||||
You can also optionally specify the name of a connection to drop the database for:
|
||||
|
||||
<info>php %command.full_name% --connection=default</info>
|
||||
|
||||
<error>Be careful: All data in a given database will be lost when executing this command.</error>
|
||||
EOT);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$connectionName = $input->getOption('connection');
|
||||
if (empty($connectionName)) {
|
||||
$connectionName = $this->getDoctrine()->getDefaultConnectionName();
|
||||
}
|
||||
|
||||
$connection = $this->getDoctrineConnection($connectionName);
|
||||
|
||||
$ifExists = $input->getOption('if-exists');
|
||||
|
||||
$params = $connection->getParams();
|
||||
|
||||
if (isset($params['primary'])) {
|
||||
$params = $params['primary'];
|
||||
}
|
||||
|
||||
$name = $params['path'] ?? ($params['dbname'] ?? false);
|
||||
if (! $name) {
|
||||
throw new InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
|
||||
}
|
||||
|
||||
/** @psalm-suppress InvalidArrayOffset Need to be compatible with DBAL < 4, which still has `$params['url']` */
|
||||
unset($params['dbname'], $params['url']);
|
||||
|
||||
if ($connection->getDatabasePlatform() instanceof PostgreSQLPlatform) {
|
||||
/** @psalm-suppress InvalidArrayOffset It's still available in DBAL 3.x that we need to support */
|
||||
$params['dbname'] = $params['default_dbname'] ?? 'postgres';
|
||||
}
|
||||
|
||||
if (! $input->getOption('force')) {
|
||||
$output->writeln('<error>ATTENTION:</error> This operation should not be executed in a production environment.');
|
||||
$output->writeln('');
|
||||
$output->writeln(sprintf('<info>Would drop the database <comment>%s</comment> for connection named <comment>%s</comment>.</info>', $name, $connectionName));
|
||||
$output->writeln('Please run the operation with --force to execute');
|
||||
$output->writeln('<error>All data will be lost!</error>');
|
||||
|
||||
return self::RETURN_CODE_NO_FORCE;
|
||||
}
|
||||
|
||||
// Reopen connection without database name set
|
||||
// as some vendors do not allow dropping the database connected to.
|
||||
$connection->close();
|
||||
$connection = DriverManager::getConnection($params, $connection->getConfiguration());
|
||||
$schemaManager = $connection->createSchemaManager();
|
||||
$shouldDropDatabase = ! $ifExists || in_array($name, $schemaManager->listDatabases());
|
||||
|
||||
// Only quote if we don't have a path
|
||||
if (! isset($params['path'])) {
|
||||
$name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name);
|
||||
}
|
||||
|
||||
try {
|
||||
if ($shouldDropDatabase) {
|
||||
if ($schemaManager instanceof SQLiteSchemaManager) {
|
||||
// dropDatabase() is deprecated for Sqlite
|
||||
$connection->close();
|
||||
if (file_exists($name)) {
|
||||
unlink($name);
|
||||
}
|
||||
} else {
|
||||
$schemaManager->dropDatabase($name);
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<info>Dropped database <comment>%s</comment> for connection named <comment>%s</comment></info>', $name, $connectionName));
|
||||
} else {
|
||||
$output->writeln(sprintf('<info>Database <comment>%s</comment> for connection named <comment>%s</comment> doesn\'t exist. Skipped.</info>', $name, $connectionName));
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (Throwable $e) {
|
||||
$output->writeln(sprintf('<error>Could not drop database <comment>%s</comment> for connection named <comment>%s</comment></error>', $name, $connectionName));
|
||||
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
|
||||
|
||||
return self::RETURN_CODE_NOT_DROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command;
|
||||
|
||||
use Doctrine\ORM\Mapping\Driver\DatabaseDriver;
|
||||
use Doctrine\ORM\Tools\Console\MetadataFilter;
|
||||
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
|
||||
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function chmod;
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function is_dir;
|
||||
use function mkdir;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* Import Doctrine ORM metadata mapping information from an existing database.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class ImportMappingDoctrineCommand extends DoctrineCommand
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $bundles;
|
||||
|
||||
/** @param string[] $bundles */
|
||||
public function __construct(ManagerRegistry $doctrine, array $bundles)
|
||||
{
|
||||
parent::__construct($doctrine);
|
||||
|
||||
$this->bundles = $bundles;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('doctrine:mapping:import')
|
||||
->addArgument('name', InputArgument::REQUIRED, 'The bundle or namespace to import the mapping information to')
|
||||
->addArgument('mapping-type', InputArgument::OPTIONAL, 'The mapping type to export the imported mapping information to')
|
||||
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command')
|
||||
->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be mapped.')
|
||||
->addOption('force', null, InputOption::VALUE_NONE, 'Force to overwrite existing mapping files.')
|
||||
->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path where the files would be generated (not used when a bundle is passed).')
|
||||
->setDescription('Imports mapping information from an existing database')
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command imports mapping information
|
||||
from an existing database:
|
||||
|
||||
Generate annotation mappings into the src/ directory using App as the namespace:
|
||||
<info>php %command.full_name% App\\Entity annotation --path=src/Entity</info>
|
||||
|
||||
Generate xml mappings into the config/doctrine/ directory using App as the namespace:
|
||||
<info>php %command.full_name% App\\Entity xml --path=config/doctrine</info>
|
||||
|
||||
Generate XML mappings into a bundle:
|
||||
<info>php %command.full_name% "MyCustomBundle" xml</info>
|
||||
|
||||
You can also optionally specify which entity manager to import from with the
|
||||
<info>--em</info> option:
|
||||
|
||||
<info>php %command.full_name% "MyCustomBundle" xml --em=default</info>
|
||||
|
||||
If you don't want to map every entity that can be found in the database, use the
|
||||
<info>--filter</info> option. It will try to match the targeted mapped entity with the
|
||||
provided pattern string.
|
||||
|
||||
<info>php %command.full_name% "MyCustomBundle" xml --filter=MyMatchedEntity</info>
|
||||
|
||||
Use the <info>--force</info> option, if you want to override existing mapping files:
|
||||
|
||||
<info>php %command.full_name% "MyCustomBundle" xml --force</info>
|
||||
EOT);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$type = $input->getArgument('mapping-type') ?: 'xml';
|
||||
if ($type === 'yaml') {
|
||||
$type = 'yml';
|
||||
}
|
||||
|
||||
$namespaceOrBundle = $input->getArgument('name');
|
||||
if (isset($this->bundles[$namespaceOrBundle])) {
|
||||
$bundle = $this->getApplication()->getKernel()->getBundle($namespaceOrBundle);
|
||||
$namespace = $bundle->getNamespace() . '\Entity';
|
||||
|
||||
$destPath = $bundle->getPath();
|
||||
if ($type === 'annotation') {
|
||||
$destPath .= '/Entity';
|
||||
} else {
|
||||
$destPath .= '/Resources/config/doctrine';
|
||||
}
|
||||
} else {
|
||||
// assume a namespace has been passed
|
||||
$namespace = $namespaceOrBundle;
|
||||
$destPath = $input->getOption('path');
|
||||
if ($destPath === null) {
|
||||
throw new InvalidArgumentException('The --path option is required when passing a namespace (e.g. --path=src). If you intended to pass a bundle name, check your spelling.');
|
||||
}
|
||||
}
|
||||
|
||||
$cme = new ClassMetadataExporter();
|
||||
$exporter = $cme->getExporter($type);
|
||||
$exporter->setOverwriteExistingFiles($input->getOption('force'));
|
||||
|
||||
if ($type === 'annotation') {
|
||||
$entityGenerator = $this->getEntityGenerator();
|
||||
$exporter->setEntityGenerator($entityGenerator);
|
||||
}
|
||||
|
||||
$em = $this->getEntityManager($input->getOption('em'));
|
||||
|
||||
$databaseDriver = new DatabaseDriver($em->getConnection()->getSchemaManager());
|
||||
$em->getConfiguration()->setMetadataDriverImpl($databaseDriver);
|
||||
|
||||
$emName = $input->getOption('em');
|
||||
$emName = $emName ? $emName : 'default';
|
||||
|
||||
$cmf = new DisconnectedClassMetadataFactory();
|
||||
$cmf->setEntityManager($em);
|
||||
$metadata = $cmf->getAllMetadata();
|
||||
$metadata = MetadataFilter::filter($metadata, $input->getOption('filter'));
|
||||
if ($metadata) {
|
||||
$output->writeln(sprintf('Importing mapping information from "<info>%s</info>" entity manager', $emName));
|
||||
foreach ($metadata as $class) {
|
||||
$className = $class->name;
|
||||
$class->name = $namespace . '\\' . $className;
|
||||
if ($type === 'annotation') {
|
||||
$path = $destPath . '/' . str_replace('\\', '.', $className) . '.php';
|
||||
} else {
|
||||
$path = $destPath . '/' . str_replace('\\', '.', $className) . '.orm.' . $type;
|
||||
}
|
||||
|
||||
$output->writeln(sprintf(' > writing <comment>%s</comment>', $path));
|
||||
$code = $exporter->exportClassMetadata($class);
|
||||
$dir = dirname($path);
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0775, true);
|
||||
}
|
||||
|
||||
file_put_contents($path, $code);
|
||||
chmod($path, 0664);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$output->writeln('Database does not have any mapping information.');
|
||||
$output->writeln('');
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to clear the metadata cache of the various cache drivers.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand instead
|
||||
*/
|
||||
class ClearMetadataCacheDoctrineCommand extends MetadataCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:cache:clear-metadata')
|
||||
->setDescription('Clears all metadata cache for an entity manager');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to clear the query cache of the various cache drivers.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand instead
|
||||
*/
|
||||
class ClearQueryCacheDoctrineCommand extends QueryCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:cache:clear-query')
|
||||
->setDescription('Clears all query cache for an entity manager');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to clear the result cache of the various cache drivers.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand instead
|
||||
*/
|
||||
class ClearResultCacheDoctrineCommand extends ResultCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:cache:clear-result')
|
||||
->setDescription('Clears result cache for an entity manager');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to clear a collection cache region.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand instead
|
||||
*/
|
||||
class CollectionRegionDoctrineCommand extends CollectionRegionCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:cache:clear-collection-region');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand;
|
||||
use Doctrine\ORM\Tools\Export\Driver\AbstractExporter;
|
||||
use Doctrine\ORM\Tools\Export\Driver\XmlExporter;
|
||||
use Doctrine\ORM\Tools\Export\Driver\YamlExporter;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
use function assert;
|
||||
|
||||
/**
|
||||
* Convert Doctrine ORM metadata mapping information between the various supported
|
||||
* formats.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand instead
|
||||
*
|
||||
* @psalm-suppress UndefinedClass ORM < 3
|
||||
*/
|
||||
class ConvertMappingDoctrineCommand extends ConvertMappingCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:mapping:convert');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $toType
|
||||
* @param string $destPath
|
||||
*
|
||||
* @return AbstractExporter
|
||||
*/
|
||||
protected function getExporter($toType, $destPath)
|
||||
{
|
||||
$exporter = parent::getExporter($toType, $destPath);
|
||||
assert($exporter instanceof AbstractExporter);
|
||||
if ($exporter instanceof XmlExporter) {
|
||||
$exporter->setExtension('.orm.xml');
|
||||
} elseif ($exporter instanceof YamlExporter) {
|
||||
$exporter->setExtension('.orm.yml');
|
||||
}
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to execute the SQL needed to generate the database schema for
|
||||
* a given entity manager.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand instead
|
||||
*/
|
||||
class CreateSchemaDoctrineCommand extends CreateCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:schema:create')
|
||||
->setDescription('Executes (or dumps) the SQL needed to generate the database schema');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Tools\Console\EntityManagerProvider;
|
||||
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
|
||||
use function assert;
|
||||
use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* Provides some helper and convenience methods to configure doctrine commands in the context of bundles
|
||||
* and multiple connections/entity managers.
|
||||
*
|
||||
* @deprecated since DoctrineBundle 2.7 and will be removed in 3.0
|
||||
*/
|
||||
abstract class DoctrineCommandHelper
|
||||
{
|
||||
/**
|
||||
* Convenience method to push the helper sets of a given entity manager into the application.
|
||||
*
|
||||
* @param string $emName
|
||||
*/
|
||||
public static function setApplicationEntityManager(Application $application, $emName)
|
||||
{
|
||||
$em = $application->getKernel()->getContainer()->get('doctrine')->getManager($emName);
|
||||
assert($em instanceof EntityManagerInterface);
|
||||
$helperSet = $application->getHelperSet();
|
||||
/** @psalm-suppress InvalidArgument ORM < 3 specific */
|
||||
$helperSet->set(new EntityManagerHelper($em), 'em');
|
||||
|
||||
trigger_deprecation(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.7',
|
||||
'Providing an EntityManager using "%s" is deprecated. Use an instance of "%s" instead.',
|
||||
EntityManagerHelper::class,
|
||||
EntityManagerProvider::class,
|
||||
);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to drop the database schema for a set of classes based on their mappings.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand instead
|
||||
*/
|
||||
class DropSchemaDoctrineCommand extends DropCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:schema:drop')
|
||||
->setDescription('Executes (or dumps) the SQL needed to drop the current database schema');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Ensure the Doctrine ORM is configured properly for a production environment.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand instead
|
||||
*
|
||||
* @psalm-suppress UndefinedClass ORM < 3 specific
|
||||
*/
|
||||
class EnsureProductionSettingsDoctrineCommand extends EnsureProductionSettingsCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:ensure-production-settings');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to clear a entity cache region.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand instead
|
||||
*/
|
||||
class EntityRegionCacheDoctrineCommand extends EntityRegionCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:cache:clear-entity-region');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\InfoCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Show information about mapped entities
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\InfoCommand instead
|
||||
*/
|
||||
class InfoDoctrineCommand extends InfoCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('doctrine:mapping:info');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\EntityManagerProvider;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
trait OrmProxyCommand
|
||||
{
|
||||
private ?EntityManagerProvider $entityManagerProvider;
|
||||
|
||||
public function __construct(?EntityManagerProvider $entityManagerProvider = null)
|
||||
{
|
||||
parent::__construct($entityManagerProvider);
|
||||
|
||||
$this->entityManagerProvider = $entityManagerProvider;
|
||||
|
||||
trigger_deprecation(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.8',
|
||||
'Class "%s" is deprecated. Use "%s" instead.',
|
||||
self::class,
|
||||
parent::class,
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (! $this->entityManagerProvider) {
|
||||
DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
|
||||
}
|
||||
|
||||
return parent::execute($input, $output);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to clear a query cache region.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand instead
|
||||
*/
|
||||
class QueryRegionCacheDoctrineCommand extends QueryRegionCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:cache:clear-query-region');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\RunDqlCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Execute a Doctrine DQL query and output the results.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\RunDqlCommand instead
|
||||
*/
|
||||
class RunDqlDoctrineCommand extends RunDqlCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:query:dql')
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command executes the given DQL query and
|
||||
outputs the results:
|
||||
|
||||
<info>php %command.full_name% "SELECT u FROM UserBundle:User u"</info>
|
||||
|
||||
You can also optional specify some additional options like what type of
|
||||
hydration to use when executing the query:
|
||||
|
||||
<info>php %command.full_name% "SELECT u FROM UserBundle:User u" --hydrate=array</info>
|
||||
|
||||
Additionally you can specify the first result and maximum amount of results to
|
||||
show:
|
||||
|
||||
<info>php %command.full_name% "SELECT u FROM UserBundle:User u" --first-result=0 --max-result=30</info>
|
||||
EOT);
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* Execute a SQL query and output the results.
|
||||
*
|
||||
* @deprecated use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand instead
|
||||
*/
|
||||
class RunSqlDoctrineCommand extends RunSqlCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:query:sql')
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command executes the given SQL query and
|
||||
outputs the results:
|
||||
|
||||
<info>php %command.full_name% "SELECT * FROM users"</info>
|
||||
EOT);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
trigger_deprecation(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.2',
|
||||
'The "%s" (doctrine:query:sql) is deprecated, use dbal:run-sql command instead.',
|
||||
self::class,
|
||||
);
|
||||
|
||||
return parent::execute($input, $output);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to generate the SQL needed to update the database schema to match
|
||||
* the current mapping information.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand instead
|
||||
*/
|
||||
class UpdateSchemaDoctrineCommand extends UpdateCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:schema:update');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand as DoctrineValidateSchemaCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Command to run Doctrine ValidateSchema() on the current mappings.
|
||||
*
|
||||
* @deprecated use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand instead
|
||||
*/
|
||||
class ValidateSchemaCommand extends DoctrineValidateSchemaCommand
|
||||
{
|
||||
use OrmProxyCommand;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('doctrine:schema:validate');
|
||||
|
||||
if ($this->getDefinition()->hasOption('em')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle;
|
||||
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\DBAL\Configuration;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Connection\StaticServerVersionProvider;
|
||||
use Doctrine\DBAL\ConnectionException;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Exception as DBALException;
|
||||
use Doctrine\DBAL\Exception\DriverException;
|
||||
use Doctrine\DBAL\Exception\DriverRequired;
|
||||
use Doctrine\DBAL\Exception\InvalidWrapperClass;
|
||||
use Doctrine\DBAL\Exception\MalformedDsnException;
|
||||
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Tools\DsnParser;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function array_merge;
|
||||
use function class_exists;
|
||||
use function is_subclass_of;
|
||||
use function method_exists;
|
||||
use function trigger_deprecation;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
/** @psalm-import-type Params from DriverManager */
|
||||
class ConnectionFactory
|
||||
{
|
||||
/** @internal */
|
||||
public const DEFAULT_SCHEME_MAP = [
|
||||
'db2' => 'ibm_db2',
|
||||
'mssql' => 'pdo_sqlsrv',
|
||||
'mysql' => 'pdo_mysql',
|
||||
'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason
|
||||
'postgres' => 'pdo_pgsql',
|
||||
'postgresql' => 'pdo_pgsql',
|
||||
'pgsql' => 'pdo_pgsql',
|
||||
'sqlite' => 'pdo_sqlite',
|
||||
'sqlite3' => 'pdo_sqlite',
|
||||
];
|
||||
|
||||
/** @var mixed[][] */
|
||||
private array $typesConfig = [];
|
||||
|
||||
private DsnParser $dsnParser;
|
||||
|
||||
private bool $initialized = false;
|
||||
|
||||
/** @param mixed[][] $typesConfig */
|
||||
public function __construct(array $typesConfig, ?DsnParser $dsnParser = null)
|
||||
{
|
||||
$this->typesConfig = $typesConfig;
|
||||
$this->dsnParser = $dsnParser ?? new DsnParser(self::DEFAULT_SCHEME_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection by name.
|
||||
*
|
||||
* @param mixed[] $params
|
||||
* @param array<string, string> $mappingTypes
|
||||
* @psalm-param Params $params
|
||||
*
|
||||
* @return Connection
|
||||
*/
|
||||
public function createConnection(array $params, ?Configuration $config = null, ?EventManager $eventManager = null, array $mappingTypes = [])
|
||||
{
|
||||
if (! method_exists(Connection::class, 'getEventManager') && $eventManager !== null) {
|
||||
throw new InvalidArgumentException('Passing an EventManager instance is not supported with DBAL > 3');
|
||||
}
|
||||
|
||||
if (! $this->initialized) {
|
||||
$this->initializeTypes();
|
||||
}
|
||||
|
||||
$overriddenOptions = [];
|
||||
/** @psalm-suppress InvalidArrayOffset We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */
|
||||
if (isset($params['connection_override_options'])) {
|
||||
trigger_deprecation('doctrine/doctrine-bundle', '2.4', 'The "connection_override_options" connection parameter is deprecated');
|
||||
$overriddenOptions = $params['connection_override_options'];
|
||||
unset($params['connection_override_options']);
|
||||
}
|
||||
|
||||
$params = $this->parseDatabaseUrl($params);
|
||||
|
||||
// URL support for PrimaryReplicaConnection
|
||||
if (isset($params['primary'])) {
|
||||
$params['primary'] = $this->parseDatabaseUrl($params['primary']);
|
||||
}
|
||||
|
||||
if (isset($params['replica'])) {
|
||||
foreach ($params['replica'] as $key => $replicaParams) {
|
||||
$params['replica'][$key] = $this->parseDatabaseUrl($replicaParams);
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress InvalidArrayOffset We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */
|
||||
if (! isset($params['pdo']) && (! isset($params['charset']) || $overriddenOptions || isset($params['dbname_suffix']))) {
|
||||
$wrapperClass = null;
|
||||
|
||||
if (isset($params['wrapperClass'])) {
|
||||
if (! is_subclass_of($params['wrapperClass'], Connection::class)) {
|
||||
if (class_exists(InvalidWrapperClass::class)) {
|
||||
throw InvalidWrapperClass::new($params['wrapperClass']);
|
||||
}
|
||||
|
||||
throw DBALException::invalidWrapperClass($params['wrapperClass']);
|
||||
}
|
||||
|
||||
$wrapperClass = $params['wrapperClass'];
|
||||
$params['wrapperClass'] = null;
|
||||
}
|
||||
|
||||
$connection = DriverManager::getConnection(...array_merge([$params, $config], $eventManager ? [$eventManager] : []));
|
||||
$params = $this->addDatabaseSuffix(array_merge($connection->getParams(), $overriddenOptions));
|
||||
$driver = $connection->getDriver();
|
||||
/** @psalm-suppress InvalidScalarArgument Bogus error, StaticServerVersionProvider implements Doctrine\DBAL\ServerVersionProvider */
|
||||
$platform = $driver->getDatabasePlatform(
|
||||
...(class_exists(StaticServerVersionProvider::class)
|
||||
? [new StaticServerVersionProvider($params['serverVersion'] ?? $params['primary']['serverVersion'] ?? '')]
|
||||
: []
|
||||
),
|
||||
);
|
||||
|
||||
if (! isset($params['charset'])) {
|
||||
if ($platform instanceof AbstractMySQLPlatform) {
|
||||
$params['charset'] = 'utf8mb4';
|
||||
|
||||
if (isset($params['defaultTableOptions']['collate'])) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/doctrine-bundle',
|
||||
'https://github.com/doctrine/dbal/issues/5214',
|
||||
'The "collate" default table option is deprecated in favor of "collation" and will be removed in doctrine/doctrine-bundle 3.0. ',
|
||||
);
|
||||
$params['defaultTableOptions']['collation'] = $params['defaultTableOptions']['collate'];
|
||||
unset($params['defaultTableOptions']['collate']);
|
||||
}
|
||||
|
||||
if (! isset($params['defaultTableOptions']['collation'])) {
|
||||
$params['defaultTableOptions']['collation'] = 'utf8mb4_unicode_ci';
|
||||
}
|
||||
} else {
|
||||
$params['charset'] = 'utf8';
|
||||
}
|
||||
}
|
||||
|
||||
if ($wrapperClass !== null) {
|
||||
$params['wrapperClass'] = $wrapperClass;
|
||||
} else {
|
||||
$wrapperClass = Connection::class;
|
||||
}
|
||||
|
||||
$connection = new $wrapperClass($params, $driver, $config, $eventManager);
|
||||
} else {
|
||||
$connection = DriverManager::getConnection(...array_merge([$params, $config], $eventManager ? [$eventManager] : []));
|
||||
}
|
||||
|
||||
if (! empty($mappingTypes)) {
|
||||
$platform = $this->getDatabasePlatform($connection);
|
||||
foreach ($mappingTypes as $dbType => $doctrineType) {
|
||||
$platform->registerDoctrineTypeMapping($dbType, $doctrineType);
|
||||
}
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get the database platform.
|
||||
*
|
||||
* This could fail if types should be registered to an predefined/unused connection
|
||||
* and the platform version is unknown.
|
||||
*
|
||||
* @link https://github.com/doctrine/DoctrineBundle/issues/673
|
||||
*
|
||||
* @throws DBALException
|
||||
*/
|
||||
private function getDatabasePlatform(Connection $connection): AbstractPlatform
|
||||
{
|
||||
try {
|
||||
return $connection->getDatabasePlatform();
|
||||
} catch (DriverException $driverException) {
|
||||
$class = class_exists(DBALException::class) ? DBALException::class : ConnectionException::class;
|
||||
|
||||
throw new $class(
|
||||
'An exception occurred while establishing a connection to figure out your platform version.' . PHP_EOL .
|
||||
"You can circumvent this by setting a 'server_version' configuration value" . PHP_EOL . PHP_EOL .
|
||||
'For further information have a look at:' . PHP_EOL .
|
||||
'https://github.com/doctrine/DoctrineBundle/issues/673',
|
||||
0,
|
||||
$driverException,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the types
|
||||
*/
|
||||
private function initializeTypes(): void
|
||||
{
|
||||
foreach ($this->typesConfig as $typeName => $typeConfig) {
|
||||
if (Type::hasType($typeName)) {
|
||||
Type::overrideType($typeName, $typeConfig['class']);
|
||||
} else {
|
||||
Type::addType($typeName, $typeConfig['class']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function addDatabaseSuffix(array $params): array
|
||||
{
|
||||
if (isset($params['dbname']) && isset($params['dbname_suffix'])) {
|
||||
$params['dbname'] .= $params['dbname_suffix'];
|
||||
}
|
||||
|
||||
foreach ($params['replica'] ?? [] as $key => $replicaParams) {
|
||||
if (! isset($replicaParams['dbname'], $replicaParams['dbname_suffix'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$params['replica'][$key]['dbname'] .= $replicaParams['dbname_suffix'];
|
||||
}
|
||||
|
||||
if (isset($params['primary']['dbname'], $params['primary']['dbname_suffix'])) {
|
||||
$params['primary']['dbname'] .= $params['primary']['dbname_suffix'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts parts from a database URL, if present, and returns an
|
||||
* updated list of parameters.
|
||||
*
|
||||
* @param mixed[] $params The list of parameters.
|
||||
* @psalm-param Params $params
|
||||
*
|
||||
* @return mixed[] A modified list of parameters with info from a database
|
||||
* URL extracted into individual parameter parts.
|
||||
* @psalm-return Params
|
||||
*
|
||||
* @throws DBALException
|
||||
*/
|
||||
private function parseDatabaseUrl(array $params): array
|
||||
{
|
||||
/** @psalm-suppress InvalidArrayOffset Need to be compatible with DBAL < 4, which still has `$params['url']` */
|
||||
if (! isset($params['url'])) {
|
||||
return $params;
|
||||
}
|
||||
|
||||
try {
|
||||
$parsedParams = $this->dsnParser->parse($params['url']);
|
||||
} catch (MalformedDsnException $e) {
|
||||
throw new MalformedDsnException('Malformed parameter "url".', 0, $e);
|
||||
}
|
||||
|
||||
if (isset($parsedParams['driver'])) {
|
||||
// The requested driver from the URL scheme takes precedence
|
||||
// over the default custom driver from the connection parameters (if any).
|
||||
unset($params['driverClass']);
|
||||
}
|
||||
|
||||
$params = array_merge($params, $parsedParams);
|
||||
|
||||
// If a schemeless connection URL is given, we require a default driver or default custom driver
|
||||
// as connection parameter.
|
||||
if (! isset($params['driverClass']) && ! isset($params['driver'])) {
|
||||
if (class_exists(DriverRequired::class)) {
|
||||
throw DriverRequired::new($params['url']);
|
||||
}
|
||||
|
||||
throw DBALException::driverRequired($params['url']);
|
||||
}
|
||||
|
||||
unset($params['url']);
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Controller;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLitePlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLServerPlatform;
|
||||
use Doctrine\Persistence\ConnectionRegistry;
|
||||
use Exception;
|
||||
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Throwable;
|
||||
use Twig\Environment;
|
||||
|
||||
use function assert;
|
||||
|
||||
/** @internal */
|
||||
class ProfilerController
|
||||
{
|
||||
private Environment $twig;
|
||||
private ConnectionRegistry $registry;
|
||||
private Profiler $profiler;
|
||||
|
||||
public function __construct(Environment $twig, ConnectionRegistry $registry, Profiler $profiler)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
$this->registry = $registry;
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the profiler panel for the given token.
|
||||
*
|
||||
* @param string $token The profiler token
|
||||
* @param string $connectionName
|
||||
* @param int $query
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
public function explainAction($token, $connectionName, $query)
|
||||
{
|
||||
$this->profiler->disable();
|
||||
|
||||
$profile = $this->profiler->loadProfile($token);
|
||||
$collector = $profile->getCollector('db');
|
||||
|
||||
assert($collector instanceof DoctrineDataCollector);
|
||||
|
||||
$queries = $collector->getQueries();
|
||||
|
||||
if (! isset($queries[$connectionName][$query])) {
|
||||
return new Response('This query does not exist.');
|
||||
}
|
||||
|
||||
$query = $queries[$connectionName][$query];
|
||||
if (! $query['explainable']) {
|
||||
return new Response('This query cannot be explained.');
|
||||
}
|
||||
|
||||
$connection = $this->registry->getConnection($connectionName);
|
||||
assert($connection instanceof Connection);
|
||||
try {
|
||||
$platform = $connection->getDatabasePlatform();
|
||||
if ($platform instanceof SQLitePlatform) {
|
||||
$results = $this->explainSQLitePlatform($connection, $query);
|
||||
} elseif ($platform instanceof SQLServerPlatform) {
|
||||
throw new Exception('Explain for SQLServerPlatform is currently not supported. Contributions are welcome.');
|
||||
} elseif ($platform instanceof OraclePlatform) {
|
||||
$results = $this->explainOraclePlatform($connection, $query);
|
||||
} else {
|
||||
$results = $this->explainOtherPlatform($connection, $query);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
return new Response('This query cannot be explained.');
|
||||
}
|
||||
|
||||
return new Response($this->twig->render('@Doctrine/Collector/explain.html.twig', [
|
||||
'data' => $results,
|
||||
'query' => $query,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $query
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function explainSQLitePlatform(Connection $connection, array $query): array
|
||||
{
|
||||
$params = $query['params'];
|
||||
|
||||
if ($params instanceof Data) {
|
||||
$params = $params->getValue(true);
|
||||
}
|
||||
|
||||
return $connection->executeQuery('EXPLAIN QUERY PLAN ' . $query['sql'], $params, $query['types'])
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $query
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function explainOtherPlatform(Connection $connection, array $query): array
|
||||
{
|
||||
$params = $query['params'];
|
||||
|
||||
if ($params instanceof Data) {
|
||||
$params = $params->getValue(true);
|
||||
}
|
||||
|
||||
return $connection->executeQuery('EXPLAIN ' . $query['sql'], $params, $query['types'])
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $query
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function explainOraclePlatform(Connection $connection, array $query): array
|
||||
{
|
||||
$connection->executeQuery('EXPLAIN PLAN FOR ' . $query['sql']);
|
||||
|
||||
return $connection->executeQuery('SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY())')
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DataCollector;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\Cache\CacheConfiguration;
|
||||
use Doctrine\ORM\Cache\Logging\CacheLoggerChain;
|
||||
use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Tools\SchemaValidator;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector as BaseCollector;
|
||||
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
use function array_map;
|
||||
use function array_sum;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function usort;
|
||||
|
||||
/**
|
||||
* @psalm-type QueryType = array{
|
||||
* executionMS: float,
|
||||
* explainable: bool,
|
||||
* sql: string,
|
||||
* params: ?array<array-key, mixed>,
|
||||
* runnable: bool,
|
||||
* types: ?array<array-key, Type|int|string|null>,
|
||||
* }
|
||||
* @psalm-type DataType = array{
|
||||
* caches: array{
|
||||
* enabled: bool,
|
||||
* counts: array<"puts"|"hits"|"misses", int>,
|
||||
* log_enabled: bool,
|
||||
* regions: array<"puts"|"hits"|"misses", array<string, int>>,
|
||||
* },
|
||||
* connections: list<string>,
|
||||
* entities: array<string, array<class-string, array{class: class-string, file: false|string, line: false|int}>>,
|
||||
* errors: array<string, array<class-string, list<string>>>,
|
||||
* managers: list<string>,
|
||||
* queries: array<string, list<QueryType>>,
|
||||
* }
|
||||
* @psalm-property DataType $data
|
||||
*/
|
||||
class DoctrineDataCollector extends BaseCollector
|
||||
{
|
||||
private ManagerRegistry $registry;
|
||||
private ?int $invalidEntityCount = null;
|
||||
|
||||
/**
|
||||
* @var mixed[][]|null
|
||||
* @psalm-var ?array<string, list<QueryType&array{count: int, index: int, executionPercent?: float}>>
|
||||
*/
|
||||
private ?array $groupedQueries = null;
|
||||
|
||||
private bool $shouldValidateSchema;
|
||||
|
||||
public function __construct(ManagerRegistry $registry, bool $shouldValidateSchema = true, ?DebugDataHolder $debugDataHolder = null)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->shouldValidateSchema = $shouldValidateSchema;
|
||||
|
||||
parent::__construct($registry, $debugDataHolder);
|
||||
}
|
||||
|
||||
public function collect(Request $request, Response $response, ?Throwable $exception = null): void
|
||||
{
|
||||
parent::collect($request, $response, $exception);
|
||||
|
||||
$errors = [];
|
||||
$entities = [];
|
||||
$caches = [
|
||||
'enabled' => false,
|
||||
'log_enabled' => false,
|
||||
'counts' => [
|
||||
'puts' => 0,
|
||||
'hits' => 0,
|
||||
'misses' => 0,
|
||||
],
|
||||
'regions' => [
|
||||
'puts' => [],
|
||||
'hits' => [],
|
||||
'misses' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->registry->getManagers() as $name => $em) {
|
||||
assert($em instanceof EntityManagerInterface);
|
||||
if ($this->shouldValidateSchema) {
|
||||
$entities[$name] = [];
|
||||
|
||||
$factory = $em->getMetadataFactory();
|
||||
$validator = new SchemaValidator($em);
|
||||
|
||||
assert($factory instanceof AbstractClassMetadataFactory);
|
||||
|
||||
foreach ($factory->getLoadedMetadata() as $class) {
|
||||
assert($class instanceof ClassMetadata);
|
||||
if (isset($entities[$name][$class->getName()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classErrors = $validator->validateClass($class);
|
||||
$r = $class->getReflectionClass();
|
||||
$entities[$name][$class->getName()] = [
|
||||
'class' => $class->getName(),
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
];
|
||||
|
||||
if (empty($classErrors)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errors[$name][$class->getName()] = $classErrors;
|
||||
}
|
||||
}
|
||||
|
||||
$emConfig = $em->getConfiguration();
|
||||
$slcEnabled = $emConfig->isSecondLevelCacheEnabled();
|
||||
|
||||
if (! $slcEnabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$caches['enabled'] = true;
|
||||
|
||||
$cacheConfiguration = $emConfig->getSecondLevelCacheConfiguration();
|
||||
assert($cacheConfiguration instanceof CacheConfiguration);
|
||||
$cacheLoggerChain = $cacheConfiguration->getCacheLogger();
|
||||
assert($cacheLoggerChain instanceof CacheLoggerChain || $cacheLoggerChain === null);
|
||||
|
||||
if (! $cacheLoggerChain || ! $cacheLoggerChain->getLogger('statistics')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cacheLoggerStats = $cacheLoggerChain->getLogger('statistics');
|
||||
assert($cacheLoggerStats instanceof StatisticsCacheLogger);
|
||||
$caches['log_enabled'] = true;
|
||||
|
||||
$caches['counts']['puts'] += $cacheLoggerStats->getPutCount();
|
||||
$caches['counts']['hits'] += $cacheLoggerStats->getHitCount();
|
||||
$caches['counts']['misses'] += $cacheLoggerStats->getMissCount();
|
||||
|
||||
foreach ($cacheLoggerStats->getRegionsPut() as $key => $value) {
|
||||
if (! isset($caches['regions']['puts'][$key])) {
|
||||
$caches['regions']['puts'][$key] = 0;
|
||||
}
|
||||
|
||||
$caches['regions']['puts'][$key] += $value;
|
||||
}
|
||||
|
||||
foreach ($cacheLoggerStats->getRegionsHit() as $key => $value) {
|
||||
if (! isset($caches['regions']['hits'][$key])) {
|
||||
$caches['regions']['hits'][$key] = 0;
|
||||
}
|
||||
|
||||
$caches['regions']['hits'][$key] += $value;
|
||||
}
|
||||
|
||||
foreach ($cacheLoggerStats->getRegionsMiss() as $key => $value) {
|
||||
if (! isset($caches['regions']['misses'][$key])) {
|
||||
$caches['regions']['misses'][$key] = 0;
|
||||
}
|
||||
|
||||
$caches['regions']['misses'][$key] += $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['entities'] = $entities;
|
||||
$this->data['errors'] = $errors;
|
||||
$this->data['caches'] = $caches;
|
||||
$this->groupedQueries = null;
|
||||
}
|
||||
|
||||
/** @return array<string, array<class-string, array{class: class-string, file: false|string, line: false|int}>> */
|
||||
public function getEntities()
|
||||
{
|
||||
return $this->data['entities'];
|
||||
}
|
||||
|
||||
/** @return array<string, array<string, list<string>>> */
|
||||
public function getMappingErrors()
|
||||
{
|
||||
return $this->data['errors'];
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getCacheHitsCount()
|
||||
{
|
||||
return $this->data['caches']['counts']['hits'];
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getCachePutsCount()
|
||||
{
|
||||
return $this->data['caches']['counts']['puts'];
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getCacheMissesCount()
|
||||
{
|
||||
return $this->data['caches']['counts']['misses'];
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function getCacheEnabled()
|
||||
{
|
||||
return $this->data['caches']['enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, int>>
|
||||
* @psalm-return array<"puts"|"hits"|"misses", array<string, int>>
|
||||
*/
|
||||
public function getCacheRegions()
|
||||
{
|
||||
return $this->data['caches']['regions'];
|
||||
}
|
||||
|
||||
/** @return array<string, int> */
|
||||
public function getCacheCounts()
|
||||
{
|
||||
return $this->data['caches']['counts'];
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getInvalidEntityCount()
|
||||
{
|
||||
return $this->invalidEntityCount ??= array_sum(array_map('count', $this->data['errors']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
* @psalm-return array<string, list<QueryType&array{count: int, index: int, executionPercent?: float}>>
|
||||
*/
|
||||
public function getGroupedQueries()
|
||||
{
|
||||
if ($this->groupedQueries !== null) {
|
||||
return $this->groupedQueries;
|
||||
}
|
||||
|
||||
$this->groupedQueries = [];
|
||||
$totalExecutionMS = 0;
|
||||
foreach ($this->data['queries'] as $connection => $queries) {
|
||||
$connectionGroupedQueries = [];
|
||||
foreach ($queries as $i => $query) {
|
||||
$key = $query['sql'];
|
||||
if (! isset($connectionGroupedQueries[$key])) {
|
||||
$connectionGroupedQueries[$key] = $query;
|
||||
$connectionGroupedQueries[$key]['executionMS'] = 0;
|
||||
$connectionGroupedQueries[$key]['count'] = 0;
|
||||
$connectionGroupedQueries[$key]['index'] = $i; // "Explain query" relies on query index in 'queries'.
|
||||
}
|
||||
|
||||
$connectionGroupedQueries[$key]['executionMS'] += $query['executionMS'];
|
||||
$connectionGroupedQueries[$key]['count']++;
|
||||
$totalExecutionMS += $query['executionMS'];
|
||||
}
|
||||
|
||||
usort($connectionGroupedQueries, static function ($a, $b) {
|
||||
if ($a['executionMS'] === $b['executionMS']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a['executionMS'] < $b['executionMS'] ? 1 : -1;
|
||||
});
|
||||
$this->groupedQueries[$connection] = $connectionGroupedQueries;
|
||||
}
|
||||
|
||||
foreach ($this->groupedQueries as $connection => $queries) {
|
||||
foreach ($queries as $i => $query) {
|
||||
$this->groupedQueries[$connection][$i]['executionPercent'] =
|
||||
$this->executionTimePercentage($query['executionMS'], $totalExecutionMS);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->groupedQueries;
|
||||
}
|
||||
|
||||
private function executionTimePercentage(float $executionTimeMS, float $totalExecutionTimeMS): float
|
||||
{
|
||||
if (! $totalExecutionTimeMS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $executionTimeMS / $totalExecutionTimeMS * 100;
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getGroupedQueryCount()
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->getGroupedQueries() as $connectionGroupedQueries) {
|
||||
$count += count($connectionGroupedQueries);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Dbal;
|
||||
|
||||
use Doctrine\DBAL\Schema\AbstractAsset;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/** @deprecated Implement your own include/exclude mechanism */
|
||||
class BlacklistSchemaAssetFilter
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $blacklist;
|
||||
|
||||
/** @param string[] $blacklist */
|
||||
public function __construct(array $blacklist)
|
||||
{
|
||||
$this->blacklist = $blacklist;
|
||||
}
|
||||
|
||||
/** @param string|AbstractAsset $assetName */
|
||||
public function __invoke($assetName): bool
|
||||
{
|
||||
if ($assetName instanceof AbstractAsset) {
|
||||
$assetName = $assetName->getName();
|
||||
}
|
||||
|
||||
return ! in_array($assetName, $this->blacklist, true);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Dbal;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
|
||||
use Doctrine\Persistence\AbstractManagerRegistry;
|
||||
|
||||
class ManagerRegistryAwareConnectionProvider implements ConnectionProvider
|
||||
{
|
||||
private AbstractManagerRegistry $managerRegistry;
|
||||
|
||||
public function __construct(AbstractManagerRegistry $managerRegistry)
|
||||
{
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
}
|
||||
|
||||
public function getDefaultConnection(): Connection
|
||||
{
|
||||
return $this->managerRegistry->getConnection();
|
||||
}
|
||||
|
||||
public function getConnection(string $name): Connection
|
||||
{
|
||||
return $this->managerRegistry->getConnection($name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Dbal;
|
||||
|
||||
use Doctrine\DBAL\Schema\AbstractAsset;
|
||||
|
||||
use function preg_match;
|
||||
|
||||
class RegexSchemaAssetFilter
|
||||
{
|
||||
private string $filterExpression;
|
||||
|
||||
public function __construct(string $filterExpression)
|
||||
{
|
||||
$this->filterExpression = $filterExpression;
|
||||
}
|
||||
|
||||
/** @param string|AbstractAsset $assetName */
|
||||
public function __invoke($assetName): bool
|
||||
{
|
||||
if ($assetName instanceof AbstractAsset) {
|
||||
$assetName = $assetName->getName();
|
||||
}
|
||||
|
||||
return (bool) preg_match($this->filterExpression, $assetName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Dbal;
|
||||
|
||||
use Doctrine\DBAL\Schema\AbstractAsset;
|
||||
|
||||
/**
|
||||
* Manages schema filters passed to Connection::setSchemaAssetsFilter()
|
||||
*/
|
||||
class SchemaAssetsFilterManager
|
||||
{
|
||||
/** @var callable[] */
|
||||
private array $schemaAssetFilters;
|
||||
|
||||
/** @param callable[] $schemaAssetFilters */
|
||||
public function __construct(array $schemaAssetFilters)
|
||||
{
|
||||
$this->schemaAssetFilters = $schemaAssetFilters;
|
||||
}
|
||||
|
||||
/** @param string|AbstractAsset $assetName */
|
||||
public function __invoke($assetName): bool
|
||||
{
|
||||
foreach ($this->schemaAssetFilters as $schemaAssetFilter) {
|
||||
if ($schemaAssetFilter($assetName) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function array_keys;
|
||||
use function assert;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
use function trigger_deprecation;
|
||||
|
||||
/** @internal */
|
||||
final class CacheCompatibilityPass implements CompilerPassInterface
|
||||
{
|
||||
private const CONFIGURATION_TAG = 'doctrine.orm.configuration';
|
||||
private const CACHE_METHODS_PSR6_SUPPORT = [
|
||||
'setMetadataCache',
|
||||
'setQueryCache',
|
||||
'setResultCache',
|
||||
];
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
foreach (array_keys($container->findTaggedServiceIds(self::CONFIGURATION_TAG)) as $id) {
|
||||
foreach ($container->getDefinition($id)->getMethodCalls() as $methodCall) {
|
||||
if ($methodCall[0] === 'setSecondLevelCacheConfiguration') {
|
||||
$this->updateSecondLevelCache($container, $methodCall[1][0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! in_array($methodCall[0], self::CACHE_METHODS_PSR6_SUPPORT, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aliasId = (string) $methodCall[1][0];
|
||||
$definitionId = (string) $container->getAlias($aliasId);
|
||||
|
||||
$this->wrapIfNecessary($container, $aliasId, $definitionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSecondLevelCache(ContainerBuilder $container, Definition $slcConfigDefinition): void
|
||||
{
|
||||
foreach ($slcConfigDefinition->getMethodCalls() as $methodCall) {
|
||||
if ($methodCall[0] !== 'setCacheFactory') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$factoryDefinition = $methodCall[1][0];
|
||||
assert($factoryDefinition instanceof Definition);
|
||||
$aliasId = (string) $factoryDefinition->getArgument(1);
|
||||
$this->wrapIfNecessary($container, $aliasId, (string) $container->getAlias($aliasId));
|
||||
foreach ($factoryDefinition->getMethodCalls() as $factoryMethodCall) {
|
||||
if ($factoryMethodCall[0] !== 'setRegion') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$regionDefinition = $container->getDefinition($factoryMethodCall[1][0]);
|
||||
|
||||
// Get inner service for FileLock
|
||||
if ($regionDefinition->getClass() === '%doctrine.orm.second_level_cache.filelock_region.class%') {
|
||||
$regionDefinition = $container->getDefinition($regionDefinition->getArgument(0));
|
||||
}
|
||||
|
||||
// We don't know how to adjust custom region classes
|
||||
if ($regionDefinition->getClass() !== '%doctrine.orm.second_level_cache.default_region.class%') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$driverId = (string) $regionDefinition->getArgument(1);
|
||||
if (! $container->hasAlias($driverId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->wrapIfNecessary($container, $driverId, (string) $container->getAlias($driverId));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function createCompatibilityLayerDefinition(ContainerBuilder $container, string $definitionId): ?Definition
|
||||
{
|
||||
$definition = $container->getDefinition($definitionId);
|
||||
|
||||
while (! $definition->getClass() && $definition instanceof ChildDefinition) {
|
||||
$definition = $container->findDefinition($definition->getParent());
|
||||
}
|
||||
|
||||
if (is_a($definition->getClass(), CacheItemPoolInterface::class, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
trigger_deprecation(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.4',
|
||||
'Configuring doctrine/cache is deprecated. Please update the cache service "%s" to use a PSR-6 cache.',
|
||||
$definitionId,
|
||||
);
|
||||
|
||||
return (new Definition(CacheItemPoolInterface::class))
|
||||
->setFactory([CacheAdapter::class, 'wrap'])
|
||||
->addArgument(new Reference($definitionId));
|
||||
}
|
||||
|
||||
private function wrapIfNecessary(ContainerBuilder $container, string $aliasId, string $definitionId): void
|
||||
{
|
||||
$compatibilityLayer = $this->createCompatibilityLayerDefinition($container, $definitionId);
|
||||
if ($compatibilityLayer === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compatibilityLayerId = $definitionId . '.compatibility_layer';
|
||||
$container->setAlias($aliasId, $compatibilityLayerId);
|
||||
$container->setDefinition($compatibilityLayerId, $compatibilityLayer);
|
||||
}
|
||||
}
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter;
|
||||
use Symfony\Component\Cache\Adapter\PdoAdapter;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Injects Doctrine DBAL and legacy PDO adapters into their schema subscribers.
|
||||
*
|
||||
* Must be run later after ResolveChildDefinitionsPass.
|
||||
*
|
||||
* @final since 2.9
|
||||
*/
|
||||
class CacheSchemaSubscriberPass implements CompilerPassInterface
|
||||
{
|
||||
/** @return void */
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
// deprecated in Symfony 6.3
|
||||
$this->injectAdapters($container, 'doctrine.orm.listeners.doctrine_dbal_cache_adapter_schema_subscriber', DoctrineDbalAdapter::class);
|
||||
|
||||
$this->injectAdapters($container, 'doctrine.orm.listeners.doctrine_dbal_cache_adapter_schema_listener', DoctrineDbalAdapter::class);
|
||||
|
||||
// available in Symfony 5.1 and up to Symfony 5.4 (deprecated)
|
||||
$this->injectAdapters($container, 'doctrine.orm.listeners.pdo_cache_adapter_doctrine_schema_subscriber', PdoAdapter::class);
|
||||
}
|
||||
|
||||
private function injectAdapters(ContainerBuilder $container, string $subscriberId, string $class)
|
||||
{
|
||||
if (! $container->hasDefinition($subscriberId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriber = $container->getDefinition($subscriberId);
|
||||
|
||||
$cacheAdaptersReferences = [];
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
if ($definition->isAbstract() || $definition->isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($definition->getClass() !== $class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cacheAdaptersReferences[] = new Reference($id);
|
||||
}
|
||||
|
||||
$subscriber->replaceArgument(0, $cacheAdaptersReferences);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Processes the doctrine.dbal.schema_filter
|
||||
*
|
||||
* @final since 2.9
|
||||
*/
|
||||
class DbalSchemaFilterPass implements CompilerPassInterface
|
||||
{
|
||||
/** @return void */
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$filters = $container->findTaggedServiceIds('doctrine.dbal.schema_filter');
|
||||
|
||||
$connectionFilters = [];
|
||||
foreach ($filters as $id => $tagAttributes) {
|
||||
foreach ($tagAttributes as $attributes) {
|
||||
$name = $attributes['connection'] ?? $container->getParameter('doctrine.default_connection');
|
||||
|
||||
if (! isset($connectionFilters[$name])) {
|
||||
$connectionFilters[$name] = [];
|
||||
}
|
||||
|
||||
$connectionFilters[$name][] = new Reference($id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($connectionFilters as $name => $references) {
|
||||
$configurationId = sprintf('doctrine.dbal.%s_connection.configuration', $name);
|
||||
|
||||
if (! $container->hasDefinition($configurationId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$definition = new ChildDefinition('doctrine.dbal.schema_asset_filter_manager');
|
||||
$definition->setArgument(0, $references);
|
||||
|
||||
$id = sprintf('doctrine.dbal.%s_schema_asset_filter_manager', $name);
|
||||
$container->setDefinition($id, $definition);
|
||||
$container->findDefinition($configurationId)
|
||||
->addMethodCall('setSchemaAssetsFilter', [new Reference($id)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+183
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
|
||||
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
|
||||
use Doctrine\ORM\Mapping\Driver\XmlDriver;
|
||||
use Doctrine\ORM\Mapping\Driver\YamlDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\PHPDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator;
|
||||
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterMappingsPass;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Class for Symfony bundles to configure mappings for model classes not in the
|
||||
* auto-mapped folder.
|
||||
*
|
||||
* @final since 2.9
|
||||
*/
|
||||
class DoctrineOrmMappingsPass extends RegisterMappingsPass
|
||||
{
|
||||
/**
|
||||
* You should not directly instantiate this class but use one of the
|
||||
* factory methods.
|
||||
*
|
||||
* @param Definition|Reference $driver Driver DI definition or reference.
|
||||
* @param string[] $namespaces List of namespaces handled by $driver.
|
||||
* @param string[] $managerParameters Ordered list of container parameters that
|
||||
* could hold the manager name.
|
||||
* doctrine.default_entity_manager is appended
|
||||
* automatically.
|
||||
* @param string|false $enabledParameter If specified, the compiler pass only executes
|
||||
* if this parameter is defined in the service
|
||||
* container.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
*/
|
||||
public function __construct($driver, array $namespaces, array $managerParameters, $enabledParameter = false, array $aliasMap = [])
|
||||
{
|
||||
$managerParameters[] = 'doctrine.default_entity_manager';
|
||||
|
||||
parent::__construct(
|
||||
$driver,
|
||||
$namespaces,
|
||||
$managerParameters,
|
||||
'doctrine.orm.%s_metadata_driver',
|
||||
$enabledParameter,
|
||||
'doctrine.orm.%s_configuration',
|
||||
'addEntityNamespace',
|
||||
$aliasMap,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces Hashmap of directory path to namespace.
|
||||
* @param string[] $managerParameters List of parameters that could which object manager name
|
||||
* your bundle uses. This compiler pass will automatically
|
||||
* append the parameter name for the default entity manager
|
||||
* to this list.
|
||||
* @param string|false $enabledParameter Service container parameter that must be present to
|
||||
* enable the mapping. Set to false to not do any check,
|
||||
* optional.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function createXmlMappingDriver(array $namespaces, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [], bool $enableXsdValidation = false)
|
||||
{
|
||||
$locator = new Definition(SymfonyFileLocator::class, [$namespaces, '.orm.xml']);
|
||||
$driver = new Definition(XmlDriver::class, [$locator, XmlDriver::DEFAULT_FILE_EXTENSION, $enableXsdValidation]);
|
||||
|
||||
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces Hashmap of directory path to namespace
|
||||
* @param string[] $managerParameters List of parameters that could which object manager name
|
||||
* your bundle uses. This compiler pass will automatically
|
||||
* append the parameter name for the default entity manager
|
||||
* to this list.
|
||||
* @param string|false $enabledParameter Service container parameter that must be present to
|
||||
* enable the mapping. Set to false to not do any check,
|
||||
* optional.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function createYamlMappingDriver(array $namespaces, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
|
||||
{
|
||||
$locator = new Definition(SymfonyFileLocator::class, [$namespaces, '.orm.yml']);
|
||||
$driver = new Definition(YamlDriver::class, [$locator]);
|
||||
|
||||
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces Hashmap of directory path to namespace
|
||||
* @param string[] $managerParameters List of parameters that could which object manager name
|
||||
* your bundle uses. This compiler pass will automatically
|
||||
* append the parameter name for the default entity manager
|
||||
* to this list.
|
||||
* @param string|false $enabledParameter Service container parameter that must be present to
|
||||
* enable the mapping. Set to false to not do any check,
|
||||
* optional.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function createPhpMappingDriver(array $namespaces, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
|
||||
{
|
||||
$locator = new Definition(SymfonyFileLocator::class, [$namespaces, '.php']);
|
||||
$driver = new Definition(PHPDriver::class, [$locator]);
|
||||
|
||||
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces List of namespaces that are handled with annotation mapping
|
||||
* @param string[] $directories List of directories to look for annotated classes
|
||||
* @param string[] $managerParameters List of parameters that could which object manager name
|
||||
* your bundle uses. This compiler pass will automatically
|
||||
* append the parameter name for the default entity manager
|
||||
* to this list.
|
||||
* @param string|false $enabledParameter Service container parameter that must be present to
|
||||
* enable the mapping. Set to false to not do any check,
|
||||
* optional.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
* @param bool $reportFieldsWhereDeclared Will report fields for the classes where they are declared
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function createAnnotationMappingDriver(array $namespaces, array $directories, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [], bool $reportFieldsWhereDeclared = false)
|
||||
{
|
||||
$reader = new Reference('annotation_reader');
|
||||
$driver = new Definition(AnnotationDriver::class, [$reader, $directories, $reportFieldsWhereDeclared]);
|
||||
|
||||
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces List of namespaces that are handled with attribute mapping
|
||||
* @param string[] $directories List of directories to look for classes with attributes
|
||||
* @param string[] $managerParameters List of parameters that could which object manager name
|
||||
* your bundle uses. This compiler pass will automatically
|
||||
* append the parameter name for the default entity manager
|
||||
* to this list.
|
||||
* @param string|false $enabledParameter Service container parameter that must be present to
|
||||
* enable the mapping. Set to false to not do any check,
|
||||
* optional.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
* @param bool $reportFieldsWhereDeclared Will report fields for the classes where they are declared
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function createAttributeMappingDriver(array $namespaces, array $directories, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [], bool $reportFieldsWhereDeclared = false)
|
||||
{
|
||||
$driver = new Definition(AttributeDriver::class, [$directories, $reportFieldsWhereDeclared]);
|
||||
|
||||
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces List of namespaces that are handled with static php mapping
|
||||
* @param string[] $directories List of directories to look for static php mapping files
|
||||
* @param string[] $managerParameters List of parameters that could which object manager name
|
||||
* your bundle uses. This compiler pass will automatically
|
||||
* append the parameter name for the default entity manager
|
||||
* to this list.
|
||||
* @param string|false $enabledParameter Service container parameter that must be present to
|
||||
* enable the mapping. Set to false to not do any check,
|
||||
* optional.
|
||||
* @param string[] $aliasMap Map of alias to namespace.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function createStaticPhpMappingDriver(array $namespaces, array $directories, array $managerParameters = [], $enabledParameter = false, array $aliasMap = [])
|
||||
{
|
||||
$driver = new Definition(StaticPHPDriver::class, [$directories]);
|
||||
|
||||
return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap);
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Mapping\ContainerEntityListenerResolver;
|
||||
use Doctrine\Bundle\DoctrineBundle\Mapping\EntityListenerServiceResolver;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function is_a;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Class for Symfony bundles to register entity listeners
|
||||
*
|
||||
* @final since 2.9
|
||||
*/
|
||||
class EntityListenerPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
/** @return void */
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$resolvers = $this->findAndSortTaggedServices('doctrine.orm.entity_listener', $container);
|
||||
|
||||
$lazyServiceReferencesByResolver = [];
|
||||
|
||||
foreach ($resolvers as $reference) {
|
||||
$id = $reference->__toString();
|
||||
foreach ($container->getDefinition($id)->getTag('doctrine.orm.entity_listener') as $attributes) {
|
||||
$name = $attributes['entity_manager'] ?? $container->getParameter('doctrine.default_entity_manager');
|
||||
$entityManager = sprintf('doctrine.orm.%s_entity_manager', $name);
|
||||
|
||||
if (! $container->hasDefinition($entityManager)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolverId = sprintf('doctrine.orm.%s_entity_listener_resolver', $name);
|
||||
|
||||
if (! $container->has($resolverId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolver = $container->findDefinition($resolverId);
|
||||
$resolver->setPublic(true);
|
||||
|
||||
if (isset($attributes['entity'])) {
|
||||
$this->attachToListener($container, $name, $this->getConcreteDefinitionClass($container->findDefinition($id), $container, $id), $attributes);
|
||||
}
|
||||
|
||||
$resolverClass = $this->getResolverClass($resolver, $container, $resolverId);
|
||||
$resolverSupportsLazyListeners = is_a($resolverClass, EntityListenerServiceResolver::class, true);
|
||||
|
||||
$lazyByAttribute = isset($attributes['lazy']) && $attributes['lazy'];
|
||||
if ($lazyByAttribute && ! $resolverSupportsLazyListeners) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Lazy-loaded entity listeners can only be resolved by a resolver implementing %s.',
|
||||
EntityListenerServiceResolver::class,
|
||||
));
|
||||
}
|
||||
|
||||
if (! isset($attributes['lazy']) && $resolverSupportsLazyListeners || $lazyByAttribute) {
|
||||
$listener = $container->findDefinition($id);
|
||||
|
||||
$resolver->addMethodCall('registerService', [$this->getConcreteDefinitionClass($listener, $container, $id), $id]);
|
||||
|
||||
// if the resolver uses the default class we will use a service locator for all listeners
|
||||
if ($resolverClass === ContainerEntityListenerResolver::class) {
|
||||
if (! isset($lazyServiceReferencesByResolver[$resolverId])) {
|
||||
$lazyServiceReferencesByResolver[$resolverId] = [];
|
||||
}
|
||||
|
||||
$lazyServiceReferencesByResolver[$resolverId][$id] = new Reference($id);
|
||||
} else {
|
||||
$listener->setPublic(true);
|
||||
}
|
||||
} else {
|
||||
$resolver->addMethodCall('register', [new Reference($id)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($lazyServiceReferencesByResolver as $resolverId => $listenerReferences) {
|
||||
$container->findDefinition($resolverId)->setArgument(0, ServiceLocatorTagPass::register($container, $listenerReferences));
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array{entity: class-string, event?: ?string, method?: string} $attributes */
|
||||
private function attachToListener(ContainerBuilder $container, string $name, string $class, array $attributes): void
|
||||
{
|
||||
$listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
|
||||
|
||||
if (! $container->has($listenerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$args = [
|
||||
$attributes['entity'],
|
||||
$class,
|
||||
$attributes['event'] ?? null,
|
||||
];
|
||||
|
||||
if (isset($attributes['method'])) {
|
||||
$args[] = $attributes['method'];
|
||||
} elseif (isset($attributes['event']) && ! method_exists($class, $attributes['event']) && method_exists($class, '__invoke')) {
|
||||
$args[] = '__invoke';
|
||||
}
|
||||
|
||||
$container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
|
||||
}
|
||||
|
||||
private function getResolverClass(Definition $resolver, ContainerBuilder $container, string $id): string
|
||||
{
|
||||
$resolverClass = $this->getConcreteDefinitionClass($resolver, $container, $id);
|
||||
|
||||
if (substr($resolverClass, 0, 1) === '%') {
|
||||
// resolve container parameter first
|
||||
$resolverClass = $container->getParameterBag()->resolveValue($resolverClass);
|
||||
}
|
||||
|
||||
return $resolverClass;
|
||||
}
|
||||
|
||||
private function getConcreteDefinitionClass(Definition $definition, ContainerBuilder $container, string $id): string
|
||||
{
|
||||
$class = $definition->getClass();
|
||||
if ($class) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
while ($definition instanceof ChildDefinition) {
|
||||
$definition = $container->findDefinition($definition->getParent());
|
||||
|
||||
$class = $definition->getClass();
|
||||
if ($class) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('The service "%s" must define its class.', $id));
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\Bundle\DoctrineBundle\Mapping\MappingDriver;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory as ORMClassMetadataFactory;
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function array_combine;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function sprintf;
|
||||
|
||||
final class IdGeneratorPass implements CompilerPassInterface
|
||||
{
|
||||
public const ID_GENERATOR_TAG = 'doctrine.id_generator';
|
||||
public const CONFIGURATION_TAG = 'doctrine.orm.configuration';
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$generatorIds = array_keys($container->findTaggedServiceIds(self::ID_GENERATOR_TAG));
|
||||
|
||||
// when ORM is not enabled
|
||||
if (! $container->hasDefinition('doctrine.orm.configuration') || ! $generatorIds) {
|
||||
return;
|
||||
}
|
||||
|
||||
$generatorRefs = array_map(static function ($id) {
|
||||
return new Reference($id);
|
||||
}, $generatorIds);
|
||||
|
||||
$ref = ServiceLocatorTagPass::register($container, array_combine($generatorIds, $generatorRefs));
|
||||
$container->setAlias('doctrine.id_generator_locator', new Alias((string) $ref, false));
|
||||
|
||||
foreach ($container->findTaggedServiceIds(self::CONFIGURATION_TAG) as $id => $tags) {
|
||||
$configurationDef = $container->getDefinition($id);
|
||||
$methodCalls = $configurationDef->getMethodCalls();
|
||||
$metadataDriverImpl = null;
|
||||
|
||||
foreach ($methodCalls as $i => [$method, $arguments]) {
|
||||
if ($method === 'setMetadataDriverImpl') {
|
||||
$metadataDriverImpl = (string) $arguments[0];
|
||||
}
|
||||
|
||||
if ($method !== 'setClassMetadataFactoryName') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($arguments[0] !== ORMClassMetadataFactory::class && $arguments[0] !== ClassMetadataFactory::class) {
|
||||
$class = $container->getReflectionClass($arguments[0]);
|
||||
|
||||
if ($class && $class->isSubclassOf(ClassMetadataFactory::class)) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$methodCalls[$i] = ['setClassMetadataFactoryName', [ClassMetadataFactory::class]];
|
||||
}
|
||||
|
||||
if ($metadataDriverImpl === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$configurationDef->setMethodCalls($methodCalls);
|
||||
$container->register('.' . $metadataDriverImpl, MappingDriver::class)
|
||||
->setDecoratedService($metadataDriverImpl)
|
||||
->setArguments([
|
||||
new Reference(sprintf('.%s.inner', $metadataDriverImpl)),
|
||||
new Reference('doctrine.id_generator_locator'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Middleware\ConnectionNameAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function is_subclass_of;
|
||||
use function sprintf;
|
||||
use function uasort;
|
||||
|
||||
final class MiddlewaresPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (! $container->hasParameter('doctrine.connections')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$middlewareAbstractDefs = [];
|
||||
$middlewareConnections = [];
|
||||
$middlewarePriorities = [];
|
||||
foreach ($container->findTaggedServiceIds('doctrine.middleware') as $id => $tags) {
|
||||
$middlewareAbstractDefs[$id] = $container->getDefinition($id);
|
||||
// When a def has doctrine.middleware tags with connection attributes equal to connection names
|
||||
// registration of this middleware is limited to the connections with these names
|
||||
foreach ($tags as $tag) {
|
||||
if (! isset($tag['connection'])) {
|
||||
if (isset($tag['priority']) && ! isset($middlewarePriorities[$id])) {
|
||||
$middlewarePriorities[$id] = $tag['priority'];
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$middlewareConnections[$id][$tag['connection']] = $tag['priority'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_keys($container->getParameter('doctrine.connections')) as $name) {
|
||||
$middlewareRefs = [];
|
||||
$i = 0;
|
||||
foreach ($middlewareAbstractDefs as $id => $abstractDef) {
|
||||
if (isset($middlewareConnections[$id]) && ! array_key_exists($name, $middlewareConnections[$id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childDef = $container->setDefinition(
|
||||
$childId = sprintf('%s.%s', $id, $name),
|
||||
(new ChildDefinition($id))
|
||||
->setTags($abstractDef->getTags())->clearTag('doctrine.middleware')
|
||||
->setAutoconfigured($abstractDef->isAutoconfigured())
|
||||
->setAutowired($abstractDef->isAutowired()),
|
||||
);
|
||||
$middlewareRefs[$id] = [new Reference($childId), ++$i];
|
||||
|
||||
if (! is_subclass_of($abstractDef->getClass(), ConnectionNameAwareInterface::class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childDef->addMethodCall('setConnectionName', [$name]);
|
||||
}
|
||||
|
||||
$middlewareRefs = array_map(
|
||||
static fn (string $id, array $ref) => [
|
||||
$middlewareConnections[$id][$name] ?? $middlewarePriorities[$id] ?? 0,
|
||||
$ref[1],
|
||||
$ref[0],
|
||||
],
|
||||
array_keys($middlewareRefs),
|
||||
array_values($middlewareRefs),
|
||||
);
|
||||
uasort($middlewareRefs, static fn (array $a, array $b): int => $b[0] <=> $a[0] ?: $a[1] <=> $b[1]);
|
||||
$middlewareRefs = array_map(static fn (array $value): Reference => $value[2], $middlewareRefs);
|
||||
|
||||
$container
|
||||
->getDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name))
|
||||
->addMethodCall('setMiddlewares', [$middlewareRefs]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/** @internal */
|
||||
final class RemoveLoggingMiddlewarePass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if ($container->has('logger')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->removeDefinition('doctrine.dbal.logging_middleware');
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Controller\ProfilerController;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/** @internal */
|
||||
final class RemoveProfilerControllerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if ($container->has('twig') && $container->has('profiler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->removeDefinition(ProfilerController::class);
|
||||
}
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function array_combine;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
|
||||
final class ServiceRepositoryCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public const REPOSITORY_SERVICE_TAG = 'doctrine.repository_service';
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
// when ORM is not enabled
|
||||
if (! $container->hasDefinition('doctrine.orm.container_repository_factory')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$locatorDef = $container->getDefinition('doctrine.orm.container_repository_factory');
|
||||
|
||||
$repoServiceIds = array_keys($container->findTaggedServiceIds(self::REPOSITORY_SERVICE_TAG));
|
||||
|
||||
$repoReferences = array_map(static function ($id) {
|
||||
return new Reference($id);
|
||||
}, $repoServiceIds);
|
||||
|
||||
$ref = ServiceLocatorTagPass::register($container, array_combine($repoServiceIds, $repoReferences));
|
||||
$locatorDef->replaceArgument(0, $ref);
|
||||
}
|
||||
}
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||
|
||||
use function array_keys;
|
||||
use function method_exists;
|
||||
|
||||
/**
|
||||
* Blacklist tables used by well-known Symfony classes.
|
||||
*
|
||||
* @deprecated Implement your own include/exclude mechanism
|
||||
*
|
||||
* @final since 2.9
|
||||
*/
|
||||
class WellKnownSchemaFilterPass implements CompilerPassInterface
|
||||
{
|
||||
/** @return void */
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$blacklist = [];
|
||||
|
||||
foreach ($container->getDefinitions() as $definition) {
|
||||
if ($definition->isAbstract() || $definition->isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($definition->getClass() !== PdoSessionHandler::class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$table = $definition->getArguments()[1]['db_table'] ?? 'sessions';
|
||||
|
||||
if (! method_exists($definition->getClass(), 'configureSchema')) {
|
||||
$blacklist[] = $table;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (! $blacklist) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('doctrine.dbal.well_known_schema_asset_filter');
|
||||
$definition->replaceArgument(0, $blacklist);
|
||||
|
||||
foreach (array_keys($container->getParameter('doctrine.connections')) as $name) {
|
||||
$definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,893 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
|
||||
|
||||
use Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
|
||||
use function array_diff_key;
|
||||
use function array_intersect_key;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_pop;
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function constant;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function key;
|
||||
use function method_exists;
|
||||
use function reset;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* This class contains the configuration information for the bundle
|
||||
*
|
||||
* This information is solely responsible for how the different configuration
|
||||
* sections are normalized, and merged.
|
||||
*
|
||||
* @final since 2.9
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
private bool $debug;
|
||||
|
||||
/** @param bool $debug Whether to use the debug mode */
|
||||
public function __construct(bool $debug)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('doctrine');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$this->addDbalSection($rootNode);
|
||||
$this->addOrmSection($rootNode);
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add DBAL section to configuration tree
|
||||
*/
|
||||
private function addDbalSection(ArrayNodeDefinition $node): void
|
||||
{
|
||||
// Key that should not be rewritten to the connection config
|
||||
$excludedKeys = ['default_connection' => true, 'driver_schemes' => true, 'driver_scheme' => true, 'types' => true, 'type' => true];
|
||||
|
||||
$node
|
||||
->children()
|
||||
->arrayNode('dbal')
|
||||
->beforeNormalization()
|
||||
->ifTrue(static function ($v) use ($excludedKeys) {
|
||||
if (! is_array($v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists('connections', $v) || array_key_exists('connection', $v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is there actually anything to use once excluded keys are considered?
|
||||
return (bool) array_diff_key($v, $excludedKeys);
|
||||
})
|
||||
->then(static function ($v) use ($excludedKeys) {
|
||||
$connection = [];
|
||||
foreach ($v as $key => $value) {
|
||||
if (isset($excludedKeys[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$connection[$key] = $v[$key];
|
||||
unset($v[$key]);
|
||||
}
|
||||
|
||||
$v['connections'] = [($v['default_connection'] ?? 'default') => $connection];
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('default_connection')->end()
|
||||
->end()
|
||||
->fixXmlConfig('type')
|
||||
->children()
|
||||
->arrayNode('types')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(static function ($v) {
|
||||
return ['class' => $v];
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('class')->isRequired()->end()
|
||||
->booleanNode('commented')
|
||||
->setDeprecated(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.0',
|
||||
'The doctrine-bundle type commenting features were removed; the corresponding config parameter was deprecated in 2.0 and will be dropped in 3.0.',
|
||||
)
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('driver_scheme')
|
||||
->children()
|
||||
->arrayNode('driver_schemes')
|
||||
->useAttributeAsKey('scheme')
|
||||
->normalizeKeys(false)
|
||||
->scalarPrototype()->end()
|
||||
->info('Defines a driver for given URL schemes. Schemes being driver names cannot be redefined. However, other default schemes can be overwritten.')
|
||||
->validate()
|
||||
->always()
|
||||
->then(static function (array $value) {
|
||||
$unsupportedSchemes = [];
|
||||
|
||||
foreach ($value as $scheme => $driver) {
|
||||
if (! in_array($scheme, ['pdo-mysql', 'pdo-sqlite', 'pdo-pgsql', 'pdo-oci', 'oci8', 'ibm-db2', 'pdo-sqlsrv', 'mysqli', 'pgsql', 'sqlsrv', 'sqlite3'], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$unsupportedSchemes[] = $scheme;
|
||||
}
|
||||
|
||||
if ($unsupportedSchemes) {
|
||||
throw new InvalidArgumentException(sprintf('Registering a scheme with the name of one of the official drivers is forbidden, as those are defined in DBAL itself. The following schemes are forbidden: %s', implode(', ', $unsupportedSchemes)));
|
||||
}
|
||||
|
||||
return $value;
|
||||
})
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('connection')
|
||||
->append($this->getDbalConnectionsNode())
|
||||
->end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dbal connections node
|
||||
*/
|
||||
private function getDbalConnectionsNode(): ArrayNodeDefinition
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('connections');
|
||||
$node = $treeBuilder->getRootNode();
|
||||
|
||||
$connectionNode = $node
|
||||
->requiresAtLeastOneElement()
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array');
|
||||
assert($connectionNode instanceof ArrayNodeDefinition);
|
||||
|
||||
$this->configureDbalDriverNode($connectionNode);
|
||||
|
||||
$collationKey = defined('Doctrine\DBAL\Connection::PARAM_ASCII_STR_ARRAY')
|
||||
? 'collate'
|
||||
: 'collation';
|
||||
|
||||
$connectionNode
|
||||
->fixXmlConfig('option')
|
||||
->fixXmlConfig('mapping_type')
|
||||
->fixXmlConfig('slave')
|
||||
->fixXmlConfig('replica')
|
||||
->fixXmlConfig('default_table_option')
|
||||
->children()
|
||||
->scalarNode('driver')->defaultValue('pdo_mysql')->end()
|
||||
->scalarNode('platform_service')
|
||||
->setDeprecated(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.9',
|
||||
'The "platform_service" configuration key is deprecated since doctrine-bundle 2.9. DBAL 4 will not support setting a custom platform via connection params anymore.',
|
||||
)
|
||||
->end()
|
||||
->booleanNode('auto_commit')->end()
|
||||
->scalarNode('schema_filter')->end()
|
||||
->booleanNode('logging')->defaultValue($this->debug)->end()
|
||||
->booleanNode('profiling')->defaultValue($this->debug)->end()
|
||||
->booleanNode('profiling_collect_backtrace')
|
||||
->defaultValue(false)
|
||||
->info('Enables collecting backtraces when profiling is enabled')
|
||||
->end()
|
||||
->booleanNode('profiling_collect_schema_errors')
|
||||
->defaultValue(true)
|
||||
->info('Enables collecting schema errors when profiling is enabled')
|
||||
->end()
|
||||
->booleanNode('disable_type_comments')->end()
|
||||
->scalarNode('server_version')->end()
|
||||
->integerNode('idle_connection_ttl')->defaultValue(600)->end()
|
||||
->scalarNode('driver_class')->end()
|
||||
->scalarNode('wrapper_class')->end()
|
||||
->booleanNode('keep_slave')
|
||||
->setDeprecated(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.2',
|
||||
'The "keep_slave" configuration key is deprecated since doctrine-bundle 2.2. Use the "keep_replica" configuration key instead.',
|
||||
)
|
||||
->end()
|
||||
->booleanNode('keep_replica')->end()
|
||||
->arrayNode('options')
|
||||
->useAttributeAsKey('key')
|
||||
->prototype('variable')->end()
|
||||
->end()
|
||||
->arrayNode('mapping_types')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->arrayNode('default_table_options')
|
||||
->info(sprintf(
|
||||
"This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','%s', and 'engine'.",
|
||||
$collationKey,
|
||||
))
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('schema_manager_factory')
|
||||
->cannotBeEmpty()
|
||||
->defaultValue($this->getDefaultSchemaManagerFactory())
|
||||
->end()
|
||||
->scalarNode('result_cache')->end()
|
||||
->end();
|
||||
|
||||
// dbal < 2.11
|
||||
$slaveNode = $connectionNode
|
||||
->children()
|
||||
->arrayNode('slaves')
|
||||
->setDeprecated(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.2',
|
||||
'The "slaves" configuration key will be renamed to "replicas" in doctrine-bundle 3.0. "slaves" is deprecated since doctrine-bundle 2.2.',
|
||||
)
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array');
|
||||
$this->configureDbalDriverNode($slaveNode);
|
||||
|
||||
// dbal >= 2.11
|
||||
$replicaNode = $connectionNode
|
||||
->children()
|
||||
->arrayNode('replicas')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array');
|
||||
$this->configureDbalDriverNode($replicaNode);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds config keys related to params processed by the DBAL drivers
|
||||
*
|
||||
* These keys are available for replica configurations too.
|
||||
*/
|
||||
private function configureDbalDriverNode(ArrayNodeDefinition $node): void
|
||||
{
|
||||
$node
|
||||
->validate()
|
||||
->always(static function (array $values) {
|
||||
if (! isset($values['url'])) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$urlConflictingOptions = ['host' => true, 'port' => true, 'user' => true, 'password' => true, 'path' => true, 'dbname' => true, 'unix_socket' => true, 'memory' => true];
|
||||
$urlConflictingValues = array_keys(array_intersect_key($values, $urlConflictingOptions));
|
||||
|
||||
if ($urlConflictingValues) {
|
||||
$tail = count($urlConflictingValues) > 1 ? sprintf('or "%s" options', array_pop($urlConflictingValues)) : 'option';
|
||||
trigger_deprecation(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.4',
|
||||
'Setting the "doctrine.dbal.%s" %s while the "url" one is defined is deprecated',
|
||||
implode('", "', $urlConflictingValues),
|
||||
$tail,
|
||||
);
|
||||
}
|
||||
|
||||
return $values;
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
|
||||
->scalarNode('dbname')->end()
|
||||
->scalarNode('host')->info('Defaults to "localhost" at runtime.')->end()
|
||||
->scalarNode('port')->info('Defaults to null at runtime.')->end()
|
||||
->scalarNode('user')->info('Defaults to "root" at runtime.')->end()
|
||||
->scalarNode('password')->info('Defaults to null at runtime.')->end()
|
||||
->booleanNode('override_url')->setDeprecated(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.4',
|
||||
'The "doctrine.dbal.override_url" configuration key is deprecated.',
|
||||
)->end()
|
||||
->scalarNode('dbname_suffix')->end()
|
||||
->scalarNode('application_name')->end()
|
||||
->scalarNode('charset')->end()
|
||||
->scalarNode('path')->end()
|
||||
->booleanNode('memory')->end()
|
||||
->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end()
|
||||
->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end()
|
||||
->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if omitted)')->end()
|
||||
->booleanNode('service')
|
||||
->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle')
|
||||
->end()
|
||||
->scalarNode('servicename')
|
||||
->info(
|
||||
'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter ' .
|
||||
'for Oracle depending on the service parameter.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('sessionMode')
|
||||
->info('The session mode to use for the oci8 driver')
|
||||
->end()
|
||||
->scalarNode('server')
|
||||
->info('The name of a running database server to connect to for SQL Anywhere.')
|
||||
->end()
|
||||
->scalarNode('default_dbname')
|
||||
->info(
|
||||
'Override the default database (postgres) to connect to for PostgreSQL connexion.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('sslmode')
|
||||
->info(
|
||||
'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with ' .
|
||||
'the server for PostgreSQL.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('sslrootcert')
|
||||
->info(
|
||||
'The name of a file containing SSL certificate authority (CA) certificate(s). ' .
|
||||
'If the file exists, the server\'s certificate will be verified to be signed by one of these authorities.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('sslcert')
|
||||
->info(
|
||||
'The path to the SSL client certificate file for PostgreSQL.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('sslkey')
|
||||
->info(
|
||||
'The path to the SSL client key file for PostgreSQL.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('sslcrl')
|
||||
->info(
|
||||
'The file name of the SSL certificate revocation list for PostgreSQL.',
|
||||
)
|
||||
->end()
|
||||
->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end()
|
||||
->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end()
|
||||
->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end()
|
||||
->scalarNode('instancename')
|
||||
->info(
|
||||
'Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection.' .
|
||||
' It is generally used to connect to an Oracle RAC server to select the name' .
|
||||
' of a particular instance.',
|
||||
)
|
||||
->end()
|
||||
->scalarNode('connectstring')
|
||||
->info(
|
||||
'Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.' .
|
||||
'When using this option, you will still need to provide the user and password parameters, but the other ' .
|
||||
'parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods' .
|
||||
' from Doctrine\DBAL\Connection will no longer function as expected.',
|
||||
)
|
||||
->end()
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
->ifTrue(static function ($v) {
|
||||
return ! isset($v['sessionMode']) && isset($v['session_mode']);
|
||||
})
|
||||
->then(static function ($v) {
|
||||
$v['sessionMode'] = $v['session_mode'];
|
||||
unset($v['session_mode']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
->ifTrue(static function ($v) {
|
||||
return ! isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);
|
||||
})
|
||||
->then(static function ($v) {
|
||||
$v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
|
||||
unset($v['multiple_active_result_sets']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the ORM section to configuration tree
|
||||
*/
|
||||
private function addOrmSection(ArrayNodeDefinition $node): void
|
||||
{
|
||||
// Key that should not be rewritten to the entity-manager config
|
||||
$excludedKeys = [
|
||||
'default_entity_manager' => true,
|
||||
'auto_generate_proxy_classes' => true,
|
||||
'enable_lazy_ghost_objects' => true,
|
||||
'proxy_dir' => true,
|
||||
'proxy_namespace' => true,
|
||||
'resolve_target_entities' => true,
|
||||
'resolve_target_entity' => true,
|
||||
'controller_resolver' => true,
|
||||
];
|
||||
|
||||
$node
|
||||
->children()
|
||||
->arrayNode('orm')
|
||||
->beforeNormalization()
|
||||
->ifTrue(static function ($v) use ($excludedKeys) {
|
||||
if (! empty($v) && ! class_exists(EntityManager::class)) {
|
||||
throw new LogicException('The doctrine/orm package is required when the doctrine.orm config is set.');
|
||||
}
|
||||
|
||||
if (! is_array($v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists('entity_managers', $v) || array_key_exists('entity_manager', $v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is there actually anything to use once excluded keys are considered?
|
||||
return (bool) array_diff_key($v, $excludedKeys);
|
||||
})
|
||||
->then(static function ($v) use ($excludedKeys) {
|
||||
$entityManager = [];
|
||||
foreach ($v as $key => $value) {
|
||||
if (isset($excludedKeys[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entityManager[$key] = $v[$key];
|
||||
unset($v[$key]);
|
||||
}
|
||||
|
||||
$v['entity_managers'] = [($v['default_entity_manager'] ?? 'default') => $entityManager];
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('default_entity_manager')->end()
|
||||
->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
|
||||
->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL", "FILE_NOT_EXISTS_OR_CHANGED"')
|
||||
->validate()
|
||||
->ifTrue(function ($v) {
|
||||
$generationModes = $this->getAutoGenerateModes();
|
||||
|
||||
if (is_int($v) && in_array($v, $generationModes['values']/*array(0, 1, 2, 3)*/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_bool($v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_string($v)) {
|
||||
if (in_array(strtoupper($v), $generationModes['names']/*array('NEVER', 'ALWAYS', 'FILE_NOT_EXISTS', 'EVAL', 'FILE_NOT_EXISTS_OR_CHANGED')*/)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
->thenInvalid('Invalid auto generate mode value %s')
|
||||
->end()
|
||||
->validate()
|
||||
->ifString()
|
||||
->then(static function ($v) {
|
||||
return constant('Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_' . strtoupper($v));
|
||||
})
|
||||
->end()
|
||||
->end()
|
||||
->booleanNode('enable_lazy_ghost_objects')
|
||||
->defaultValue(! method_exists(ProxyFactory::class, 'resetUninitializedProxy'))
|
||||
->info('Enables the new implementation of proxies based on lazy ghosts instead of using the legacy implementation')
|
||||
->end()
|
||||
->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
|
||||
->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
|
||||
->arrayNode('controller_resolver')
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
->booleanNode('auto_mapping')
|
||||
->defaultNull()
|
||||
->info('Set to false to disable using route placeholders as lookup criteria when the primary key doesn\'t match the argument name')
|
||||
->end()
|
||||
->booleanNode('evict_cache')
|
||||
->info('Set to true to fetch the entity from the database instead of using the cache, if any')
|
||||
->defaultFalse()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('entity_manager')
|
||||
->append($this->getOrmEntityManagersNode())
|
||||
->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
|
||||
->append($this->getOrmTargetEntityResolverNode())
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ORM target entity resolver node
|
||||
*/
|
||||
private function getOrmTargetEntityResolverNode(): NodeDefinition
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('resolve_target_entities');
|
||||
$node = $treeBuilder->getRootNode();
|
||||
|
||||
$node
|
||||
->useAttributeAsKey('interface')
|
||||
->prototype('scalar')
|
||||
->cannotBeEmpty()
|
||||
->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ORM entity listener node
|
||||
*/
|
||||
private function getOrmEntityListenersNode(): NodeDefinition
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('entity_listeners');
|
||||
$node = $treeBuilder->getRootNode();
|
||||
|
||||
$normalizer = static function ($mappings) {
|
||||
$entities = [];
|
||||
|
||||
foreach ($mappings as $entityClass => $mapping) {
|
||||
$listeners = [];
|
||||
|
||||
foreach ($mapping as $listenerClass => $listenerEvent) {
|
||||
$events = [];
|
||||
|
||||
foreach ($listenerEvent as $eventType => $eventMapping) {
|
||||
if ($eventMapping === null) {
|
||||
$eventMapping = [null];
|
||||
}
|
||||
|
||||
foreach ($eventMapping as $method) {
|
||||
$events[] = [
|
||||
'type' => $eventType,
|
||||
'method' => $method,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$listeners[] = [
|
||||
'class' => $listenerClass,
|
||||
'event' => $events,
|
||||
];
|
||||
}
|
||||
|
||||
$entities[] = [
|
||||
'class' => $entityClass,
|
||||
'listener' => $listeners,
|
||||
];
|
||||
}
|
||||
|
||||
return ['entities' => $entities];
|
||||
};
|
||||
|
||||
$node
|
||||
->beforeNormalization()
|
||||
// Yaml normalization
|
||||
->ifTrue(static function ($v) {
|
||||
return is_array(reset($v)) && is_string(key(reset($v)));
|
||||
})
|
||||
->then($normalizer)
|
||||
->end()
|
||||
->fixXmlConfig('entity', 'entities')
|
||||
->children()
|
||||
->arrayNode('entities')
|
||||
->useAttributeAsKey('class')
|
||||
->prototype('array')
|
||||
->fixXmlConfig('listener')
|
||||
->children()
|
||||
->arrayNode('listeners')
|
||||
->useAttributeAsKey('class')
|
||||
->prototype('array')
|
||||
->fixXmlConfig('event')
|
||||
->children()
|
||||
->arrayNode('events')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('type')->end()
|
||||
->scalarNode('method')->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ORM entity manager node
|
||||
*/
|
||||
private function getOrmEntityManagersNode(): ArrayNodeDefinition
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('entity_managers');
|
||||
$node = $treeBuilder->getRootNode();
|
||||
|
||||
$node
|
||||
->requiresAtLeastOneElement()
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->addDefaultsIfNotSet()
|
||||
->append($this->getOrmCacheDriverNode('query_cache_driver'))
|
||||
->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
|
||||
->append($this->getOrmCacheDriverNode('result_cache_driver'))
|
||||
->append($this->getOrmEntityListenersNode())
|
||||
->fixXmlConfig('schema_ignore_class', 'schema_ignore_classes')
|
||||
->children()
|
||||
->scalarNode('connection')->end()
|
||||
->scalarNode('class_metadata_factory_name')->defaultValue(ClassMetadataFactory::class)->end()
|
||||
->scalarNode('default_repository_class')->defaultValue(EntityRepository::class)->end()
|
||||
->scalarNode('auto_mapping')->defaultFalse()->end()
|
||||
->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
|
||||
->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
|
||||
->scalarNode('typed_field_mapper')->defaultValue('doctrine.orm.typed_field_mapper.default')->end()
|
||||
->scalarNode('entity_listener_resolver')->defaultNull()->end()
|
||||
->scalarNode('repository_factory')->defaultValue('doctrine.orm.container_repository_factory')->end()
|
||||
->arrayNode('schema_ignore_classes')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->booleanNode('report_fields_where_declared')
|
||||
->defaultValue(! class_exists(AnnotationDriver::class))
|
||||
->info('Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.16 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/10455.')
|
||||
->validate()
|
||||
->ifTrue(static fn (bool $v): bool => ! class_exists(AnnotationDriver::class) && ! $v)
|
||||
->thenInvalid('The setting "report_fields_where_declared" cannot be disabled for ORM 3.')
|
||||
->end()
|
||||
->end()
|
||||
->booleanNode('validate_xml_mapping')->defaultFalse()->info('Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/6728.')->end()
|
||||
->end()
|
||||
->children()
|
||||
->arrayNode('second_level_cache')
|
||||
->children()
|
||||
->append($this->getOrmCacheDriverNode('region_cache_driver'))
|
||||
->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
|
||||
->booleanNode('log_enabled')->defaultValue($this->debug)->end()
|
||||
->scalarNode('region_lifetime')->defaultValue(3600)->end()
|
||||
->booleanNode('enabled')->defaultValue(true)->end()
|
||||
->scalarNode('factory')->end()
|
||||
->end()
|
||||
->fixXmlConfig('region')
|
||||
->children()
|
||||
->arrayNode('regions')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->append($this->getOrmCacheDriverNode('cache_driver'))
|
||||
->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
|
||||
->scalarNode('lock_lifetime')->defaultValue(60)->end()
|
||||
->scalarNode('type')->defaultValue('default')->end()
|
||||
->scalarNode('lifetime')->defaultValue(0)->end()
|
||||
->scalarNode('service')->end()
|
||||
->scalarNode('name')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('logger')
|
||||
->children()
|
||||
->arrayNode('loggers')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('name')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('hydrator')
|
||||
->children()
|
||||
->arrayNode('hydrators')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('mapping')
|
||||
->children()
|
||||
->arrayNode('mappings')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(static function ($v) {
|
||||
return ['type' => $v];
|
||||
})
|
||||
->end()
|
||||
->treatNullLike([])
|
||||
->treatFalseLike(['mapping' => false])
|
||||
->performNoDeepMerging()
|
||||
->children()
|
||||
->scalarNode('mapping')->defaultValue(true)->end()
|
||||
->scalarNode('type')->end()
|
||||
->scalarNode('dir')->end()
|
||||
->scalarNode('alias')->end()
|
||||
->scalarNode('prefix')->end()
|
||||
->booleanNode('is_bundle')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('dql')
|
||||
->fixXmlConfig('string_function')
|
||||
->fixXmlConfig('numeric_function')
|
||||
->fixXmlConfig('datetime_function')
|
||||
->children()
|
||||
->arrayNode('string_functions')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->arrayNode('numeric_functions')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->arrayNode('datetime_functions')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('filter')
|
||||
->children()
|
||||
->arrayNode('filters')
|
||||
->info('Register SQL Filters in the entity manager')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(static function ($v) {
|
||||
return ['class' => $v];
|
||||
})
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
// The content of the XML node is returned as the "value" key so we need to rename it
|
||||
->ifTrue(static function ($v) {
|
||||
return is_array($v) && isset($v['value']);
|
||||
})
|
||||
->then(static function ($v) {
|
||||
$v['class'] = $v['value'];
|
||||
unset($v['value']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->fixXmlConfig('parameter')
|
||||
->children()
|
||||
->scalarNode('class')->isRequired()->end()
|
||||
->booleanNode('enabled')->defaultFalse()->end()
|
||||
->arrayNode('parameters')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('variable')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->fixXmlConfig('identity_generation_preference')
|
||||
->children()
|
||||
->arrayNode('identity_generation_preferences')
|
||||
->info('Configures the preferences for identity generation when using the AUTO strategy. Valid values are "SEQUENCE" or "IDENTITY".')
|
||||
->useAttributeAsKey('platform')
|
||||
->prototype('scalar')
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(static function ($v) {
|
||||
return constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($v));
|
||||
})
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an ORM cache driver node for a given entity manager
|
||||
*/
|
||||
private function getOrmCacheDriverNode(string $name): ArrayNodeDefinition
|
||||
{
|
||||
$treeBuilder = new TreeBuilder($name);
|
||||
$node = $treeBuilder->getRootNode();
|
||||
|
||||
$node
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(static function ($v): array {
|
||||
return ['type' => $v];
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('type')->defaultNull()->end()
|
||||
->scalarNode('id')->end()
|
||||
->scalarNode('pool')->end()
|
||||
->end();
|
||||
|
||||
if ($name !== 'metadata_cache_driver') {
|
||||
$node->addDefaultsIfNotSet();
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find proxy auto generate modes for their names and int values
|
||||
*
|
||||
* @return array{names: list<string>, values: list<int>}
|
||||
*/
|
||||
private function getAutoGenerateModes(): array
|
||||
{
|
||||
$constPrefix = 'AUTOGENERATE_';
|
||||
$prefixLen = strlen($constPrefix);
|
||||
$refClass = new ReflectionClass(ProxyFactory::class);
|
||||
$constsArray = $refClass->getConstants();
|
||||
$namesArray = [];
|
||||
$valuesArray = [];
|
||||
|
||||
foreach ($constsArray as $key => $value) {
|
||||
if (strpos($key, $constPrefix) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$namesArray[] = substr($key, $prefixLen);
|
||||
$valuesArray[] = (int) $value;
|
||||
}
|
||||
|
||||
return [
|
||||
'names' => $namesArray,
|
||||
'values' => $valuesArray,
|
||||
];
|
||||
}
|
||||
|
||||
private function getDefaultSchemaManagerFactory(): string
|
||||
{
|
||||
if (class_exists(LegacySchemaManagerFactory::class)) {
|
||||
return 'doctrine.dbal.legacy_schema_manager_factory';
|
||||
}
|
||||
|
||||
return 'doctrine.dbal.default_schema_manager_factory';
|
||||
}
|
||||
}
|
||||
+1241
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\CacheCompatibilityPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\CacheSchemaSubscriberPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\IdGeneratorPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\MiddlewaresPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\RemoveLoggingMiddlewarePass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\RemoveProfilerControllerPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Proxy\Autoloader;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass;
|
||||
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass;
|
||||
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterUidTypePass;
|
||||
use Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider\EntityFactory;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function clearstatcache;
|
||||
use function dirname;
|
||||
use function spl_autoload_unregister;
|
||||
|
||||
/** @final since 2.9 */
|
||||
class DoctrineBundle extends Bundle
|
||||
{
|
||||
private ?Closure $autoloader = null;
|
||||
|
||||
/** @return void */
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new class () implements CompilerPassInterface {
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if ($container->has('session.handler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->removeDefinition('doctrine.orm.listeners.pdo_session_handler_schema_listener');
|
||||
}
|
||||
}, PassConfig::TYPE_BEFORE_OPTIMIZATION);
|
||||
|
||||
$container->addCompilerPass(new RegisterEventListenersAndSubscribersPass('doctrine.connections', 'doctrine.dbal.%s_connection.event_manager', 'doctrine'), PassConfig::TYPE_BEFORE_OPTIMIZATION);
|
||||
|
||||
if ($container->hasExtension('security')) {
|
||||
$security = $container->getExtension('security');
|
||||
|
||||
if ($security instanceof SecurityExtension) {
|
||||
$security->addUserProviderFactory(new EntityFactory('entity', 'doctrine.orm.security.user.provider'));
|
||||
}
|
||||
}
|
||||
|
||||
$container->addCompilerPass(new CacheCompatibilityPass());
|
||||
$container->addCompilerPass(new DoctrineValidationPass('orm'));
|
||||
$container->addCompilerPass(new EntityListenerPass());
|
||||
$container->addCompilerPass(new ServiceRepositoryCompilerPass());
|
||||
$container->addCompilerPass(new IdGeneratorPass());
|
||||
$container->addCompilerPass(new WellKnownSchemaFilterPass());
|
||||
$container->addCompilerPass(new DbalSchemaFilterPass());
|
||||
$container->addCompilerPass(new CacheSchemaSubscriberPass(), PassConfig::TYPE_BEFORE_REMOVING, -10);
|
||||
$container->addCompilerPass(new RemoveProfilerControllerPass());
|
||||
$container->addCompilerPass(new RemoveLoggingMiddlewarePass());
|
||||
$container->addCompilerPass(new MiddlewaresPass());
|
||||
|
||||
if (! class_exists(RegisterUidTypePass::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->addCompilerPass(new RegisterUidTypePass());
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function boot()
|
||||
{
|
||||
// Register an autoloader for proxies to avoid issues when unserializing them
|
||||
// when the ORM is used.
|
||||
if (! $this->container->hasParameter('doctrine.orm.proxy_namespace')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$namespace = (string) $this->container->getParameter('doctrine.orm.proxy_namespace');
|
||||
$dir = (string) $this->container->getParameter('doctrine.orm.proxy_dir');
|
||||
$proxyGenerator = null;
|
||||
|
||||
if ($this->container->getParameter('doctrine.orm.auto_generate_proxy_classes')) {
|
||||
// See https://github.com/symfony/symfony/pull/3419 for usage of references
|
||||
/** @psalm-suppress UnsupportedPropertyReferenceUsage */
|
||||
$container = &$this->container;
|
||||
|
||||
$proxyGenerator = static function ($proxyDir, $proxyNamespace, $class) use (&$container): void {
|
||||
$originalClassName = (new DefaultProxyClassNameResolver())->resolveClassName($class);
|
||||
$registry = $container->get('doctrine');
|
||||
assert($registry instanceof Registry);
|
||||
|
||||
foreach ($registry->getManagers() as $em) {
|
||||
assert($em instanceof EntityManagerInterface);
|
||||
if (! $em->getConfiguration()->getAutoGenerateProxyClasses()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadataFactory = $em->getMetadataFactory();
|
||||
|
||||
if ($metadataFactory->isTransient($originalClassName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classMetadata = $metadataFactory->getMetadataFor($originalClassName);
|
||||
|
||||
$em->getProxyFactory()->generateProxyClasses([$classMetadata]);
|
||||
|
||||
clearstatcache(true, Autoloader::resolveFile($proxyDir, $proxyNamespace, $class));
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$this->autoloader = Autoloader::register($dir, $namespace, $proxyGenerator);
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function shutdown()
|
||||
{
|
||||
if ($this->autoloader !== null) {
|
||||
spl_autoload_unregister($this->autoloader);
|
||||
$this->autoloader = null;
|
||||
}
|
||||
|
||||
// Clear all entity managers to clear references to entities for GC
|
||||
if ($this->container->hasParameter('doctrine.entity_managers')) {
|
||||
foreach ($this->container->getParameter('doctrine.entity_managers') as $id) {
|
||||
if (! $this->container->initialized($id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->container->get($id)->clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Close all connections to avoid reaching too many connections in the process when booting again later (tests)
|
||||
if (! $this->container->hasParameter('doctrine.connections')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->container->getParameter('doctrine.connections') as $id) {
|
||||
if (! $this->container->initialized($id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->container->get($id)->close();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function registerCommands(Application $application)
|
||||
{
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return dirname(__DIR__);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\EventSubscriber;
|
||||
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
|
||||
/** @deprecated use the {@see AsDoctrineListener} attribute instead */
|
||||
interface EventSubscriberInterface extends EventSubscriber
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\Filter\SQLFilter;
|
||||
|
||||
/**
|
||||
* Configurator for an EntityManager
|
||||
*/
|
||||
class ManagerConfigurator
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $enabledFilters = [];
|
||||
|
||||
/** @var array<string,array<string,string>> */
|
||||
private array $filtersParameters = [];
|
||||
|
||||
/**
|
||||
* @param string[] $enabledFilters
|
||||
* @param array<string,array<string,string>> $filtersParameters
|
||||
*/
|
||||
public function __construct(array $enabledFilters, array $filtersParameters)
|
||||
{
|
||||
$this->enabledFilters = $enabledFilters;
|
||||
$this->filtersParameters = $filtersParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection by name.
|
||||
*/
|
||||
public function configure(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->enableFilters($entityManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables filters for a given entity manager
|
||||
*/
|
||||
private function enableFilters(EntityManagerInterface $entityManager): void
|
||||
{
|
||||
if (empty($this->enabledFilters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filterCollection = $entityManager->getFilters();
|
||||
foreach ($this->enabledFilters as $filter) {
|
||||
$this->setFilterParameters($filter, $filterCollection->enable($filter));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default parameters for a given filter
|
||||
*/
|
||||
private function setFilterParameters(string $name, SQLFilter $filter): void
|
||||
{
|
||||
if (empty($this->filtersParameters[$name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameters = $this->filtersParameters[$name];
|
||||
foreach ($parameters as $paramName => $paramValue) {
|
||||
$filter->setParameter($paramName, $paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
class ClassMetadataCollection
|
||||
{
|
||||
private ?string $path = null;
|
||||
private ?string $namespace = null;
|
||||
|
||||
/** @var ClassMetadata[] */
|
||||
private array $metadata;
|
||||
|
||||
/** @param ClassMetadata[] $metadata */
|
||||
public function __construct(array $metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
/** @return ClassMetadata[] */
|
||||
public function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/** @param string $path */
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/** @param string $namespace */
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
|
||||
|
||||
use Doctrine\ORM\Id\AbstractIdGenerator;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory as BaseClassMetadataFactory;
|
||||
|
||||
use function assert;
|
||||
|
||||
class ClassMetadataFactory extends BaseClassMetadataFactory
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents): void
|
||||
{
|
||||
parent::doLoadMetadata($class, $parent, $rootEntityFound, $nonSuperclassParents);
|
||||
|
||||
$customGeneratorDefinition = $class->customGeneratorDefinition;
|
||||
|
||||
if (! isset($customGeneratorDefinition['instance'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert($customGeneratorDefinition['instance'] instanceof AbstractIdGenerator);
|
||||
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);
|
||||
$class->setIdGenerator($customGeneratorDefinition['instance']);
|
||||
unset($customGeneratorDefinition['instance']);
|
||||
$class->setCustomGeneratorDefinition($customGeneratorDefinition);
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
use function trim;
|
||||
|
||||
/** @final */
|
||||
class ContainerEntityListenerResolver implements EntityListenerServiceResolver
|
||||
{
|
||||
private ContainerInterface $container;
|
||||
|
||||
/** @var object[] Map to store entity listener instances. */
|
||||
private array $instances = [];
|
||||
|
||||
/** @var string[] Map to store registered service ids */
|
||||
private array $serviceIds = [];
|
||||
|
||||
/** @param ContainerInterface $container a service locator for listeners */
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear($className = null): void
|
||||
{
|
||||
if ($className === null) {
|
||||
$this->instances = [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$className = $this->normalizeClassName($className);
|
||||
|
||||
unset($this->instances[$className]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function register($object): void
|
||||
{
|
||||
if (! is_object($object)) {
|
||||
throw new InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object)));
|
||||
}
|
||||
|
||||
$className = $this->normalizeClassName(get_class($object));
|
||||
|
||||
$this->instances[$className] = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function registerService($className, $serviceId)
|
||||
{
|
||||
$this->serviceIds[$this->normalizeClassName($className)] = $serviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function resolve($className): object
|
||||
{
|
||||
$className = $this->normalizeClassName($className);
|
||||
|
||||
if (! isset($this->instances[$className])) {
|
||||
if (isset($this->serviceIds[$className])) {
|
||||
$this->instances[$className] = $this->resolveService($this->serviceIds[$className]);
|
||||
} else {
|
||||
$this->instances[$className] = new $className();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->instances[$className];
|
||||
}
|
||||
|
||||
private function resolveService(string $serviceId): object
|
||||
{
|
||||
if (! $this->container->has($serviceId)) {
|
||||
throw new RuntimeException(sprintf('There is no service named "%s"', $serviceId));
|
||||
}
|
||||
|
||||
return $this->container->get($serviceId);
|
||||
}
|
||||
|
||||
private function normalizeClassName(string $className): string
|
||||
{
|
||||
return trim($className, '\\');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use ReflectionClass;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
|
||||
use function dirname;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* This class provides methods to access Doctrine entity class metadata for a
|
||||
* given bundle, namespace or entity class, for generation purposes
|
||||
*/
|
||||
class DisconnectedMetadataFactory
|
||||
{
|
||||
private ManagerRegistry $registry;
|
||||
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata of all classes of a bundle.
|
||||
*
|
||||
* @param BundleInterface $bundle A BundleInterface instance
|
||||
*
|
||||
* @return ClassMetadataCollection A ClassMetadataCollection instance
|
||||
*
|
||||
* @throws RuntimeException When bundle does not contain mapped entities.
|
||||
*/
|
||||
public function getBundleMetadata(BundleInterface $bundle)
|
||||
{
|
||||
$namespace = $bundle->getNamespace();
|
||||
$metadata = $this->getMetadataForNamespace($namespace);
|
||||
if (! $metadata->getMetadata()) {
|
||||
throw new RuntimeException(sprintf('Bundle "%s" does not contain any mapped entities.', $bundle->getName()));
|
||||
}
|
||||
|
||||
$path = $this->getBasePathForClass($bundle->getName(), $bundle->getNamespace(), $bundle->getPath());
|
||||
|
||||
$metadata->setPath($path);
|
||||
$metadata->setNamespace($bundle->getNamespace());
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata of a class.
|
||||
*
|
||||
* @param string $class A class name
|
||||
* @param string $path The path where the class is stored (if known)
|
||||
*
|
||||
* @return ClassMetadataCollection A ClassMetadataCollection instance
|
||||
*
|
||||
* @throws MappingException When class is not valid entity or mapped superclass.
|
||||
*/
|
||||
public function getClassMetadata($class, $path = null)
|
||||
{
|
||||
$metadata = $this->getMetadataForClass($class);
|
||||
if (! $metadata->getMetadata()) {
|
||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($class);
|
||||
}
|
||||
|
||||
$this->findNamespaceAndPathForMetadata($metadata, $path);
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata of all classes of a namespace.
|
||||
*
|
||||
* @param string $namespace A namespace name
|
||||
* @param string $path The path where the class is stored (if known)
|
||||
*
|
||||
* @return ClassMetadataCollection A ClassMetadataCollection instance
|
||||
*
|
||||
* @throws RuntimeException When namespace not contain mapped entities.
|
||||
*/
|
||||
public function getNamespaceMetadata($namespace, $path = null)
|
||||
{
|
||||
$metadata = $this->getMetadataForNamespace($namespace);
|
||||
if (! $metadata->getMetadata()) {
|
||||
throw new RuntimeException(sprintf('Namespace "%s" does not contain any mapped entities.', $namespace));
|
||||
}
|
||||
|
||||
$this->findNamespaceAndPathForMetadata($metadata, $path);
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and configure path and namespace for the metadata collection.
|
||||
*
|
||||
* @param string|null $path
|
||||
*
|
||||
* @throws RuntimeException When unable to determine the path.
|
||||
*/
|
||||
public function findNamespaceAndPathForMetadata(ClassMetadataCollection $metadata, $path = null)
|
||||
{
|
||||
$r = new ReflectionClass($metadata->getMetadata()[0]->name);
|
||||
$metadata->setPath($this->getBasePathForClass($r->getName(), $r->getNamespaceName(), dirname($r->getFilename())));
|
||||
$metadata->setNamespace($r->getNamespaceName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a base path for a class
|
||||
*
|
||||
* @throws RuntimeException When base path not found.
|
||||
*/
|
||||
private function getBasePathForClass(string $name, string $namespace, string $path): string
|
||||
{
|
||||
$namespace = str_replace('\\', '/', $namespace);
|
||||
$search = str_replace('\\', '/', $path);
|
||||
$destination = str_replace('/' . $namespace, '', $search, $c);
|
||||
|
||||
if ($c !== 1) {
|
||||
throw new RuntimeException(sprintf('Can\'t find base path for "%s" (path: "%s", destination: "%s").', $name, $path, $destination));
|
||||
}
|
||||
|
||||
return $destination;
|
||||
}
|
||||
|
||||
private function getMetadataForNamespace(string $namespace): ClassMetadataCollection
|
||||
{
|
||||
$metadata = [];
|
||||
foreach ($this->getAllMetadata() as $m) {
|
||||
if (strpos($m->name, $namespace) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadata[] = $m;
|
||||
}
|
||||
|
||||
return new ClassMetadataCollection($metadata);
|
||||
}
|
||||
|
||||
private function getMetadataForClass(string $entity): ClassMetadataCollection
|
||||
{
|
||||
foreach ($this->registry->getManagers() as $em) {
|
||||
$cmf = new DisconnectedClassMetadataFactory();
|
||||
$cmf->setEntityManager($em);
|
||||
|
||||
if (! $cmf->isTransient($entity)) {
|
||||
return new ClassMetadataCollection([$cmf->getMetadataFor($entity)]);
|
||||
}
|
||||
}
|
||||
|
||||
return new ClassMetadataCollection([]);
|
||||
}
|
||||
|
||||
/** @return ClassMetadata[] */
|
||||
private function getAllMetadata(): array
|
||||
{
|
||||
$metadata = [];
|
||||
foreach ($this->registry->getManagers() as $em) {
|
||||
$cmf = new DisconnectedClassMetadataFactory();
|
||||
$cmf->setEntityManager($em);
|
||||
foreach ($cmf->getAllMetadata() as $m) {
|
||||
$metadata[] = $m;
|
||||
}
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
|
||||
|
||||
use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
|
||||
interface EntityListenerServiceResolver extends EntityListenerResolver
|
||||
{
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $serviceId
|
||||
*/
|
||||
// phpcs:ignore
|
||||
public function registerService($className, $serviceId);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Mapping;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata as OrmClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver as MappingDriverInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class MappingDriver implements MappingDriverInterface
|
||||
{
|
||||
private MappingDriverInterface $driver;
|
||||
private ContainerInterface $idGeneratorLocator;
|
||||
|
||||
public function __construct(MappingDriverInterface $driver, ContainerInterface $idGeneratorLocator)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$this->idGeneratorLocator = $idGeneratorLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAllClassNames()
|
||||
{
|
||||
return $this->driver->getAllClassNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isTransient($className): bool
|
||||
{
|
||||
return $this->driver->isTransient($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function loadMetadataForClass($className, ClassMetadata $metadata): void
|
||||
{
|
||||
$this->driver->loadMetadataForClass($className, $metadata);
|
||||
|
||||
if (
|
||||
! $metadata instanceof OrmClassMetadata
|
||||
|| $metadata->generatorType !== OrmClassMetadata::GENERATOR_TYPE_CUSTOM
|
||||
|| ! isset($metadata->customGeneratorDefinition['class'])
|
||||
|| ! $this->idGeneratorLocator->has($metadata->customGeneratorDefinition['class'])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$idGenerator = $this->idGeneratorLocator->get($metadata->customGeneratorDefinition['class']);
|
||||
$metadata->setCustomGeneratorDefinition(['instance' => $idGenerator] + $metadata->customGeneratorDefinition);
|
||||
$metadata->setIdGeneratorType(OrmClassMetadata::GENERATOR_TYPE_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inner driver
|
||||
*/
|
||||
public function getDriver(): MappingDriverInterface
|
||||
{
|
||||
return $this->driver;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
|
||||
use Symfony\Bridge\Doctrine\Middleware\Debug\Query;
|
||||
|
||||
use function array_slice;
|
||||
use function debug_backtrace;
|
||||
use function in_array;
|
||||
|
||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
|
||||
/** @psalm-suppress MissingDependency */
|
||||
class BacktraceDebugDataHolder extends DebugDataHolder
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $connWithBacktraces;
|
||||
|
||||
/** @var array<string, array<int|string, mixed>[]> */
|
||||
private array $backtraces = [];
|
||||
|
||||
/** @param string[] $connWithBacktraces */
|
||||
public function __construct(array $connWithBacktraces)
|
||||
{
|
||||
$this->connWithBacktraces = $connWithBacktraces;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->backtraces = [];
|
||||
}
|
||||
|
||||
public function addQuery(string $connectionName, Query $query): void
|
||||
{
|
||||
parent::addQuery($connectionName, $query);
|
||||
|
||||
if (! in_array($connectionName, $this->connWithBacktraces, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// array_slice to skip middleware calls in the trace
|
||||
$this->backtraces[$connectionName][] = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 2);
|
||||
}
|
||||
|
||||
/** @return array<string, array<string, mixed>[]> */
|
||||
public function getData(): array
|
||||
{
|
||||
$dataWithBacktraces = [];
|
||||
|
||||
$data = parent::getData();
|
||||
foreach ($data as $connectionName => $dataForConn) {
|
||||
$dataWithBacktraces[$connectionName] = $this->getDataForConnection($connectionName, $dataForConn);
|
||||
}
|
||||
|
||||
return $dataWithBacktraces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[][] $dataForConn
|
||||
*
|
||||
* @return mixed[][]
|
||||
*/
|
||||
private function getDataForConnection(string $connectionName, array $dataForConn): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($dataForConn as $idx => $record) {
|
||||
$data[] = $this->addBacktracesIfAvailable($connectionName, $record, $idx);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $record
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function addBacktracesIfAvailable(string $connectionName, array $record, int $idx): array
|
||||
{
|
||||
if (! isset($this->backtraces[$connectionName])) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
$record['backtrace'] = $this->backtraces[$connectionName][$idx];
|
||||
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
|
||||
|
||||
interface ConnectionNameAwareInterface
|
||||
{
|
||||
public function setConnectionName(string $name): void;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
|
||||
|
||||
use Doctrine\DBAL\Driver as DriverInterface;
|
||||
use Doctrine\DBAL\Driver\Middleware;
|
||||
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
|
||||
use Symfony\Bridge\Doctrine\Middleware\Debug\Driver;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
|
||||
class DebugMiddleware implements Middleware, ConnectionNameAwareInterface
|
||||
{
|
||||
private DebugDataHolder $debugDataHolder;
|
||||
private ?Stopwatch $stopwatch;
|
||||
private string $connectionName = 'default';
|
||||
|
||||
public function __construct(DebugDataHolder $debugDataHolder, ?Stopwatch $stopwatch)
|
||||
{
|
||||
$this->debugDataHolder = $debugDataHolder;
|
||||
$this->stopwatch = $stopwatch;
|
||||
}
|
||||
|
||||
public function setConnectionName(string $name): void
|
||||
{
|
||||
$this->connectionName = $name;
|
||||
}
|
||||
|
||||
public function wrap(DriverInterface $driver): DriverInterface
|
||||
{
|
||||
/** @psalm-suppress InternalClass,InternalMethod */
|
||||
return new Driver($driver, $this->debugDataHolder, $this->stopwatch, $this->connectionName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
|
||||
|
||||
use ArrayObject;
|
||||
use Doctrine\DBAL\Driver;
|
||||
use Doctrine\DBAL\Driver\Middleware;
|
||||
use Symfony\Bridge\Doctrine\Middleware\IdleConnection\Driver as IdleConnectionDriver;
|
||||
|
||||
class IdleConnectionMiddleware implements Middleware, ConnectionNameAwareInterface
|
||||
{
|
||||
private ArrayObject $connectionExpiries;
|
||||
/** @var array<string, int> */
|
||||
private array $ttlByConnection;
|
||||
private string $connectionName;
|
||||
|
||||
/**
|
||||
* @param ArrayObject<string, int> $connectionExpiries
|
||||
* @param array<string, int> $ttlByConnection
|
||||
*/
|
||||
public function __construct(ArrayObject $connectionExpiries, array $ttlByConnection)
|
||||
{
|
||||
$this->connectionExpiries = $connectionExpiries;
|
||||
$this->ttlByConnection = $ttlByConnection;
|
||||
}
|
||||
|
||||
public function setConnectionName(string $name): void
|
||||
{
|
||||
$this->connectionName = $name;
|
||||
}
|
||||
|
||||
public function wrap(Driver $driver): IdleConnectionDriver
|
||||
{
|
||||
return new IdleConnectionDriver($driver, $this->connectionExpiries, $this->ttlByConnection[$this->connectionName], $this->connectionName);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Orm;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Tools\Console\EntityManagerProvider;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use RuntimeException;
|
||||
|
||||
use function get_class;
|
||||
use function sprintf;
|
||||
|
||||
final class ManagerRegistryAwareEntityManagerProvider implements EntityManagerProvider
|
||||
{
|
||||
private ManagerRegistry $managerRegistry;
|
||||
|
||||
public function __construct(ManagerRegistry $managerRegistry)
|
||||
{
|
||||
$this->managerRegistry = $managerRegistry;
|
||||
}
|
||||
|
||||
public function getDefaultManager(): EntityManagerInterface
|
||||
{
|
||||
return $this->getManager($this->managerRegistry->getDefaultManagerName());
|
||||
}
|
||||
|
||||
public function getManager(string $name): EntityManagerInterface
|
||||
{
|
||||
$em = $this->managerRegistry->getManager($name);
|
||||
|
||||
if ($em instanceof EntityManagerInterface) {
|
||||
return $em;
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
'Only managers of type "%s" are supported. Instance of "%s given.',
|
||||
EntityManagerInterface::class,
|
||||
get_class($em),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\ORMException;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
use ProxyManager\Proxy\LazyLoadingInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Bridge\Doctrine\ManagerRegistry;
|
||||
use Symfony\Component\VarExporter\LazyObjectInterface;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
use function array_keys;
|
||||
use function assert;
|
||||
|
||||
/**
|
||||
* References all Doctrine connections and entity managers in a given Container.
|
||||
*/
|
||||
class Registry extends ManagerRegistry implements ResetInterface
|
||||
{
|
||||
/**
|
||||
* @param string[] $connections
|
||||
* @param string[] $entityManagers
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, array $connections, array $entityManagers, string $defaultConnection, string $defaultEntityManager)
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
parent::__construct('ORM', $connections, $entityManagers, $defaultConnection, $defaultEntityManager, Proxy::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a registered namespace alias to the full namespace.
|
||||
*
|
||||
* This method looks for the alias in all registered entity managers.
|
||||
*
|
||||
* @see Configuration::getEntityNamespace
|
||||
*
|
||||
* @param string $alias The alias
|
||||
*
|
||||
* @return string The full namespace
|
||||
*/
|
||||
public function getAliasNamespace($alias)
|
||||
{
|
||||
foreach (array_keys($this->getManagers()) as $name) {
|
||||
$objectManager = $this->getManager($name);
|
||||
|
||||
if (! $objectManager instanceof EntityManagerInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
/** @psalm-suppress UndefinedMethod ORM < 3 specific */
|
||||
return $objectManager->getConfiguration()->getEntityNamespace($alias);
|
||||
} catch (ORMException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
throw ORMException::unknownEntityNamespace($alias);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
foreach ($this->getManagerNames() as $managerName => $serviceId) {
|
||||
$this->resetOrClearManager($managerName, $serviceId);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetOrClearManager(string $managerName, string $serviceId): void
|
||||
{
|
||||
if (! $this->container->initialized($serviceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$manager = $this->container->get($serviceId);
|
||||
|
||||
assert($manager instanceof EntityManagerInterface);
|
||||
|
||||
if ((! $manager instanceof LazyLoadingInterface && ! $manager instanceof LazyObjectInterface) || $manager->isOpen()) {
|
||||
$manager->clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->resetManager($managerName);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Repository;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
use function class_exists;
|
||||
use function get_debug_type;
|
||||
use function is_a;
|
||||
use function spl_object_hash;
|
||||
use function sprintf;
|
||||
use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* Fetches repositories from the container or falls back to normal creation.
|
||||
*/
|
||||
final class ContainerRepositoryFactory implements RepositoryFactory
|
||||
{
|
||||
use RepositoryFactoryCompatibility;
|
||||
|
||||
/** @var array<string, ObjectRepository> */
|
||||
private array $managedRepositories = [];
|
||||
|
||||
private ContainerInterface $container;
|
||||
|
||||
/** @param ContainerInterface $container A service locator containing the repositories */
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string<T> $entityName
|
||||
*
|
||||
* @return ObjectRepository<T>
|
||||
* @psalm-return ($strictTypeCheck is true ? EntityRepository<T> : ObjectRepository<T>)
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
private function doGetRepository(EntityManagerInterface $entityManager, string $entityName, bool $strictTypeCheck): ObjectRepository
|
||||
{
|
||||
$metadata = $entityManager->getClassMetadata($entityName);
|
||||
$repositoryServiceId = $metadata->customRepositoryClassName;
|
||||
|
||||
$customRepositoryName = $metadata->customRepositoryClassName;
|
||||
if ($customRepositoryName !== null) {
|
||||
// fetch from the container
|
||||
if ($this->container->has($customRepositoryName)) {
|
||||
$repository = $this->container->get($customRepositoryName);
|
||||
|
||||
if (! $repository instanceof EntityRepository && $strictTypeCheck) {
|
||||
throw new RuntimeException(sprintf('The service "%s" must extend EntityRepository (e.g. by extending ServiceEntityRepository), "%s" given.', $repositoryServiceId, get_debug_type($repository)));
|
||||
}
|
||||
|
||||
if (! $repository instanceof ObjectRepository) {
|
||||
throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository), "%s" given.', $repositoryServiceId, get_debug_type($repository)));
|
||||
}
|
||||
|
||||
if (! $repository instanceof EntityRepository) {
|
||||
trigger_deprecation('doctrine/doctrine-bundle', '2.11', 'The service "%s" of type "%s" should extend "%s", not doing so is deprecated.', $repositoryServiceId, get_debug_type($repository), EntityRepository::class);
|
||||
}
|
||||
|
||||
/** @psalm-var ObjectRepository<T> */
|
||||
return $repository;
|
||||
}
|
||||
|
||||
// if not in the container but the class/id implements the interface, throw an error
|
||||
if (is_a($customRepositoryName, ServiceEntityRepositoryInterface::class, true)) {
|
||||
throw new RuntimeException(sprintf('The "%s" entity repository implements "%s", but its service could not be found. Make sure the service exists and is tagged with "%s".', $customRepositoryName, ServiceEntityRepositoryInterface::class, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
|
||||
}
|
||||
|
||||
if (! class_exists($customRepositoryName)) {
|
||||
throw new RuntimeException(sprintf('The "%s" entity has a repositoryClass set to "%s", but this is not a valid class. Check your class naming. If this is meant to be a service id, make sure this service exists and is tagged with "%s".', $metadata->name, $customRepositoryName, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
|
||||
}
|
||||
|
||||
// allow the repository to be created below
|
||||
}
|
||||
|
||||
return $this->getOrCreateRepository($entityManager, $metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMetadata<TEntity> $metadata
|
||||
*
|
||||
* @return ObjectRepository<TEntity>
|
||||
*
|
||||
* @template TEntity of object
|
||||
*/
|
||||
private function getOrCreateRepository(
|
||||
EntityManagerInterface $entityManager,
|
||||
ClassMetadata $metadata
|
||||
): ObjectRepository {
|
||||
$repositoryHash = $metadata->getName() . spl_object_hash($entityManager);
|
||||
if (isset($this->managedRepositories[$repositoryHash])) {
|
||||
/** @psalm-var ObjectRepository<TEntity> */
|
||||
return $this->managedRepositories[$repositoryHash];
|
||||
}
|
||||
|
||||
$repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
|
||||
|
||||
/** @psalm-var ObjectRepository<TEntity> */
|
||||
return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($entityManager, $metadata);
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use LogicException;
|
||||
use Symfony\Component\VarExporter\LazyObjectInterface;
|
||||
|
||||
use function debug_backtrace;
|
||||
use function sprintf;
|
||||
|
||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
|
||||
/**
|
||||
* @internal Extend {@see ServiceEntityRepository} instead.
|
||||
*
|
||||
* @template T of object
|
||||
* @template-extends EntityRepository<T>
|
||||
*/
|
||||
class LazyServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
|
||||
{
|
||||
private ManagerRegistry $registry;
|
||||
private string $entityClass;
|
||||
|
||||
/**
|
||||
* @param string $entityClass The class name of the entity this repository manages
|
||||
* @psalm-param class-string<T> $entityClass
|
||||
*/
|
||||
public function __construct(ManagerRegistry $registry, string $entityClass)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->entityClass = $entityClass;
|
||||
|
||||
if ($this instanceof LazyObjectInterface) {
|
||||
$this->initialize();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->_em);
|
||||
unset($this->_class);
|
||||
unset($this->_entityName);
|
||||
}
|
||||
|
||||
/** @return mixed */
|
||||
public function __get(string $name)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$scope = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['class'] ?? null;
|
||||
|
||||
return (function () use ($name) {
|
||||
return $this->$name;
|
||||
})->bindTo($this, $scope)();
|
||||
}
|
||||
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$scope = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['class'] ?? null;
|
||||
|
||||
return (function () use ($name) {
|
||||
return isset($this->$name);
|
||||
})->bindTo($this, $scope)();
|
||||
}
|
||||
|
||||
private function initialize(): void
|
||||
{
|
||||
$manager = $this->registry->getManagerForClass($this->entityClass);
|
||||
|
||||
if ($manager === null) {
|
||||
throw new LogicException(sprintf(
|
||||
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
|
||||
$this->entityClass,
|
||||
));
|
||||
}
|
||||
|
||||
parent::__construct($manager, $manager->getClassMetadata($this->entityClass));
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use ReflectionMethod;
|
||||
|
||||
if ((new ReflectionMethod(RepositoryFactory::class, 'getRepository'))->hasReturnType()) {
|
||||
// ORM >= 3
|
||||
/** @internal */
|
||||
trait RepositoryFactoryCompatibility
|
||||
{
|
||||
/**
|
||||
* Gets the repository for an entity class.
|
||||
*
|
||||
* @param class-string<T> $entityName
|
||||
*
|
||||
* @return EntityRepository<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository
|
||||
{
|
||||
return $this->doGetRepository($entityManager, $entityName, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ORM 2
|
||||
/** @internal */
|
||||
trait RepositoryFactoryCompatibility
|
||||
{
|
||||
/** {@inheritDoc} */
|
||||
public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository
|
||||
{
|
||||
return $this->doGetRepository($entityManager, $entityName, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
use function property_exists;
|
||||
|
||||
if (property_exists(EntityRepository::class, '_entityName')) {
|
||||
// ORM 2
|
||||
/**
|
||||
* Optional EntityRepository base class with a simplified constructor (for autowiring).
|
||||
*
|
||||
* To use in your class, inject the "registry" service and call
|
||||
* the parent constructor. For example:
|
||||
*
|
||||
* class YourEntityRepository extends ServiceEntityRepository
|
||||
* {
|
||||
* public function __construct(ManagerRegistry $registry)
|
||||
* {
|
||||
* parent::__construct($registry, YourEntity::class);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @template T of object
|
||||
* @template-extends LazyServiceEntityRepository<T>
|
||||
*/
|
||||
class ServiceEntityRepository extends LazyServiceEntityRepository
|
||||
{
|
||||
}
|
||||
} else {
|
||||
// ORM 3
|
||||
/**
|
||||
* Optional EntityRepository base class with a simplified constructor (for autowiring).
|
||||
*
|
||||
* To use in your class, inject the "registry" service and call
|
||||
* the parent constructor. For example:
|
||||
*
|
||||
* class YourEntityRepository extends ServiceEntityRepository
|
||||
* {
|
||||
* public function __construct(ManagerRegistry $registry)
|
||||
* {
|
||||
* parent::__construct($registry, YourEntity::class);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @template T of object
|
||||
* @template-extends ServiceEntityRepositoryProxy<T>
|
||||
*/
|
||||
class ServiceEntityRepository extends ServiceEntityRepositoryProxy
|
||||
{
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Repository;
|
||||
|
||||
/**
|
||||
* This interface signals that your repository should be loaded from the container.
|
||||
*/
|
||||
interface ServiceEntityRepositoryInterface
|
||||
{
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Repository;
|
||||
|
||||
use Doctrine\Common\Collections\AbstractLazyCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use LogicException;
|
||||
use Symfony\Component\VarExporter\LazyObjectInterface;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @internal Extend {@see ServiceEntityRepository} instead.
|
||||
*
|
||||
* @template T of object
|
||||
* @template-extends EntityRepository<T>
|
||||
*/
|
||||
class ServiceEntityRepositoryProxy extends EntityRepository implements ServiceEntityRepositoryInterface
|
||||
{
|
||||
private ?EntityRepository $repository = null;
|
||||
|
||||
/** @param class-string<T> $entityClass The class name of the entity this repository manages */
|
||||
public function __construct(
|
||||
private readonly ManagerRegistry $registry,
|
||||
private readonly string $entityClass,
|
||||
) {
|
||||
if (! $this instanceof LazyObjectInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->repository = $this->resolveRepository();
|
||||
}
|
||||
|
||||
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->createQueryBuilder($alias, $indexBy);
|
||||
}
|
||||
|
||||
public function createResultSetMappingBuilder(string $alias): ResultSetMappingBuilder
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->createResultSetMappingBuilder($alias);
|
||||
}
|
||||
|
||||
public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
/** @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class */
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->find($id, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class
|
||||
* @psalm-suppress InvalidReturnType This proxy is used only in combination with newer parent class
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null): object|null
|
||||
{
|
||||
/** @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class */
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->findOneBy($criteria, $orderBy);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function count(array $criteria = []): int
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())->count($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function __call(string $method, array $arguments): mixed
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())->$method(...$arguments);
|
||||
}
|
||||
|
||||
protected function getEntityName(): string
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())->getEntityName();
|
||||
}
|
||||
|
||||
protected function getEntityManager(): EntityManagerInterface
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())->getEntityManager();
|
||||
}
|
||||
|
||||
/** @psalm-suppress InvalidReturnType This proxy is used only in combination with newer parent class */
|
||||
protected function getClassMetadata(): ClassMetadata
|
||||
{
|
||||
/** @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class */
|
||||
return ($this->repository ??= $this->resolveRepository())->getClassMetadata();
|
||||
}
|
||||
|
||||
public function matching(Criteria $criteria): AbstractLazyCollection&Selectable
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())->matching($criteria);
|
||||
}
|
||||
|
||||
private function resolveRepository(): EntityRepository
|
||||
{
|
||||
$manager = $this->registry->getManagerForClass($this->entityClass);
|
||||
|
||||
if ($manager === null) {
|
||||
throw new LogicException(sprintf(
|
||||
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
|
||||
$this->entityClass,
|
||||
));
|
||||
}
|
||||
|
||||
return new EntityRepository($manager, $manager->getClassMetadata($this->entityClass));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Twig;
|
||||
|
||||
use Doctrine\SqlFormatter\HtmlHighlighter;
|
||||
use Doctrine\SqlFormatter\NullHighlighter;
|
||||
use Doctrine\SqlFormatter\SqlFormatter;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
use function addslashes;
|
||||
use function array_key_exists;
|
||||
use function bin2hex;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function method_exists;
|
||||
use function preg_match;
|
||||
use function preg_replace_callback;
|
||||
use function sprintf;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* This class contains the needed functions in order to do the query highlighting
|
||||
*
|
||||
* @internal since 2.11
|
||||
*/
|
||||
class DoctrineExtension extends AbstractExtension
|
||||
{
|
||||
private SqlFormatter $sqlFormatter;
|
||||
|
||||
/**
|
||||
* Define our functions
|
||||
*
|
||||
* @return TwigFilter[]
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('doctrine_pretty_query', [$this, 'formatQuery'], ['is_safe' => ['html'], 'deprecated' => true]),
|
||||
new TwigFilter('doctrine_prettify_sql', [$this, 'prettifySql'], ['is_safe' => ['html']]),
|
||||
new TwigFilter('doctrine_format_sql', [$this, 'formatSql'], ['is_safe' => ['html']]),
|
||||
new TwigFilter('doctrine_replace_query_parameters', [$this, 'replaceQueryParameters']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape parameters of a SQL query
|
||||
* DON'T USE THIS FUNCTION OUTSIDE ITS INTENDED SCOPE
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param mixed $parameter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function escapeFunction($parameter)
|
||||
{
|
||||
$result = $parameter;
|
||||
|
||||
switch (true) {
|
||||
// Check if result is non-unicode string using PCRE_UTF8 modifier
|
||||
case is_string($result) && ! preg_match('//u', $result):
|
||||
$result = '0x' . strtoupper(bin2hex($result));
|
||||
break;
|
||||
|
||||
case is_string($result):
|
||||
$result = "'" . addslashes($result) . "'";
|
||||
break;
|
||||
|
||||
case is_array($result):
|
||||
foreach ($result as &$value) {
|
||||
$value = static::escapeFunction($value);
|
||||
}
|
||||
|
||||
$result = implode(', ', $result) ?: 'NULL';
|
||||
break;
|
||||
|
||||
case is_object($result) && method_exists($result, '__toString'):
|
||||
$result = addslashes($result->__toString());
|
||||
break;
|
||||
|
||||
case $result === null:
|
||||
$result = 'NULL';
|
||||
break;
|
||||
|
||||
case is_bool($result):
|
||||
$result = $result ? '1' : '0';
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a query with the parameters replaced
|
||||
*
|
||||
* @param string $query
|
||||
* @param mixed[]|Data $parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function replaceQueryParameters($query, $parameters)
|
||||
{
|
||||
if ($parameters instanceof Data) {
|
||||
$parameters = $parameters->getValue(true);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
|
||||
if (! array_key_exists(0, $parameters) && array_key_exists(1, $parameters)) {
|
||||
$i = 1;
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'/\?|((?<!:):[a-z0-9_]+)/i',
|
||||
static function ($matches) use ($parameters, &$i) {
|
||||
$key = substr($matches[0], 1);
|
||||
|
||||
if (! array_key_exists($i, $parameters) && ($key === false || ! array_key_exists($key, $parameters))) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$value = array_key_exists($i, $parameters) ? $parameters[$i] : $parameters[$key];
|
||||
$result = DoctrineExtension::escapeFunction($value);
|
||||
$i++;
|
||||
|
||||
return $result;
|
||||
},
|
||||
$query,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and/or highlights the given SQL statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param bool $highlightOnly If true the query is not formatted, just highlighted
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatQuery($sql, $highlightOnly = false)
|
||||
{
|
||||
trigger_deprecation(
|
||||
'doctrine/doctrine-bundle',
|
||||
'2.1',
|
||||
'The "%s()" method is deprecated and will be removed in doctrine-bundle 3.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
|
||||
$this->setUpSqlFormatter(true, true);
|
||||
|
||||
if ($highlightOnly) {
|
||||
return $this->sqlFormatter->highlight($sql);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<div class="highlight highlight-sql"><pre>%s</pre></div>',
|
||||
$this->sqlFormatter->format($sql),
|
||||
);
|
||||
}
|
||||
|
||||
public function prettifySql(string $sql): string
|
||||
{
|
||||
$this->setUpSqlFormatter();
|
||||
|
||||
return $this->sqlFormatter->highlight($sql);
|
||||
}
|
||||
|
||||
public function formatSql(string $sql, bool $highlight): string
|
||||
{
|
||||
$this->setUpSqlFormatter($highlight);
|
||||
|
||||
return $this->sqlFormatter->format($sql);
|
||||
}
|
||||
|
||||
private function setUpSqlFormatter(bool $highlight = true, bool $legacy = false): void
|
||||
{
|
||||
$this->sqlFormatter = new SqlFormatter($highlight ? new HtmlHighlighter([
|
||||
HtmlHighlighter::HIGHLIGHT_PRE => 'class="highlight highlight-sql"',
|
||||
HtmlHighlighter::HIGHLIGHT_QUOTE => 'class="string"',
|
||||
HtmlHighlighter::HIGHLIGHT_BACKTICK_QUOTE => 'class="string"',
|
||||
HtmlHighlighter::HIGHLIGHT_RESERVED => 'class="keyword"',
|
||||
HtmlHighlighter::HIGHLIGHT_BOUNDARY => 'class="symbol"',
|
||||
HtmlHighlighter::HIGHLIGHT_NUMBER => 'class="number"',
|
||||
HtmlHighlighter::HIGHLIGHT_WORD => 'class="word"',
|
||||
HtmlHighlighter::HIGHLIGHT_ERROR => 'class="error"',
|
||||
HtmlHighlighter::HIGHLIGHT_COMMENT => 'class="comment"',
|
||||
HtmlHighlighter::HIGHLIGHT_VARIABLE => 'class="variable"',
|
||||
], ! $legacy) : new NullHighlighter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-database" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<ellipse cx="12" cy="6" rx="8" ry="3"></ellipse>
|
||||
<path d="M4 6v6a8 3 0 0 0 16 0v-6"></path>
|
||||
<path d="M4 12v6a8 3 0 0 0 16 0v-6"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 439 B |
@@ -0,0 +1,533 @@
|
||||
{% extends request.isXmlHttpRequest ? '@WebProfiler/Profiler/ajax_layout.html.twig' : '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.querycount > 0 or collector.invalidEntityCount > 0 %}
|
||||
|
||||
{% set icon %}
|
||||
{% set status = collector.invalidEntityCount > 0 ? 'red' : collector.querycount > 50 ? 'yellow' %}
|
||||
|
||||
{% if profiler_markup_version >= 3 %}
|
||||
{{ include('@Doctrine/Collector/database.svg') }}
|
||||
{% else %}
|
||||
<span class="icon">{{ include('@Doctrine/Collector/icon.svg') }}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if collector.querycount == 0 and collector.invalidEntityCount > 0 %}
|
||||
<span class="sf-toolbar-value">{{ collector.invalidEntityCount }}</span>
|
||||
<span class="sf-toolbar-label">errors</span>
|
||||
{% else %}
|
||||
<span class="sf-toolbar-value">{{ collector.querycount }}</span>
|
||||
<span class="sf-toolbar-info-piece-additional-detail">
|
||||
<span class="sf-toolbar-label">in</span>
|
||||
<span class="sf-toolbar-value">{{ '%0.2f'|format(collector.time * 1000) }}</span>
|
||||
<span class="sf-toolbar-label">ms</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Database Queries</b>
|
||||
<span class="sf-toolbar-status {{ collector.querycount > 50 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.querycount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Different statements</b>
|
||||
<span class="sf-toolbar-status">{{ collector.groupedQueryCount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Query time</b>
|
||||
<span>{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Invalid entities</b>
|
||||
<span class="sf-toolbar-status {{ collector.invalidEntityCount > 0 ? 'sf-toolbar-status-red' : '' }}">{{ collector.invalidEntityCount }}</span>
|
||||
</div>
|
||||
{% if collector.cacheEnabled %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Cache hits</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.cacheHitsCount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Cache misses</b>
|
||||
<span class="sf-toolbar-status {{ collector.cacheMissesCount > 0 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.cacheMissesCount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Cache puts</b>
|
||||
<span class="sf-toolbar-status {{ collector.cachePutsCount > 0 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.cachePutsCount }}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Second Level Cache</b>
|
||||
<span class="sf-toolbar-status">disabled</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status|default('') }) }}
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ collector.invalidEntityCount > 0 ? 'label-status-error' }} {{ collector.querycount == 0 ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@Doctrine/Collector/' ~ (profiler_markup_version < 3 ? 'icon' : 'database') ~ '.svg') }}</span>
|
||||
<strong>Doctrine</strong>
|
||||
{% if collector.invalidEntityCount %}
|
||||
<span class="count">
|
||||
<span>{{ collector.invalidEntityCount }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{% if 'explain' == page %}
|
||||
{{ render(controller('Doctrine\\Bundle\\DoctrineBundle\\Controller\\ProfilerController::explainAction', {
|
||||
token: token,
|
||||
panel: 'db',
|
||||
connectionName: request.query.get('connection'),
|
||||
query: request.query.get('query')
|
||||
})) }}
|
||||
{% else %}
|
||||
{{ block('queries') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block queries %}
|
||||
<style>
|
||||
.time-container { position: relative; }
|
||||
.time-container .nowrap { position: relative; z-index: 1; text-shadow: 0 0 2px #fff; }
|
||||
.time-bar { display: block; position: absolute; top: 0; left: 0; bottom: 0; background: #e0e0e0; }
|
||||
.sql-runnable.sf-toggle-content.sf-toggle-visible { display: flex; flex-direction: column; }
|
||||
.sql-runnable button { align-self: end; }
|
||||
{% if profiler_markup_version >= 3 %}
|
||||
.highlight .keyword { color: var(--highlight-keyword); font-weight: bold; }
|
||||
.highlight .word { color: var(--color-text); }
|
||||
.highlight .variable { color: var(--highlight-variable); }
|
||||
.highlight .symbol { color: var(--color-text); }
|
||||
.highlight .comment { color: var(--highlight-comment); }
|
||||
.highlight .string { color: var(--highlight-string); }
|
||||
.highlight .number { color: var(--highlight-constant); font-weight: bold; }
|
||||
.highlight .error { color: var(--highlight-error); }
|
||||
{% endif %}
|
||||
</style>
|
||||
|
||||
<h2>Query Metrics</h2>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric-group">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.querycount }}</span>
|
||||
<span class="label">Database Queries</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.groupedQueryCount }}</span>
|
||||
<span class="label">Different statements</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
|
||||
<span class="label">Query time</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.invalidEntityCount }}</span>
|
||||
<span class="label">Invalid entities</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if collector.cacheEnabled %}
|
||||
<div class="metric-group">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.cacheHitsCount }}</span>
|
||||
<span class="label">Cache hits</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.cacheMissesCount }}</span>
|
||||
<span class="label">Cache misses</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.cachePutsCount }}</span>
|
||||
<span class="label">Cache puts</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="sf-tabs" style="margin-top: 20px;">
|
||||
<div class="tab {{ collector.queries is empty ? 'disabled' }}">
|
||||
{% set group_queries = request.query.getBoolean('group') %}
|
||||
<h3 class="tab-title">
|
||||
{% if group_queries %}
|
||||
Grouped Statements
|
||||
{% else %}
|
||||
Queries
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if not collector.queries %}
|
||||
<div class="empty">
|
||||
<p>No executed queries.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if group_queries %}
|
||||
<p><a href="{{ path('_profiler', { panel: 'db', token: token }) }}">Show all queries</a></p>
|
||||
{% else %}
|
||||
<p><a href="{{ path('_profiler', { panel: 'db', token: token, group: true }) }}">Group similar statements</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% for connection, queries in collector.queries %}
|
||||
{% if collector.connections|length > 1 %}
|
||||
<h3>{{ connection }} <small>connection</small></h3>
|
||||
{% endif %}
|
||||
|
||||
{% if queries is empty %}
|
||||
<div class="empty">
|
||||
<p>No database queries were performed.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if group_queries %}
|
||||
{% set queries = collector.groupedQueries[connection] %}
|
||||
{% endif %}
|
||||
<table class="alt queries-table">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if group_queries %}
|
||||
<th class="nowrap" onclick="javascript:sortTable(this, 0, 'queries-{{ loop.index }}')" data-sort-direction="1" style="cursor: pointer;">Time<span class="text-muted">▼</span></th>
|
||||
<th class="nowrap" onclick="javascript:sortTable(this, 1, 'queries-{{ loop.index }}')" style="cursor: pointer;">Count<span></span></th>
|
||||
{% else %}
|
||||
<th class="nowrap" onclick="javascript:sortTable(this, 0, 'queries-{{ loop.index }}')" data-sort-direction="-1" style="cursor: pointer;">#<span class="text-muted">▲</span></th>
|
||||
<th class="nowrap" onclick="javascript:sortTable(this, 1, 'queries-{{ loop.index }}')" style="cursor: pointer;">Time<span></span></th>
|
||||
{% endif %}
|
||||
<th style="width: 100%;">Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="queries-{{ loop.index }}">
|
||||
{% for i, query in queries %}
|
||||
{% set i = group_queries ? query.index : i %}
|
||||
<tr id="queryNo-{{ i }}-{{ loop.parent.loop.index }}">
|
||||
{% if group_queries %}
|
||||
<td class="time-container">
|
||||
<span class="time-bar" style="width:{{ '%0.2f'|format(query.executionPercent) }}%"></span>
|
||||
<span class="nowrap">{{ '%0.2f'|format(query.executionMS * 1000) }} ms<br />({{ '%0.2f'|format(query.executionPercent) }}%)</span>
|
||||
</td>
|
||||
<td class="nowrap">{{ query.count }}</td>
|
||||
{% else %}
|
||||
<td class="nowrap">{{ loop.index }}</td>
|
||||
<td class="nowrap">{{ '%0.2f'|format(query.executionMS * 1000) }} ms</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{{ query.sql|doctrine_prettify_sql }}
|
||||
|
||||
<div>
|
||||
<strong class="font-normal text-small">Parameters</strong>: {{ profiler_dump(query.params, 2) }}
|
||||
</div>
|
||||
|
||||
<div class="text-small font-normal">
|
||||
<a href="#" class="sf-toggle link-inverse" data-toggle-selector="#formatted-query-{{ i }}-{{ loop.parent.loop.index }}" data-toggle-alt-content="Hide formatted query">View formatted query</a>
|
||||
|
||||
{% if query.runnable %}
|
||||
|
||||
<a href="#" class="sf-toggle link-inverse" data-toggle-selector="#original-query-{{ i }}-{{ loop.parent.loop.index }}" data-toggle-alt-content="Hide runnable query">View runnable query</a>
|
||||
{% endif %}
|
||||
|
||||
{% if query.explainable %}
|
||||
|
||||
<a class="link-inverse" href="{{ path('_profiler', { panel: 'db', token: token, page: 'explain', connection: connection, query: i }) }}" onclick="return explain(this);" data-target-id="explain-{{ i }}-{{ loop.parent.loop.index }}">Explain query</a>
|
||||
{% endif %}
|
||||
|
||||
{% if query.backtrace is defined %}
|
||||
|
||||
<a href="#" class="sf-toggle link-inverse" data-toggle-selector="#backtrace-{{ i }}-{{ loop.parent.loop.index }}" data-toggle-alt-content="Hide query backtrace">View query backtrace</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="formatted-query-{{ i }}-{{ loop.parent.loop.index }}" class="sql-runnable hidden">
|
||||
{{ query.sql|doctrine_format_sql(highlight = true) }}
|
||||
<button class="btn btn-sm hidden" data-clipboard-text="{{ query.sql|doctrine_format_sql(highlight = false)|e('html_attr') }}">Copy</button>
|
||||
</div>
|
||||
|
||||
{% if query.runnable %}
|
||||
<div id="original-query-{{ i }}-{{ loop.parent.loop.index }}" class="sql-runnable hidden">
|
||||
{% set runnable_sql = (query.sql ~ ';')|doctrine_replace_query_parameters(query.params) %}
|
||||
{{ runnable_sql|doctrine_prettify_sql }}
|
||||
<button class="btn btn-sm hidden" data-clipboard-text="{{ runnable_sql|e('html_attr') }}">Copy</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if query.explainable %}
|
||||
<div id="explain-{{ i }}-{{ loop.parent.loop.index }}" class="sql-explain"></div>
|
||||
{% endif %}
|
||||
|
||||
{% if query.backtrace is defined %}
|
||||
<div id="backtrace-{{ i }}-{{ loop.parent.loop.index }}" class="hidden">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">File/Call</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for trace in query.backtrace %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>
|
||||
<span class="text-small">
|
||||
{% set line_number = trace.line|default(1) %}
|
||||
{% if trace.file is defined %}
|
||||
<a href="{{ trace.file|file_link(line_number) }}">
|
||||
{% endif %}
|
||||
{{- trace.class|default ~ (trace.class is defined ? trace.type|default('::')) -}}
|
||||
<span class="status-warning">{{ trace.function }}</span>
|
||||
{% if trace.file is defined %}
|
||||
</a>
|
||||
{% endif %}
|
||||
(line {{ line_number }})
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ collector.connections is empty ? 'disabled' }}">
|
||||
<h3 class="tab-title">Database Connections</h3>
|
||||
<div class="tab-content">
|
||||
{% if not collector.connections %}
|
||||
<div class="empty">
|
||||
<p>There are no configured database connections.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ helper.render_simple_table('Name', 'Service', collector.connections) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ collector.managers is empty ? 'disabled' }}">
|
||||
<h3 class="tab-title">Entity Managers</h3>
|
||||
<div class="tab-content">
|
||||
|
||||
{% if not collector.managers %}
|
||||
<div class="empty">
|
||||
<p>There are no configured entity managers.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ helper.render_simple_table('Name', 'Service', collector.managers) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ not collector.cacheEnabled ? 'disabled' }}">
|
||||
<h3 class="tab-title">Second Level Cache</h3>
|
||||
<div class="tab-content">
|
||||
|
||||
{% if not collector.cacheEnabled %}
|
||||
<div class="empty">
|
||||
<p>Second Level Cache is not enabled.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if not collector.cacheCounts %}
|
||||
<div class="empty">
|
||||
<p>Second level cache information is not available.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.cacheCounts.hits }}</span>
|
||||
<span class="label">Hits</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.cacheCounts.misses }}</span>
|
||||
<span class="label">Misses</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.cacheCounts.puts }}</span>
|
||||
<span class="label">Puts</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if collector.cacheRegions.hits %}
|
||||
<h3>Number of cache hits</h3>
|
||||
{{ helper.render_simple_table('Region', 'Hits', collector.cacheRegions.hits) }}
|
||||
{% endif %}
|
||||
|
||||
{% if collector.cacheRegions.misses %}
|
||||
<h3>Number of cache misses</h3>
|
||||
{{ helper.render_simple_table('Region', 'Misses', collector.cacheRegions.misses) }}
|
||||
{% endif %}
|
||||
|
||||
{% if collector.cacheRegions.puts %}
|
||||
<h3>Number of cache puts</h3>
|
||||
{{ helper.render_simple_table('Region', 'Puts', collector.cacheRegions.puts) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ not collector.entities ? 'disabled' }}">
|
||||
<h3 class="tab-title">Entities Mapping</h3>
|
||||
<div class="tab-content">
|
||||
|
||||
{% if not collector.entities %}
|
||||
<div class="empty">
|
||||
<p>No mapped entities.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% for manager, classes in collector.entities %}
|
||||
{% if collector.managers|length > 1 %}
|
||||
<h3>{{ manager }} <small>entity manager</small></h3>
|
||||
{% endif %}
|
||||
|
||||
{% if classes is empty %}
|
||||
<div class="empty">
|
||||
<p>No loaded entities.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Class</th>
|
||||
<th scope="col">Mapping errors</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for class in classes %}
|
||||
{% set contains_errors = collector.mappingErrors[manager] is defined and collector.mappingErrors[manager][class.class] is defined %}
|
||||
<tr class="{{ contains_errors ? 'status-error' }}">
|
||||
<td>
|
||||
<a href="{{ class.file|file_link(class.line) }}">{{ class. class}}</a>
|
||||
</td>
|
||||
<td class="font-normal">
|
||||
{% if contains_errors %}
|
||||
<ul>
|
||||
{% for error in collector.mappingErrors[manager][class.class] %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
No errors.
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
function explain(link) {
|
||||
"use strict";
|
||||
|
||||
var targetId = link.getAttribute('data-target-id');
|
||||
var targetElement = document.getElementById(targetId);
|
||||
|
||||
if (targetElement.style.display != 'block') {
|
||||
if (targetElement.getAttribute('data-sfurl') !== link.href) {
|
||||
fetch(link.href, {
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'}
|
||||
}).then(async function (response) {
|
||||
targetElement.innerHTML = await response.text()
|
||||
targetElement.setAttribute('data-sfurl', link.href)
|
||||
}, function () {
|
||||
targetElement.innerHTML = 'An error occurred while loading the query explanation.';
|
||||
})
|
||||
}
|
||||
|
||||
targetElement.style.display = 'block';
|
||||
link.innerHTML = 'Hide query explanation';
|
||||
} else {
|
||||
targetElement.style.display = 'none';
|
||||
link.innerHTML = 'Explain query';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function sortTable(header, column, targetId) {
|
||||
"use strict";
|
||||
|
||||
var direction = parseInt(header.getAttribute('data-sort-direction')) || 1,
|
||||
items = [],
|
||||
target = document.getElementById(targetId),
|
||||
rows = target.children,
|
||||
headers = header.parentElement.children,
|
||||
i;
|
||||
|
||||
for (i = 0; i < rows.length; ++i) {
|
||||
items.push(rows[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < headers.length; ++i) {
|
||||
headers[i].removeAttribute('data-sort-direction');
|
||||
if (headers[i].children.length > 0) {
|
||||
headers[i].children[0].innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
header.setAttribute('data-sort-direction', (-1*direction).toString());
|
||||
header.children[0].innerHTML = direction > 0 ? '<span class="text-muted">▲</span>' : '<span class="text-muted">▼</span>';
|
||||
|
||||
items.sort(function(a, b) {
|
||||
return direction * (parseFloat(a.children[column].innerHTML) - parseFloat(b.children[column].innerHTML));
|
||||
});
|
||||
|
||||
for (i = 0; i < items.length; ++i) {
|
||||
target.appendChild(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (navigator.clipboard) {
|
||||
document.querySelectorAll('[data-clipboard-text]').forEach(function(button) {
|
||||
button.classList.remove('hidden');
|
||||
button.addEventListener('click', function() {
|
||||
navigator.clipboard.writeText(button.getAttribute('data-clipboard-text'));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
//]]></script>
|
||||
{% endblock %}
|
||||
|
||||
{% macro render_simple_table(label1, label2, data) %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="key">{{ label1 }}</th>
|
||||
<th scope="col">{{ label2 }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in data %}
|
||||
<tr>
|
||||
<th scope="row">{{ key }}</th>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,28 @@
|
||||
{% if data[0]|length > 1 %}
|
||||
{# The platform returns a table for the explanation (e.g. MySQL), display all columns #}
|
||||
<table style="margin: 5px 0;">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for label in data[0]|keys %}
|
||||
<th>{{ label }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in data %}
|
||||
<tr>
|
||||
{% for key, item in row %}
|
||||
<td>{{ item|replace({',': ', '}) }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
{# The Platform returns a single column for a textual explanation (e.g. PostgreSQL), display all lines #}
|
||||
<pre style="margin: 5px 0;">
|
||||
{%- for row in data -%}
|
||||
{{ row|first }}{{ "\n" }}
|
||||
{%- endfor -%}
|
||||
</pre>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path fill="#AAAAAA" d="M5,8h14c1.7,0,3-1.3,3-3s-1.3-3-3-3H5C3.3,2,2,3.3,2,5S3.3,8,5,8z M18,3.6c0.8,0,1.5,0.7,1.5,1.5S18.8,6.6,18,6.6s-1.5-0.7-1.5-1.5S17.2,3.6,18,3.6z M19,9H5c-1.7,0-3,1.3-3,3s1.3,3,3,3h14c1.7,0,3-1.3,3-3S20.7,9,19,9z M18,13.6
|
||||
c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5s1.5,0.7,1.5,1.5S18.8,13.6,18,13.6z M19,16H5c-1.7,0-3,1.3-3,3s1.3,3,3,3h14c1.7,0,3-1.3,3-3S20.7,16,19,16z M18,20.6c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5s1.5,0.7,1.5,1.5S18.8,20.6,18,20.6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 659 B |
Reference in New Issue
Block a user