我正在尝试在自定义项目管理应用程序中实现时间跟踪机制。
此应用包含多个实体(门票,项目,维基页面,冲刺,......)
我希望我的时间跟踪能够成为“通用”,因为我希望用户能够在故障单,项目,维基页面......以及任何实体上记录时间。
现在,我试图找出用于我的TimeLog实体的数据库模式(关系)。
理论上我可以在我的应用程序中为每个实体创建一个关系,但这需要我在以后引入新实体时继续更新模式。
是否每个人都实施过这样的事情?
欢迎所有建议。
非常感谢提前。
答案 0 :(得分:0)
我在尝试添加评论,喜欢和其他类型的元素时,在我的应用中遇到了类似的情况,这些元素的行为并不真正取决于它们所附加的实体。
我最终选择的解决方案是在我的引用实体(例如Comment)中有两个字段来保存被引用实体的id及其类型。由于我多次使用它,我将属性放入以下特征:
namespace AppBundle\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait EntityReferenceTrait
{
/**
* @ORM\Column(name="reference_id", type="integer")
*/
private $referenceId;
/**
* @ORM\Column(name="reference_type", type="integer")
*/
private $referenceType;
/* ... setters & getters ... */
}
然后我可以在持有这种引用的实体中使用它:
/**
* @ORM\Table(name="comments", indexes={@ORM\Index(name="references", columns={"reference_id", "reference_type"})})
* @ORM\Entity(repositoryClass="AppBundle\Repository\Comment\CommentRepository")
*/
class Comment
{
/* ... other traits ... */
use \AppBundle\Entity\Traits\EntityReferenceTrait;
/* ... other fields & methods ... */
}
注意:我为引用添加了一个索引,但整个过程没有必要正常工作。如果你使用这样的索引,如果你想从中受益,请注意WHERE子句的顺序
为了提高性能并根据所引用实体的类型添加其他配置,我直接在我的应用程序的配置中处理了设置。因此,我有类似的东西:
commentables:
news:
classname: AppBundle\Entity\News\News
type_id: 1
browse_route: news_comments
multiple_locales: false
...
这使我能够准确了解我的Comment
实体可以引用的实体类型。它还允许我自动将特定侦听器挂钩到被引用的实体,以便删除引用的实体触发例如删除相关注释。我通过处理AppBundle/DependencyInjection/AppExtension.php
中的配置(更多关于此here)并将所需的侦听器列表保存到参数中来完成此操作。然后,通过向loadClassMetadata
事件添加侦听器,我可以有效地处理相关实体的删除,例如。
这是通过在ClassMetadata
实例上使用addEntityListener
来挂接侦听器的特定生命周期事件的侦听器的侦听器:
namespace AppBundle\Listener;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class MappingListener
{
private $entityListenersMapping = [];
/**
* @param array $mappingConfig Associative array with keys being listeners classnames and values being arrays associating an event to a method name
*/
public function __construct(array $mappingConfig)
{
$this->entityListenersMapping = $mappingConfig;
}
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
if(!array_key_exists($classMetadata->name, $this->entityListenersMapping))
{
return;
}
// Hook the entity listeners in the class metadata
foreach($this->entityListenersMapping[$classMetadata->name] as $listenerClassName => $eventsCallbacks)
{
foreach($eventsCallbacks as $event => $methodName)
{
$classMetadata->addEntityListener($event, $listenerClassName, $methodName);
}
}
}
}
无论哪种方式,对于这部分,它主要取决于您的实体的特定需求,但我想这些“软”外键通过preRemove
和{模仿ON DELETE CASCADE行为是非常普遍的需要。 {1}}事件。
考虑到这些引用和拥有它们的实体的处理,我还创建了一个postRemove
来轻松创建管理这些实体的服务,以便与其交互的其他组件不必担心底层配置。
这些管理者的大多数公共方法的界面通常需要:
使用我的经理服务中检索到的这两个信息和配置,我可以轻松地与数据库进行交互,即使在我的情况下,它将配置中定义的整数存储为引用类型而不是实体的类名被提及。
基于此,我可以为我的任何app实体启用评论,赞,投票,订阅等(只要它的主键是一个整数),我的配置文件中只有几行。无需更新数据库模式,并且需要使用正确的生命周期事件侦听器,而不必担心数据库中的孤立条目。
在旁注中,应该提到的是,您将无法从反面检索引用实体,因为它不是真正的关联。您也不会受益于外键行为。因此,即使您通过侦听删除事件来模拟EntityRefererManagerTrait
行为,如果通过DQL直接执行某些ON DELETE CASCADE
操作,您也无法确保数据库中没有孤儿。