文档章节

Table Inheritance with Doctrine

j
 jims
发布于 2016/11/14 08:26
字数 1481
阅读 8
收藏 0

NTRODUCTION

Lately we had several projects where we had to store in a database very different items that shared a common state.

As an example take the RocketLab website you are reading: Events and BlogPosts are aggregated in the LabLog list as if they were similar items. And indeed they all have a Title, a Date and a Description.

But if you get the detail page of an Event or a BlogPost you can see that they actually don’t contain the same information: a BlogPost contains essentially formatted text when an Event contains more structured information such as the place where the event will take place, the type of event it is, if people need to register to attend, etc..

Still we have to access those entities sometimes as similar items (in the LabLog list) or as different items (in the events list and in the blog posts list).

NAÏVE DATABASE MODEL

Our first idea, and it was not that bad, Drupal does just the same, was to have a database table with the common fields, a field containing the type of item (it’s either an event or a blog post) and a data field where we serialized the corresponding PHP object. This approach was ok until we had to filter or search LabLog items based on fields that were contained in the serialized data.

Indeed SQL does not know anything about PHP serialized data, thus you cannot use any of it’s features on that data.

So how do you get all the LabLog items that are Events, happen in April 2012 and are “techtalks”? The only way is to go through all the Events records of April, unserialize the data and check if it’s a techtalk event. In SQL you would normally only do a single request to find those items.

A BETTER DATABASE MODEL

There is a better way to model this in a database, it’s called table inheritance. It exists in two forms: single table inheritance and multiple table inheritance.

Multiple table inheritance

Multiple table inheritance requires to use three tables instead of a single one. The idea is to keep the common data in a “parent” table, which will reference items either in the Event table or in the BlogPost table. The type column (called the discriminator) helps to find out if the related item should be searched in the Event table or in the BlogPost table. This is called multiple table inheritance because it tries to model the same problem as object inheritance using multiple database tables.

Multiple table inheritance

When you have a LabLogItem you check the type field to know in which table to find the related item, then you look for that item with the ID equals to related_id.

Single table inheritance

Alternatively the same can be modelled in a single table. All the fields are present for all the types of LabLogItem but the one that do not pertain to this particular type of item are left empty. This is called single table inheritance.

Single table inheritance

Single or multiple table inheritance

The difference is really only in how the data is stored in the database. On the PHP side this will not change anything. One may notice that single table inheritance will promote performance because everything is in a single table and there is no need to use joins to get all the information. On the other hand, multiple table inheritance will allow a cleaner separation of the data and will not introduce “dead data fields”, i.e. fields that will remain NULL most of the time.

TABLE INHERITANCE WITH SYMFONY AND DOCTRINE

Symfony and Doctrine make it extremely easy to use table inheritance. All you need to do is to model your entities as PHP classes and then create the correct database mapping. Doctrine will take care of the hassle of implementing the inheritance in the database server.

Please note that the code I present here is not exactly what we use in RocketLab; we are developers and as such we always have to make things harder. But the idea is there…

The parent entity

In the case of RocketLab we created a parent (abstract) entity, called LabLogItem, that contains the common properties.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

/**

* This class represents a LabLog item, either a BlogPost or an Event.

* It is abstract because we never have a LabLog entity, it's either an event or a blog post.

* @ORMEntity

* @ORMTable(name="lablog")

* @ORMInheritanceType("SINGLE_TABLE")

* @ORMDiscriminatorColumn(name="type", type="string")

* @ORMDiscriminatorMap( {"event" = "Event", "blogpost" = "BlogPost"} )

*/

abstract class LabLogItem

{

    /**

     * @ORMId

     * @ORMColumn(type="integer")

     * @ORMGeneratedValue(strategy="AUTO")

     */

    protected $id;

 

    /**

     * @ORMColumn(type="date")

     */

    protected $date;

 

    /**

     * @ORMColumn(type="string")

     */

    protected $title;

 

    /**

     * @ORMColumn(type="text")

     */

    protected $description;

 

    /***** Getters and setters *****/

 

    public function getId()

    {

        return $this->id;

    }

 

    public function setDate($date)

    {

        $this->date = $date;

    }

 

    public function getDate()

    {

        return $this->date;

    }

 

    // And so on...

}

 

There are several things to note about the mapping:

  • @ORMInheritanceType: indicates that this entity is used as parent class in the table inheritance. This example uses single table inheritance, but using multiple tables inheritance is as easy as setting the parameter to “JOINED”. Doctrine will create an manage the unique or multiple database tables for you !
  • @ORMDiscriminatorColumn: indicates which column will be used as discriminator (i.e. to store the type of item). You don’t have to define this column in the entity, it will be automagically created by Doctrine.
  • @ORMDiscriminatorMap: this is used to define the possible values of the discriminator column as well as associating a specific entity class with each type of item. Here the discriminator columns may contain the string “event” or “blogpost”. When its value is “event” the class Event will be used, when its value is “blogpost”, the class BlogPost will be used.

Basically that’s the only thing you need to use table inheritance, but let’s have a look at the children entities.

The children entities

We have two regular entities to model the events and blog posts. Those entities extend LabLogItem.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

/**

* Represent a blog post item.

* Note that this class extends LabLogItem

*/

class LabLogItemBlog extends LabLogItem

{

