Как в вопросе вопроса, как я могу настроить префикс таблицы по умолчанию в symfony2?
Лучше всего, если он может быть установлен по умолчанию для всех объектов, но с возможностью переопределения для отдельных.
Только подумав об этом, я хотел бы пролить свет на то, как это сделать.
Symfony 2 & Doctrine 2.1
Примечание. Я использую YML для конфигурации, так что это то, что я покажу.
Откройте ресурсы своего ресурса / config / services.yml
Определите префикс таблицы:
Обязательно измените mybundle и myprefix_
parameters: mybundle.db.table_prefix: myprefix_
Добавить новую услугу:
services: mybundle.tblprefix_subscriber: class: MyBundle\Subscriber\TablePrefixSubscriber arguments: [%mybundle.db.table_prefix%] tags: - { name: doctrine.event_subscriber }
Создать MyBundle \ Subscriber \ TablePrefixSubscriber.php
<?php namespace MyBundle\Subscriber; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; class TablePrefixSubscriber implements \Doctrine\Common\EventSubscriber { protected $prefix = ''; public function __construct($prefix) { $this->prefix = (string) $prefix; } public function getSubscribedEvents() { return array('loadClassMetadata'); } public function loadClassMetadata(LoadClassMetadataEventArgs $args) { $classMetadata = $args->getClassMetadata(); if ($classMetadata->isInheritanceTypeSingleTable() && !$classMetadata->isRootEntity()) { // if we are in an inheritance hierarchy, only apply this once return; } $classMetadata->setTableName($this->prefix . $classMetadata->getTableName()); foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && array_key_exists('name', $classMetadata->associationMappings[$fieldName]['joinTable']) ) { // Check if "joinTable" exists, it can be null if this field is the reverse side of a ManyToMany relationship $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; } } } }
Дополнительный шаг для пользователей postgres: сделать что-то похожее для последовательностей
Это обновление с учетом новых функций, доступных в Doctrine2.
Doctrine2 использует классы NamingStrategy
которые реализуют преобразование из имени класса в имя таблицы или из имени свойства в имя столбца.
DefaultNamingStrategy
просто находит «короткое имя класса» (без его пространства имен), чтобы вывести имя таблицы.
UnderscoreNamingStrategy
делает то же самое, но также уменьшает и «сокращает» «короткое имя класса».
Класс CustomNamingStrategy
может расширить один из указанных выше (по вашему усмотрению) и переопределить методы classToTableName
и joinTableName
чтобы вы могли указать, как должно быть построено имя таблицы (с использованием префикса).
Например, мой класс CustomNamingStrategy
расширяет UnderscoreNamingStrategy
и находит имя пакета на основе соглашений об именах и использует это как префикс для всех таблиц.
Использование вышеприведенного в Symfony2 требует объявления класса CustomNamingStragery
в качестве службы, а затем ссылки на него в вашем конфиге:
doctrine: # ... orm: # ... #naming_strategy: doctrine.orm.naming_strategy.underscore naming_strategy: my_bundle.naming_strategy.prefixed_naming_strategy
Плюсы:
Минусы:
Ответ Simshaun работает нормально, но имеет проблему, когда у вас есть однонаправленное наследование, с ассоциациями над дочерним объектом. Первый if-statement возвращает, когда объект не является rootEntity, в то время как этот объект может все еще иметь ассоциации, которые должны быть префиксом.
Я исправил это, настроив подписчика на следующее:
<?php namespace MyBundle\Subscriber; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Mapping\ClassMetadataInfo; class TablePrefixSubscriber implements EventSubscriber { protected $prefix = ''; /** * Constructor * * @param string $prefix */ public function __construct($prefix) { $this->prefix = (string) $prefix; } /** * Get subscribed events * * @return array */ public function getSubscribedEvents() { return array('loadClassMetadata'); } /** * Load class meta data event * * @param LoadClassMetadataEventArgs $args * * @return void */ public function loadClassMetadata(LoadClassMetadataEventArgs $args) { $classMetadata = $args->getClassMetadata(); // Only add the prefixes to our own entities. if (FALSE !== strpos($classMetadata->namespace, 'Some\Namespace\Part')) { // Do not re-apply the prefix when the table is already prefixed if (false === strpos($classMetadata->getTableName(), $this->prefix)) { $tableName = $this->prefix . $classMetadata->getTableName(); $classMetadata->setPrimaryTable(['name' => $tableName]); } foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { if ($mapping['type'] == ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide'] == true) { $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; // Do not re-apply the prefix when the association is already prefixed if (false !== strpos($mappedTableName, $this->prefix)) { continue; } $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; } } } } }
Однако это имеет недостаток; Неправильно выбранный префикс может вызвать конфликты, когда он фактически уже является частью имени таблицы. Например, используя префикс 'co', когда theres table, называемый «content», приведет к таблице без префикса, поэтому использование подчеркивания типа «co_» уменьшит этот риск.
Ответ @simshaun хорош, но есть проблема с отношениями «Много-ко-многим» и наследованием.
Если у вас есть родительский класс « User
и дочерний класс « Employee
, а « Employee
владеет полем « Много-ко-многим» , то в таблице этого поля не будет префикса. Это из-за:
if ($classMetadata->isInheritanceTypeSingleTable() && !$classMetadata->isRootEntity()) { // if we are in an inheritance hierarchy, only apply this once return; }
namespace FooBundle\Bar\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Entity() * @ORM\Table(name="user") * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="type", type="string") * @ORM\DiscriminatorMap({"user" = "User", "employee" = "\FooBundle\Bar\Entity\Employee"}) */ class User extends User { }
namespace FooBundle\Bar\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Entity() */ class Employee extends FooBundle\Bar\Entity\User { /** * @var ArrayCollection $addresses * * @ORM\ManyToMany(targetEntity="\FooBundle\Bar\Entity\Adress") * @ORM\JoinTable(name="employee_address", * joinColumns={@ORM\JoinColumn(name="employee_id", referencedColumnName="id")}, * inverseJoinColumns={@ORM\JoinColumn(name="address_id", referencedColumnName="id")} * ) */ private $addresses; }
namespace FooBundle\Bar\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Entity() * @ORM\Table(name="address") */ class Address { }
При использовании оригинального решения, если вы примените pref_
pref_ к этому сопоставлению, вы получите таблицы:
pref_user
pref_address
employee_address
Решением может быть изменение в ответе @simshaun точки 4 следующим образом:
Создать MyBundle \ Subscriber \ TablePrefixSubscriber.php
<?php namespace MyBundle\Subscriber; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; class TablePrefixSubscriber implements \Doctrine\Common\EventSubscriber { protected $prefix = ''; public function __construct($prefix) { $this->prefix = (string) $prefix; } public function getSubscribedEvents() { return array('loadClassMetadata'); } public function loadClassMetadata(LoadClassMetadataEventArgs $args) { $classMetadata = $args->getClassMetadata(); // Put the Many-yo-Many verification before the "inheritance" verification. Else fields of the child entity are not taken into account foreach($classMetadata->getAssociationMappings() as $fieldName => $mapping) { if($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && array_key_exists('name', $classMetadata->associationMappings[$fieldName]['joinTable']) // Check if "joinTable" exists, it can be null if this field is the reverse side of a ManyToMany relationship && $mapping['sourceEntity'] == $classMetadata->getName() // If this is not the root entity of an inheritance mapping, but the "child" entity is owning the field, prefix the table. ) { $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; } } if($classMetadata->isInheritanceTypeSingleTable() && !$classMetadata->isRootEntity()) { // if we are in an inheritance hierarchy, only apply this once return; } $classMetadata->setTableName($this->prefix . $classMetadata->getTableName()); } }
Здесь мы обрабатываем отношения Many-to-Many, прежде чем проверять, является ли класс дочерним наследование, и добавляем $mapping['sourceEntity'] == $classMetadata->getName()
чтобы добавить префикс только один раз, на владеющей субъектом поля.
Я не собираюсь реализовывать решение, которое связано с событием ловли (проблема с производительностью), поэтому я попробовал альтернативное решение, но оно не работает для меня. Я добавлял JMSPaymentCoreBundle и хотел добавить префикс в таблицы платежей. В этом пакете определение таблиц содержится в каталоге ресурсов \ config \ doctrine (формат xml). Я наконец нашел это решение:
1) скопируйте каталог доктрины, содержащий определения в таблице, и вставьте его в мой основной пакет
2) измените имя таблиц в определениях, чтобы добавить префикс
3) объявите его в свой config.yml в разделе doctrine / orm / entity manager / mapping (каталог – это каталог, в который были помещены измененные определения):
doctrine: orm: ... entity_managers: default: mappings: ... JMSPaymentCoreBundle: mapping: true type: xml dir: "%kernel.root_dir%/Resources/JMSPayment/doctrine" alias: ~ prefix: JMS\Payment\CoreBundle\Entity is_bundle: false