【问题标题】:Doctrine 2 Index Inheritance学说 2 索引继承
【发布时间】:2013-12-13 02:12:22
【问题描述】:

我是 Doctrine 2 的新手,我想弄清楚如何使用它进行索引继承。

我想要实现的是有一个基类,它定义了一些默认列以及应用程序中所有实体的必要索引。

示例:我的应用程序中的所有表都有created_onmodified_on,所以我准备了一个基础@MappedSuperclass,其中包含这两列。

这是我的代码:

<?php

/**
 * @MappedSuperclass
 */
abstract class EntityAbstract
{
    /**
     * @Column(type="datetime", name="created_on", nullable=false)
     */
    protected $createdOn;

    /**
     * @Column(type="datetime", name="modified_on", nullable=true)
     */
    protected $modifiedOn;

}

/**
 * @Entity
 * @Table(name="member", indexes={@Index(name="credential", columns={"email_address", "password"})})
 */
class Member extends EntityAbstract
{
    /**
     * @Column(type="string", name="full_name", length=50)
     */
    protected $fullName;

    /**
     * @Column(type="string", name="email_address", length=50, unique=true, nullable=false)
     */
    protected $emailAddress;

    /**
     * @Column(type="string", name="password", length=40, nullable=false)
     */
    protected $password;       

}
?>

我想强制 created_on 成为索引,所以我将 @Index 注释放在这个特定列的基类中。希望这将为Member 产生两个索引,即created_onemail_address+password 组合。然而,这会导致基类的索引被子类覆盖,因此created_on 不是索引。

/**
 * @MappedSuperclass
 * @Table(indexes={@Index(name="timestampcreated", columns={"created_on"})})
 */
abstract class EntityAbstract

我如何在 Doctrine 2 中实现这一点?看过单表继承,但我的理解是它有不同的目的。

【问题讨论】:

    标签: php inheritance doctrine-orm indexing


    【解决方案1】:

    这里是https://github.com/doctrine/orm/issues/5928#issuecomment-273624392 的完整解决方案,感谢https://medium.com/@alexkunin/doctrine-symfony-adding-indexes-to-fields-defined-in-traits-a8e480af66b2

    namespace App\EventListener;
    
    use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
    use App\Entity\Member;
    
    class MemberIndexListener
    {
        public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
        {
            /** @var Doctrine\ORM\Mapping\ClassMetadata $classMetadata */
            $classMetadata = $eventArgs->getClassMetadata();
            if (Member::class === $classMetadata->getName())
            {
                $prefix = strtolower((new \ReflectionClass($classMetadata->getName()))->getShortName()); // you can omit this line and ...
                $classMetadata->table['indexes'][$prefix.'_created_on'] =  // ... replace `[$prefix.'_created_on']` with `[]` for automatic index naming
                    ['columns' => ['username']
                ];
                // For UniqueConstraints, use:
                // $classMetadata->table['uniqueConstraints'][...] = ...
            }
        }
    }
    

    services.yaml:

    services:
        App\EventListener\MemberIndexListener:
            tags:
                - { name: doctrine.event_listener, event: loadClassMetadata }
    

    【讨论】:

      【解决方案2】:

      我想做同样的事情,但@Table 不允许出现在@mappedSuperclass 类中。我已经在学说2 github 页面https://github.com/doctrine/doctrine2/issues/5928 上发布了问题,答案是:

      @Table 在映射的超类上无效。

      你想想这听起来很合乎逻辑,但如果有这样的功能,继承索引和其他声明,那就太好了。

      【讨论】:

        【解决方案3】:

        这是我的自动索引继承解决方案。

        您可以在映射超类的表注释中指定索引。所有的孩子都会扩展它们。

        侦听器获取所有父映射超类的索引。

        #src/EventListener/InheritIndexListener.php
        <?php
        
        namespace App\EventListener;
        
        use Doctrine\ORM\EntityManagerInterface;
        use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
        use Doctrine\ORM\Mapping\ClassMetadata;
        
        final class InheritIndexListener
        {
            /** @var EntityManagerInterface */
            private $entityManager;
        
            public function __construct(EntityManagerInterface $entityManager)
            {
                $this->entityManager = $entityManager;
            }
        
            public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
            {
                $classMetadata = $eventArgs->getClassMetadata();
                $name = $classMetadata->name;
        
                if (!$classMetadata->isMappedSuperclass) {
                    foreach (class_parents($name) as $parent) {
                        $this->addParentIndexes($parent, $classMetadata);
                    }
                }
            }
        
            private function addParentIndexes(string $parent, ClassMetadata $classMetadata): void
            {
                $parentMetadata = $this->entityManager
                    ->getClassMetadata($parent);
        
                foreach ($parentMetadata->table['indexes'] as $index) {
                    if (!$this->hasSameIndex($index, $classMetadata->table['indexes'])) {
                        $classMetadata->table['indexes'][] = $index;
                    }
                }
        
                foreach ($parentMetadata->table['uniqueConstraints'] as $uniqueConstraint) {
                    if (!$this->hasSameIndex($uniqueConstraint, $classMetadata->table['uniqueConstraints'])) {
                        $classMetadata->table['uniqueConstraints'][] = $uniqueConstraint;
                    }
                }
            }
        
            private function hasSameIndex(array $needle, array $haystack): bool
            {
                foreach ($haystack as $item) {
                    if ($item['columns'] === $needle['columns']) {
                        return true;
                    }
                }
        
                return false;
            }
        }
        
        
        #config/services.yaml
        services:
            App\EventListener\InheritIndexListener:
                tags:
                    - { name: doctrine.event_listener, event: loadClassMetadata }
        

        还有实体。

        #src/Entity/BaseResource.php
        <?php
        
        use Doctrine\ORM\Mapping as ORM;
        
        /**
         * @ORM\MappedSuperclass
         * @ORM\Table(
         *     indexes={
         *     @ORM\Index(columns={"external_id"}),
         *     }
         *     )
         */
        abstract class BaseResource
        {
            /**
             * @var int|null
             * @ORM\Column(type="integer")
             */
            private $externalId;
            //...
        }
        
        #src/Entity/BaseSlugableResource.php
        <?php
        
        use Doctrine\ORM\Mapping as ORM;
        
        /**
         * @ORM\MappedSuperclass
         * @ORM\Table
         *     uniqueConstraints={
         *     @ORM\UniqueConstraint(columns={"slug"}),
         *     },
         *     )
         */
        abstract class BaseSlugableResource extends BaseResource
        {
            /**
             * @var string|null
             * @ORM\Column(type="string")
             */
            private $slug;
            //...
        }
        
        #src/Entity/Article.php
        <?php
        
        use Doctrine\ORM\Mapping as ORM;
        
        /**
         * @ORM\Entity
         */
        class Article extends BaseSlugableResource
        {
            //...
        }
        

        因此,Article 实体将具有 externalId 索引和 slug 唯一索引。

        【讨论】:

        • 您需要解释您的解决方案的组成部分。不仅仅是“这里有一些代码”并将其粘贴到答案中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-14
        • 1970-01-01
        • 2016-10-08
        • 2012-11-29
        • 1970-01-01
        相关资源
        最近更新 更多