与问题主题一样,如何在symfony2中设置默认表格前缀?
最好是默认情况下可以为所有实体设置,但可以选择覆盖各个实体。
答案 0 :(得分:46)
我自己刚刚想到这一点,我想详细说明如何实现这一目标。
Symfony 2&学说2.1
注意:我使用YML进行配置,这就是我要展示的内容。
打开捆绑包的 Resources / 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用户的可选步骤:do something similary for sequences
答案 1 :(得分:11)
这是一个考虑到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
优点:
缺点:
答案 2 :(得分:1)
Simshaun的回答很好,但是如果你有一个single_table继承,并且在子实体上有关联,则会出现问题。当实体不是rootEntity时,第一个if语句返回,而此实体可能仍然具有必须加前缀的关联。
我通过将订户调整为以下内容来解决此问题:
<?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;
}
}
}
}
}
这有一个缺点; 如果一个不明智的选择前缀实际上已经是表名的一部分,则可能会引发冲突。 例如。使用前缀&#39; co&#39;当有一个名为“内容”的表格时将导致一个没有前缀的表,所以使用像#co;&#39; co&_ 39;将降低这种风险。
答案 3 :(得分:0)
我没有在何时实施涉及捕获事件(性能问题)的解决方案,所以我尝试了替代解决方案,但它对我不起作用。 我正在添加JMSPaymentCoreBundle,并希望在付款表上添加前缀。 在此捆绑包中,表的定义位于Resources \ config \ doctrine目录(xml格式)中。 我终于找到了这个解决方案:
1)复制包含表格定义的doctrine目录并将其粘贴到我的主包
中2)修改定义中表格的名称以添加前缀
3)在你的config.yml中,在doctrine / orm / entity manager / mapping部分声明它(dir是放置修改过的定义的目录):
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
答案 4 :(得分:0)
此外,您可以将此捆绑软件用于新版本的Symfony(4)-DoctrinePrefixBundle
答案 5 :(得分:-1)
@simshaun答案很好,但是多对多关系和继承存在问题。
如果您有父类User
和子类Employee
,并且Employee
拥有多对多字段{{1} },此字段的表将没有前缀。
那是因为:
$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
解决方案可以在@simshaun的答案中修改第4点,如下所示:
创建 MyBundle \ Subscriber \ TablePrefixSubscriber.php
employee_address
在验证类是否是继承的子代之前,我们处理多对多关系,并且我们添加<?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());
}
}
只添加前缀一次,在拥有该领域的实体。