在学说中扩展实体的最佳方式

时间:2012-05-28 21:09:44

标签: symfony doctrine-orm

我在一个由我制作的小型框架中有一个用户实体。现在我想在几个项目中使用这个用户实体。 但在某些项目中,我想在不修改文件的情况下向用户实体添加一些字段。

到目前为止我尝试了什么:

我在DefaultUser Bundle中创建了一个新的DefaultUser实体,并使用户实体成为一个映射的超类。但现在我不能在其他实体中建立关联,比如

    /*
     * @ORM\ManyToOne(targetEntity="User", inversedBy="jobs")
     * @ORM\JoinColumn(name="user", referencedColumnName="id")
     */
     private $user;

因为Doctrine无法在用户实体中找到id列。这仅在我指定DefaultUser实体时有效。根据学说文档,如果只存在一个叶子,这仅适用于多对多关联。

然后我尝试了单表继承。这工作正常但我必须修改DiscriminatorMap,如果我想扩展我的用户实体,这是多个项目的共享acros ...

那么扩展UserEntity的最佳方式是什么?

1 个答案:

答案 0 :(得分:0)

我有完全相同的问题 - 我刚从RedBean切换到Doctrine(对于使用Zend Framework的项目),我的类的结构没有考虑到这个问题。核心问题是Doctrine中的地图与类有一对一的关系,据我所知。我们正在寻找的是一种使用一个抽象类(DefaultUser)中的映射的具体类(UserEntity)的方法。我的解决方案,可能是一个黑客(我只使用Doctrine几天),至少适用于YAML:

创建一个扩展YAML驱动程序的新映射驱动程序,并使用以下内容覆盖_loadMappingFile方法:

class MyLibrary_Doctrine_Mapping_Driver_YamlExtended extends MyLibrary_Doctrine_Mapping_Driver_YamlExtended
{
protected $_basicEntityFolder;

protected function _loadMappingFile($file)
{
$entMaps = parent::_loadMappingFile($file);

//merge this with any extensions if defined
foreach($entMaps as $ent => $map)
  { //load the relevant map
    if (!isset($map['extendEntity'])) {
      continue;
    }
    $fileName = $this->_basicEntityFolder . DIRECTORY_SEPARATOR .  str_replace('\\', '.', $map['extendEntity']) . $this->_fileExtension;

$extendedMaps = $this->_loadMappingFile($fileName);
if (!is_array($extendedMaps[$map['extendEntity']])) {
  throw new MyProject_Doctrine_Exception("Entity to extend from could not be found.");
}
//merge so that the file lower in the class hierachy always overrides the higher
$map = array_merge($extendedMaps[$map['extendEntity']], $map);

//clear the  extendEntity value
unset($map['extendEntity']);
$entMaps[$ent] = $map;
  }
    return $entMaps;
}

public function setExtendedEntitiesFolder($path)
{
  $this->_basicEntityFolder = $path;
}
}

然后我在不同的文件夹中有两个yaml文件,如下所示:

#MyApplication/Entities/Maps/Entities.User.dcm.yml
Entities\User:
 extendEntity: LibraryEntities\User

这是应用程序中的文件。然后在库中我有

#Library/Entities/Maps/ExtendedEntities/LibraryEntities.User.dcm.yml
LibraryEntities\User:
type: entity
table: user
fields:
  username:
    type: text
    nullable: true
password:
  type: text
  nullable: true
defaultProfile:
  type: text
  nullable: true
  column: default_profile

它在ExtendedEntities文件夹中的原因是我可以使用普通命名空间在库中定义mappedSuperclasses,而Doctrine会在类扩展它们时自动加载它们,但是这些extendedentities超出了Doctrine通常的类继承的路径加载(如果它们都在正常的文件夹结构中,那么例如“类ApplicationUser扩展LibraryUser”Doctrine将尝试加载LibraryUser的配置,因为它会找到它,然后导致你已经遇到的相同错误。)

然后当我设置我的$ em时,我提供了我的驱动程序:

$driverImpl = new MyLibrary_Doctrine_Mapping_Driver_YamlExtended(array(APPLICATION_PATH . '/entities/maps', 
                                                                   LIBRARY_PATH . '/Entities/Maps'));
$driverImpl->setExtendedEntitiesFolder(LIBRARY_PATH . '/Entities/Maps/ExtendedEntities');

请注意,此解决方案允许由'extendEntity'定义的继承链(因为_loadMappingFile方法是递归的)。此外,链下方的任何配置文件都可以覆盖已定义的任何属性,因此即使您的库中存在yaml:

 username:
    type: text

假设您有一个项目,其中整数的用户名可以在应用程序配置中使用

覆盖它
 username:
    type: int

或其他什么。

因此,这解决了在基类上定义Doctrine样式继承的问题。在每个项目中,您都可以根据需要定义DiscriminatorMap。

原则上相同的解决方案可以应用于注释,虽然扩展注释驱动程序有点复杂,因为它不是通过一次读取一个文件并将其转换为数组来简单地读取元数据,而是对注释阅读器的请求,这意味着实现这种结构会更棘手。

我很想知道其他人是如何解决这个问题的。