    /**

     * @ORMColumn(type="text")

     */

    protected $content;

 

    /***** Getters and setters *****/

 

    public function getContent()

    {

        return $this->content;

    }

 

    public function setContent($content)

    {

        $this->content = $content;

    }

}

 

/**

* Represent an event item.

* Note that this class extends LabLogItem

*/

class LabLogItemEvent extends LabLogItem

{

    /**

     * @ORMColumn(type="string")

     */

    protected $eventType;

 

    /**

     * @ORMColumn(type="string")

     */

    protected $location;

 

    /**

     * @ORMColumn(type="boolean")

     */

    protected $requiresRegistration;

 

    /***** Getters and setters *****/

 

    public function getEventType()

    {

        return $this->eventType;

    }

 

    public function setEventType($type)

    {

        $this->eventType = $type;

    }

 

    // And so on...

}

 

There is not much special in the children entities. An important thing to note is that the common fields defined in the parent entity LabLogItem SHOULD NOT be repeated here. Also you may notice that there is no annotations in the children such as @ORMEntity to indicate that they are entities. Indeed they will inherit the annotations of LabLogItem and become entities.

From now on, when you create a PHP object of type Event and ask the entity manager to persist it, Doctrine will automatically do the complex work for you. From the developper point of view, Events and BlogPosts are just entities like any other.

It’s easy to do operations on items which you don’t know exactly the type:

 

 

1

2

3

4

5

6

7

8

9

10

11

$item = $entityManager->getRepository('RocketLabBundle:LabLogItem')->findOneByDate($someDate);

 

// Here we don't know exactly whether $item contains a blog post or an event...

 

if ($item instanceof Event) {

    // Then it's an Event

    echo $item->getEventType();

} else {

    // Otherwise it's a BlogPost

    echo $item->getContent();

}

 

But, if you know the type of item you are using you still can use them as regular entities:

 

 

1

2

3

4

$item = $entityManager->getRepository('RocketLabBundle:Event')->findOneByDate($someDate);

 

//  We have searched the Event entity repository so what we get in $item MUST BE an Event

echo $item->getEventType();

 

CONCLUSION

As you can see above using table inheritance with Symfony and Doctrine is very easy. It’s just a matter of creating the parent class and the correct mapping. Furthermore you can switch from single to multiple table inheritance by modifying one line of code.

This technique should be used whenever you need to store items with a common state but that are very different in their nature.

本文转载自:https://blog.liip.ch/archive/2012/03/27/table-inheritance-with-doctrine.html

共有 人打赏支持
j
粉丝 4
博文 172
码字总数 30033
作品 0
合肥
私信 提问
Extending entities in Symfony2 with Doctrine2

down vote You can see in this link to know about inheritance: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance You must dec......

jims
2016/10/24
2
0
Using Redis for Doctrine Caching in Symfony2

Redis is an open source key-value cache and storage, which designed to be very fast since it uses in-memory datasets. Of course, you can persist your data by dumping it to the d......

jims
2015/11/01
0
0
Hibernate 继承映射

继承映射在 Annotation 中使用 @Inheritance 注解,并且需要使用 strategy 属性指定继承策略,继承策略有 SINGLETABLE、TABLEPER_CLASS 和 JOINED 三种。 一、SINGLE_TABLE SINGLE_TABLE 是将...

ada_young
2015/06/10
0
0
SonataEasyExtendsBundle功能包:概述

SonataEasyExtendsBundle is a prototype for generating a valid bundle structure from a Vendor Bundle. The tool is started with the simple command line: sonata:easy-extends:genera......

firehare
2014/04/17
0
0
Hibernate的继承关系分类,annotation表示

hibernate继承映射 以下测试是在mysql中进行的。 1、单表方式 Animal.java @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="animalType") @Discr......

BenjaminMa
2013/11/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

漏洞防御与修复工作

漏洞管理工作是企业安全建设必不可少的一环,在风险管理工作中,漏洞管理能够防患于未然,企业对漏洞管理有着广泛的基础建设和实践经验。但随着攻防技术的发展,传统漏洞管理的安全技术和管理...

linuxprobe16
18分钟前
0
0
MicroPython技术及应用前景

1 Micropython技术是什么? MicroPython极精简高效的实现了Python3语言。它包含Python标准库的一小部分,能在单片机和受限环境中运行。 1.1 MicroPython发展 由剑桥大学的理论物理学家乔治....

bodasisiter
24分钟前
0
0
跟我学Spring Cloud(Finchley版)-13-通用方式使用Hystrix

本节详细讲解使用Hystrix的通用方式。 简介 Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要...

周立_ITMuch
33分钟前
0
0
🛠️Hanjst/汉吉斯特更新加JavaScript运行时优化等

这是 Hanjst/汉吉斯特 发布以来的首个主要升级更新版本。这次的主要升级更新的内容包括移除HTML Comments注释行, 优化在 Hanjst include模板文件时的JavaScript运行时环境。 Hanjst 在设计和...

wadelau
今天
2
0
OSChina 周六乱弹 —— 舔狗是没有好下场的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @我没有抓狂 :#今天听什么# #今天听这个# 分享 Nirvana 的歌曲《Smells Like Teen Spi...》 《Smells Like Teen Spi...》- Nirvana 手机党少...

小小编辑
今天
494
13

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部