[英]How to setup table prefix in symfony2
Like in question topic, how can I setup default table prefix in symfony2?就像问题主题一样,如何在 symfony2 中设置默认表前缀?
The best if it can be set by default for all entities, but with option to override for individual ones.如果可以默认为所有实体设置它是最好的,但可以选择覆盖单个实体。
Having just figured this out myself, I'd like to shed some light on exactly how to accomplish this.我自己刚刚想通了这一点,我想说明一下如何做到这一点。
Symfony 2 & Doctrine 2.1 Symfony 2 和教义 2.1
Note: I use YML for config, so that's what I'll be showing.注意:我使用 YML 进行配置,所以这就是我要展示的内容。
Open up your bundle's Resources/config/services.yml打开你的 bundle 的Resources/config/services.yml
Define a table prefix parameter:定义表前缀参数:
Be sure to change mybundle and myprefix_一定要更改mybundle和myprefix_
parameters: mybundle.db.table_prefix: myprefix_
Add a new service:添加新服务:
services: mybundle.tblprefix_subscriber: class: MyBundle\\Subscriber\\TablePrefixSubscriber arguments: [%mybundle.db.table_prefix%] tags: - { name: doctrine.event_subscriber }
Create MyBundle\\Subscriber\\TablePrefixSubscriber.php创建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; } } } }
Optional step for postgres users: do something similary for sequences postgres 用户的可选步骤: 对序列做一些类似的事情
This is an update taking into account the newer features available in Doctrine2.这是考虑到 Doctrine2 中可用的新功能的更新。
Doctrine2 uses NamingStrategy
classes which implement the conversion from a class name to a table name or from a property name to a column name. Doctrine2 使用
NamingStrategy
类来实现从类名到表名或从属性名到列名的转换。
The DefaultNamingStrategy
just finds the "short class name" (without its namespace) in order to deduce the table name. DefaultNamingStrategy
只是找到“短类名”(没有其命名空间)以推断表名。
The UnderscoreNamingStrategy
does the same thing but it also lowercases and "underscorifies" the "short class name". UnderscoreNamingStrategy
做同样的事情,但它也小写并“强调”“短类名”。
Your CustomNamingStrategy
class could extend either one of the above (as you see fit) and override the classToTableName
and joinTableName
methods to allow you to specify how the table name should be constructed (with the use of a prefix).您的
CustomNamingStrategy
类可以扩展上述任一类(如您所见)并覆盖classToTableName
和joinTableName
方法,以允许您指定应如何构造表名(使用前缀)。
For example my CustomNamingStrategy
class extends the UnderscoreNamingStrategy
and finds the bundle name based on the namespacing conventions and uses that as a prefix for all tables.例如,我的
CustomNamingStrategy
类扩展了UnderscoreNamingStrategy
并根据命名空间约定查找包名称并将其用作所有表的前缀。
Using the above in Symfony2 requires declaring your CustomNamingStragery
class as a service and then referencing it in your config:在 Symfony2 中使用上述内容需要将您的
CustomNamingStragery
类声明为服务,然后在您的配置中引用它:
doctrine:
# ...
orm:
# ...
#naming_strategy: doctrine.orm.naming_strategy.underscore
naming_strategy: my_bundle.naming_strategy.prefixed_naming_strategy
Pros:优点:
Cons:缺点:
Simshaun's answer works fine, but has a problem when you have a single_table inheritance, with associations on the child entity. Simshaun 的回答工作正常,但是当您有一个 single_table 继承时会出现问题,并且在子实体上有关联。 The first if-statement returns when the entity is not the rootEntity, while this entity might still have associations that have to be prefixed.
当实体不是 rootEntity 时,第一个 if 语句返回,而该实体可能仍然具有必须加前缀的关联。
I fixed this by adjusting the subscriber to the following:我通过将订阅者调整为以下内容来解决此问题:
<?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;
}
}
}
}
}
This has a drawback though;不过这有一个缺点; A not wisely chosen prefix might cause conflicts when it's actually already part of a table name.
当前缀实际上已经是表名的一部分时,未明智选择的前缀可能会导致冲突。 Eg using prefix 'co' when theres a table called 'content' will result in a non-prefixed table, so using an underscore like 'co_' will reduce this risk.
例如,当存在名为“content”的表时使用前缀“co”将导致非前缀表,因此使用像“co_”这样的下划线将降低这种风险。
此外,您可以将此包用于新版本的 Symfony (4) - DoctrinePrefixBundle
I don't when to implement a solution that involved catching event (performance concern), so I have tried the Alternate Solution but it doesn't work for me.我不知道何时实施涉及捕获事件(性能问题)的解决方案,所以我尝试了替代解决方案,但它对我不起作用。 I was adding the JMSPaymentCoreBundle and wanted to add a prefix on the payment tables.
我正在添加 JMSPaymentCoreBundle 并想在支付表上添加一个前缀。 In this bundle, the definition of the tables are in the Resources\\config\\doctrine directory (xml format).
在此包中,表的定义位于 Resources\\config\\doctrine 目录(xml 格式)中。 I have finally found this solution:
我终于找到了这个解决方案:
1) copy doctrine directory containing the definitions on the table and paste it in my main bundle 1)复制包含表上定义的学说目录并将其粘贴到我的主包中
2) modify the name of the tables in the definitions to add your prefix 2)修改定义中表的名称以添加您的前缀
3) declare it in your config.yml, in the doctrine/orm/entity manager/mapping section (the dir is the directory where you have put the modified definitions): 3) 在你的 config.yml 中声明它,在 dotric/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
You can use this bundle: 你可以使用这个包:
@simshaun answer is good, but there is a problem with Many-to-Many relationships and inheritance. @simshaun 的回答很好,但是多对多关系和继承存在问题。
If you have a parent class User
and a child class Employee
, and the Employee
own a Many-to-Many field $addresses
, this field's table will not have a prefix.如果您有一个父类
User
和一个子类Employee
,并且Employee
拥有多对多字段$addresses
,则该字段的表将没有前缀。 That is because of:那是因为:
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 {
}
With the original solution, if you apply pref_
prefixe to this mapping, you will end up with tables :使用原始解决方案,如果您将
pref_
前缀应用于此映射,您将最终得到表:
pref_user
pref_address
employee_address
A solution can be to modify, in the answer of @simshaun, the point 4 like this:解决方案可以是在@simshaun 的回答中修改第 4 点,如下所示:
Create MyBundle\\Subscriber\\TablePrefixSubscriber.php创建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()); } }
Here we handle the Many-to-Many relationship before verifying if the class is the child of an inheritance, and we add $mapping['sourceEntity'] == $classMetadata->getName()
to add the prefix only one time, on the owning entity of the field.这里我们在验证类是否是继承的子类之前处理多对多关系,我们添加
$mapping['sourceEntity'] == $classMetadata->getName()
只添加一次前缀,在字段的拥有实体。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.