文档章节

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
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 中使用 @Inheritance 注解,并且需要使用 strategy 属性指定继承策略,继承策略有 SINGLETABLE、TABLEPER_CLASS 和 JOINED 三种。 一、SINGLE_TABLE SINGLE_TABLE 是将...

ada_young
2015/06/10
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

没有更多内容

加载失败,请刷新页面

加载更多

apache顶级项目(二) - B~C

apache顶级项目(二) - B~C https://www.apache.org/ Bahir Apache Bahir provides extensions to multiple distributed analytic platforms, extending their reach with a diversity of s......

晨猫
今天
3
0
day152-2018-11-19-英语流利阅读

“超级食物”竟然是营销噱头? Daniel 2018-11-19 1.今日导读 近几年来,超级食物 superfoods 开始逐渐走红。不难发现,越来越多的轻食餐厅也在不断推出以超级食物为主打食材的健康料理,像是...

飞鱼说编程
今天
9
0
SpringBoot源码:启动过程分析(二)

接着上篇继续分析 SpringBoot 的启动过程。 SpringBoot的版本为:2.1.0 release,最新版本。 一.时序图 一样的,我们先把时序图贴上来,方便理解: 二.源码分析 回顾一下,前面我们分析到了下...

Jacktanger
昨天
3
0
Apache防盗链配置,Directory访问控制,FilesMatch进行访问控制

防盗链配置 通过限制referer来实现防盗链的功能 配置前,使用curl -e 指定referer [root@test-a test-webroot]# curl -e "http://www.test.com/1.html" -x127.0.0.1:80 "www.test.com/1.jpg......

野雪球
昨天
6
0
RxJava threading

因为Rx针对异步系统设计,并且Rx也自然支持多线程,所以新的Rx开发人员有时会假设Rx默认是多线程的。在其他任何事情之前,重要的是澄清Rx默认是单线程的。 除非另有说明,否则每次调用onNex...

woshixin
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部