welcome back to dyb-tech
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
branches:
|
||||
- "2.5.x"
|
||||
- "2.6.x"
|
||||
- "2.7.x"
|
||||
- "2.8.x"
|
||||
- "2.9.x"
|
||||
- "2.10.x"
|
||||
- "2.11.x"
|
||||
- "2.12.x"
|
||||
maintained_branches:
|
||||
- "2.11.x"
|
||||
- "2.12.x"
|
||||
doc_dir: "Resources/doc/"
|
||||
current_branch: "2.11.x"
|
||||
dev_branch: "2.12.x"
|
||||
@@ -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,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command;
|
||||
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
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 method_exists;
|
||||
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 and we have already extracted all relevant info from url
|
||||
unset($params['dbname'], $params['path'], $params['url']);
|
||||
|
||||
$tmpConnection = DriverManager::getConnection($params, $connection->getConfiguration());
|
||||
|
||||
$schemaManager = method_exists($tmpConnection, 'createSchemaManager')
|
||||
? $tmpConnection->createSchemaManager()
|
||||
: $tmpConnection->getSchemaManager();
|
||||
$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,82 @@
|
||||
<?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
|
||||
*/
|
||||
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,127 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Command;
|
||||
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
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 method_exists;
|
||||
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.");
|
||||
}
|
||||
|
||||
unset($params['dbname'], $params['url']);
|
||||
|
||||
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 = method_exists($connection, 'createSchemaManager')
|
||||
? $connection->createSchemaManager()
|
||||
: $connection->getSchemaManager();
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?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
|
||||
*/
|
||||
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,41 @@
|
||||
<?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();
|
||||
$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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?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
|
||||
*/
|
||||
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,285 @@
|
||||
<?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();
|
||||
$platform = $driver->getDatabasePlatform(
|
||||
...(class_exists(StaticServerVersionProvider::class) ? [new StaticServerVersionProvider($params['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
|
||||
{
|
||||
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\Bundle\DoctrineBundle\Registry;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLServerPlatform;
|
||||
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 Registry $registry;
|
||||
private Profiler $profiler;
|
||||
|
||||
public function __construct(Environment $twig, Registry $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,307 @@
|
||||
<?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\Configuration;
|
||||
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();
|
||||
assert($emConfig instanceof Configuration);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
+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);
|
||||
}
|
||||
}
|
||||
+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)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+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, null, $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'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?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 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) {
|
||||
$middlewareDefs = [];
|
||||
$i = 0;
|
||||
foreach ($middlewareAbstractDefs as $id => $abstractDef) {
|
||||
if (isset($middlewareConnections[$id]) && ! array_key_exists($name, $middlewareConnections[$id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$middlewareDefs[$id] = [
|
||||
$childDef = $container->setDefinition(
|
||||
sprintf('%s.%s', $id, $name),
|
||||
new ChildDefinition($id),
|
||||
),
|
||||
++$i,
|
||||
];
|
||||
|
||||
if (! is_subclass_of($abstractDef->getClass(), ConnectionNameAwareInterface::class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childDef->addMethodCall('setConnectionName', [$name]);
|
||||
}
|
||||
|
||||
$middlewareDefs = array_map(
|
||||
static fn ($id, $def) => [
|
||||
$middlewareConnections[$id][$name] ?? $middlewarePriorities[$id] ?? 0,
|
||||
$def[1],
|
||||
$def[0],
|
||||
],
|
||||
array_keys($middlewareDefs),
|
||||
array_values($middlewareDefs),
|
||||
);
|
||||
uasort($middlewareDefs, static fn ($a, $b) => $b[0] <=> $a[0] ?: $a[1] <=> $b[1]);
|
||||
$middlewareDefs = array_map(static fn ($value) => $value[2], $middlewareDefs);
|
||||
|
||||
$container
|
||||
->getDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name))
|
||||
->addMethodCall('setMiddlewares', [$middlewareDefs]);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
+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,875 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
|
||||
|
||||
use Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
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()
|
||||
->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')
|
||||
->defaultTrue()
|
||||
->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('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()
|
||||
->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ORM cache driver node for an 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';
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+171
@@ -0,0 +1,171 @@
|
||||
<?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 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
+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.
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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) {
|
||||
$filterObject = $filterCollection->enable($filter);
|
||||
if ($filterObject === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->setFilterParameters($filter, $filterObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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,191 @@
|
||||
<?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 array_pop;
|
||||
use function class_exists;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function implode;
|
||||
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)
|
||||
{
|
||||
$all = $metadata->getMetadata();
|
||||
if (class_exists($all[0]->name)) {
|
||||
$r = new ReflectionClass($all[0]->name);
|
||||
$path = $this->getBasePathForClass($r->getName(), $r->getNamespaceName(), dirname($r->getFilename()));
|
||||
$ns = $r->getNamespaceName();
|
||||
} elseif ($path) {
|
||||
// Get namespace by removing the last component of the FQCN
|
||||
$nsParts = explode('\\', $all[0]->name);
|
||||
array_pop($nsParts);
|
||||
$ns = implode('\\', $nsParts);
|
||||
} else {
|
||||
throw new RuntimeException(sprintf('Unable to determine where to save the "%s" class (use the --path option).', $all[0]->name));
|
||||
}
|
||||
|
||||
$metadata->setPath($path);
|
||||
$metadata->setNamespace($ns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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<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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
|
||||
|
||||
interface ConnectionNameAwareInterface
|
||||
{
|
||||
public function setConnectionName(string $name): void;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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
|
||||
{
|
||||
return new Driver($driver, $this->debugDataHolder, $this->stopwatch, $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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+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 Resources/docs folder.
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<?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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?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
|
||||
*
|
||||
* @psalm-suppress MethodSignatureMismatch
|
||||
*/
|
||||
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
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?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();
|
||||
}
|
||||
|
||||
/** @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class */
|
||||
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->createQueryBuilder($alias, $indexBy);
|
||||
}
|
||||
|
||||
/** @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class */
|
||||
public function createResultSetMappingBuilder(string $alias): ResultSetMappingBuilder
|
||||
{
|
||||
return ($this->repository ??= $this->resolveRepository())
|
||||
->createResultSetMappingBuilder($alias);
|
||||
}
|
||||
|
||||
/** @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class */
|
||||
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 MethodSignatureMismatch 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}
|
||||
*
|
||||
* @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class
|
||||
*/
|
||||
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,108 @@
|
||||
<?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.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" 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" tag="messenger.receiver" />
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" />
|
||||
<tag name="doctrine.event_listener" event="onSchemaCreateTable" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?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.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>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,277 @@
|
||||
<?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>
|
||||
|
||||
<!-- 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" 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" 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" 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" />
|
||||
|
||||
<!-- 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,284 @@
|
||||
<?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: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="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:schema>
|
||||
@@ -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 |
@@ -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());
|
||||
}
|
||||
}
|
||||
+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.
|
||||
@@ -0,0 +1,8 @@
|
||||
UPGRADE FROM 2.x to 3.0
|
||||
=======================
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
* The `commented` configuration option for types is no longer supported and
|
||||
deprecated.
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"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/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\\": ""
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"": "Tests/DependencyInjection"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user