Как настроить префикс таблицы в symfony2 - PullRequest
19 голосов
/ 21 сентября 2011

Как и в вопросе, как мне установить префикс таблицы по умолчанию в symfony2?

Лучше всего, если он может быть установлен по умолчанию для всех сущностей, но с возможностью переопределения для отдельных.

Ответы [ 5 ]

46 голосов
/ 03 декабря 2011

Только что сам понял это, я хотел бы пролить свет на то, как именно это сделать.

Symfony 2 & Doctrine 2.1
Примечание: я использую YML для конфигурации, вот что я покажу.

Инструкция

  1. Откройте ваш пакет Resources / config / services.yml

  2. Определить параметр префикса таблицы:
    Обязательно измените mybundle и myprefix_

    parameters:
        mybundle.db.table_prefix: myprefix_
    
  3. Добавить новую услугу:

    services:
        mybundle.tblprefix_subscriber:
            class: MyBundle\Subscriber\TablePrefixSubscriber
            arguments: [%mybundle.db.table_prefix%]
            tags:
                - { name: doctrine.event_subscriber }
    
  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();
            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;
                }
            }
        }       
    }
    
  5. Необязательный шаг для пользователей postgres: сделать что-то похожее для последовательностей

  6. Наслаждайтесь
11 голосов
/ 26 мая 2014

Альтернативный ответ

Это обновление, учитывающее новые функции, доступные в Doctrine2.

Стратегия именования Doctrine2

Doctrine2 использует NamingStrategy классы, которые реализуют преобразование из имени класса в имя таблицы или из имени свойства в имя столбца.

DefaultNamingStrategy просто находит «короткое имя класса» (без его пространства имен), чтобы вывести имя таблицы.

UnderscoreNamingStrategy делает то же самое, но также в нижнем регистре и "подчеркивает" "короткое имя класса".

Ваш класс CustomNamingStrategy может расширить любой из вышеперечисленных (как вам удобно) и переопределить методы classToTableName и joinTableName, чтобы вы могли указать, как должно создаваться имя таблицы (с использованием префикс).

Например, мой класс CustomNamingStrategy расширяет UnderscoreNamingStrategy и находит имя пакета на основе соглашений о пространстве имен и использует его в качестве префикса для всех таблиц.


Стратегия именования Symfony2

Использование вышеперечисленного в Symfony2 требует объявления вашего класса CustomNamingStragery как службы и ссылки на него в вашей конфигурации:

doctrine:
    # ...

    orm:
        # ...
        #naming_strategy: doctrine.orm.naming_strategy.underscore
        naming_strategy: my_bundle.naming_strategy.prefixed_naming_strategy

Плюсы и минусы

Плюсы:

  • выполнение одного куска кода для выполнения одной задачи - класс стратегии именования вызывается напрямую и используется его вывод;
  • ясность структуры - вы не используете события для запуска кода, который изменяет вещи, уже созданные другим кодом;
  • лучший доступ ко всем аспектам соглашений об именах;

Минусы:

  • нулевой доступ к метаданным сопоставления - у вас есть только тот контекст, который был вам задан в качестве параметров (это также может быть полезно, поскольку он вызывает соглашение, а не исключение);
  • нужна доктрина 2.3 (сейчас это не так уж и сложно, возможно, это было в 2011 году, когда задавался этот вопрос: -));
1 голос
/ 22 августа 2014

Ответ Симшауна работает нормально, но имеет проблему, когда у вас есть наследование single_table с ассоциациями на дочернем объекте.Первый оператор if возвращается, когда объект не является 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', когда таблица с именем 'content' приведет к появлению таблицы без префикса, поэтому использование подчеркивания типа 'co_' уменьшит этот риск.

0 голосов
/ 18 октября 2016

Я не знаю, когда реализовывать решение, которое включает в себя перехват событий (проблема производительности), поэтому я попробовал альтернативное решение, но оно не работает для меня.Я добавлял JMSPaymentCoreBundle и хотел добавить префикс в таблицах выплат.В этом комплекте определения таблиц находятся в каталоге Resources \ 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
0 голосов
/ 27 сентября 2016

@ simshaun ответ хороший, но есть проблема с отношениями «многие ко многим» и наследованием.

Если у вас есть родительский класс User и дочерний класс Employee, а Employee принадлежит поле Many-to-Many $addresses, таблица этого поля не будет иметь префикс. Это из-за:

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_user
  • pref_address
  • employee_address

Решение

Решением может быть изменение в ответе @simshaun пункта 4 следующим образом:

  1. Создать 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());
        }        
    }
    

Здесь мы обрабатываем отношение Многие-ко-многим , прежде чем проверять, является ли класс дочерним для наследования, и мы добавляем $mapping['sourceEntity'] == $classMetadata->getName(), чтобы добавить префикс только один раз, для объекта-владельца. поле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...