Ste*_*ant 5 php mysql sql symfony doctrine-orm
我想给一个实体(发票,订单,预订等)一个唯一的序列号,但在那一年内只是唯一的.因此,每年(或其他字段,如客户)启动的第一张发票的ID为1.这意味着可以有复合主键(年份,ID)或一个主键(即invoice_id)和另外两个列.独特的.
我的问题:使用Doctrine2和Symfony2为对象提供自动生成的ID和其他值的唯一组合的最佳方法是什么?
复合键的学说限制
Doctrine不能将自动生成的ID分配给具有复合主键的实体(http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html) :
具有复合键的每个实体都不能使用除"ASSIGNED"之外的id生成器.这意味着ID字段必须在调用之前设置其值
EntityManager#persist($entity).
手动设置序列号
所以我必须手动分配ID.为了做到这一点,我试图在某一年寻找最高的ID并给新ID + 1的实体.我怀疑这是最好的方式,即使它是,我还没有找到最好的( DRY)这样做的方法.由于我认为这是一个通用问题,我想防止XY问题,我已经开始提出这个问题了.
香草选择:仅限MyISAM
我基于这个答案找到了'vanilla'MySQL/MyISAM解决方案:
CREATE TABLE IF NOT EXISTS `invoice` (
`year` int(1) NOT NULL,
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`year`,`id`)
) ENGINE=MyISAM;
Run Code Online (Sandbox Code Playgroud)
由于Doctrine2的局限性,这是行不通的,所以我正在寻找一个类似于这个vanilla MySQL解决方案的Doctrine ORM.
其他方案
InnoDB也有一个解决方案:在MySQL中使用自动增量定义复合键
您链接到的预插入触发器解决方案的 ORM 等效项是生命周期回调。您可以在这里阅读有关它们的更多信息。
一个天真的解决方案看起来像这样。
服务.yml
services:
invoice.listener:
class: MyCompany\CompanyBundle\EventListener\InvoiceListener
tags :
- { name: doctrine.event_subscriber, connection: default }
Run Code Online (Sandbox Code Playgroud)
发票监听器.php
<?php
namespace MyCompany\CompanyBundle\EventListener;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use MyCompany\CompanyBundle\Entity\Invoice;
class InvoiceListener implements EventSubscriber {
protected $invoices;
public function getSubscribedEvents() {
return [
'onFlush',
'postFlush'
];
}
public function onFlush(OnFlushEventArgs $event) {
$this->invoices = [];
/* @var $em \Doctrine\ORM\EntityManager */
$em = $event->getEntityManager();
/* @var $uow \Doctrine\ORM\UnitOfWork */
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof Invoice) {
$this->invoices[] = $entity;
}
}
}
public function postFlush(PostFlushEventArgs $event) {
if (!empty($this->invoices)) {
/* @var $em \Doctrine\ORM\EntityManager */
$em = $event->getEntityManager();
foreach ($this->invoices as $invoice) {
// Get all invoices already in the database for the year in question
$invoicesToDate = $em
->getRepository('MyCompanyCompanyBundle:Invoice')
->findBy(array(
'year' => $invoice->getYear()
// You could include e.g. clientID here if you wanted
// to generate a different sequence per client
);
// Add your sequence number
$invoice->setSequenceNum(count($invoicesToDate) + 1);
/* @var $invoice \MyCompany\CompanyBundle\Entity\Invoice */
$em->persist($invoice);
}
$em->flush();
}
}
}
Run Code Online (Sandbox Code Playgroud)