WEBVTT

1
00:00:00.040 --> 00:00:04.719
<v Speaker 1>Okay, let's unpack this. Imagine writing like ten thousand lines

2
00:00:04.759 --> 00:00:08.880
<v Speaker 1>of pristine, beautifully architected, object oriented PHP code.

3
00:00:09.039 --> 00:00:10.960
<v Speaker 2>Oh that's the dream, right right.

4
00:00:11.039 --> 00:00:14.439
<v Speaker 1>You have your classes, your properties, your inheritance trees, all

5
00:00:14.480 --> 00:00:16.760
<v Speaker 1>your objects are just neatly interacting with one another.

6
00:00:16.920 --> 00:00:19.160
<v Speaker 2>Yeah, a perfect domain model exactly.

7
00:00:19.600 --> 00:00:22.879
<v Speaker 1>And then after all that elegant design, you have to just,

8
00:00:22.960 --> 00:00:26.760
<v Speaker 1>you know, smash that entire architecture into a flat, rigid,

9
00:00:26.960 --> 00:00:28.920
<v Speaker 1>two dimensional grid of database tables.

10
00:00:29.039 --> 00:00:32.000
<v Speaker 2>It really is the ultimate architectural heartbreak. I mean it's

11
00:00:32.039 --> 00:00:33.079
<v Speaker 2>a huge problem.

12
00:00:33.200 --> 00:00:36.439
<v Speaker 1>It totally is because your PHP application it speaks this

13
00:00:36.560 --> 00:00:42.439
<v Speaker 1>rich language of objects, but your database it only understands tables, columns, rows,

14
00:00:42.920 --> 00:00:44.640
<v Speaker 1>and foreign keys.

15
00:00:44.880 --> 00:00:47.159
<v Speaker 2>Right. They are two completely different paradigms, and.

16
00:00:47.119 --> 00:00:50.960
<v Speaker 1>Bridging that gap manually usually means writing just endless, repetitive

17
00:00:50.960 --> 00:00:54.359
<v Speaker 1>seqle queries just to save affect your data. It completely

18
00:00:54.399 --> 00:00:56.479
<v Speaker 1>bogs down your whole workflow, it does.

19
00:00:56.600 --> 00:00:58.880
<v Speaker 2>I mean, that fundamental disconnect is honestly one of the

20
00:00:58.920 --> 00:01:02.799
<v Speaker 2>biggest hurdles in opp location development. Oh. Absolutely, You spend

21
00:01:02.799 --> 00:01:05.560
<v Speaker 2>all this energy building a domain model that represents your

22
00:01:05.599 --> 00:01:09.120
<v Speaker 2>business logic, only to have to write dozens of mapping.

23
00:01:08.840 --> 00:01:10.840
<v Speaker 1>Functions have to tear those objects.

24
00:01:10.439 --> 00:01:12.760
<v Speaker 2>Apart, exactly, you tear them apart just to fit them

25
00:01:12.760 --> 00:01:16.959
<v Speaker 2>into a relational database structure. It completely disrupts your focus.

26
00:01:17.079 --> 00:01:19.719
<v Speaker 1>You stop thinking about how your app should behave and

27
00:01:19.799 --> 00:01:23.200
<v Speaker 1>you start agonizing over like how to keep your memory

28
00:01:23.239 --> 00:01:25.319
<v Speaker 1>state synchronized with a hard drive.

29
00:01:25.120 --> 00:01:27.040
<v Speaker 2>Which is exactly what we want to avoid.

30
00:01:27.280 --> 00:01:30.400
<v Speaker 1>So for today's deep dive, we're looking at the solution

31
00:01:30.599 --> 00:01:34.400
<v Speaker 1>to that disconnect. We are talking about the doctrine ORM

32
00:01:34.879 --> 00:01:36.840
<v Speaker 1>or object relational mapper, a.

33
00:01:36.840 --> 00:01:39.719
<v Speaker 2>Life saver for PHP developers, truly.

34
00:01:40.319 --> 00:01:43.280
<v Speaker 1>We're going to decode how doctrine acts as the ultimate

35
00:01:43.799 --> 00:01:48.359
<v Speaker 1>highly skilled UN translator, working flawlessly between those two entirely

36
00:01:48.400 --> 00:01:49.159
<v Speaker 1>different languages.

37
00:01:49.200 --> 00:01:52.159
<v Speaker 2>Like that analogy a UN translator, right, And we'll.

38
00:01:52.079 --> 00:01:54.959
<v Speaker 1>Use a standard blog engine as our guiding example today

39
00:01:55.000 --> 00:01:57.439
<v Speaker 1>to see how it takes all that heavy lifting off

40
00:01:57.480 --> 00:01:57.840
<v Speaker 1>your plate.

41
00:01:57.920 --> 00:01:58.640
<v Speaker 2>Sounds perfect.

42
00:01:58.879 --> 00:02:01.040
<v Speaker 1>So to understand how doc doctrine pulls this off, we

43
00:02:01.079 --> 00:02:03.359
<v Speaker 1>have to start with the brain of the operation, which

44
00:02:03.439 --> 00:02:04.760
<v Speaker 1>is the entity manager.

45
00:02:04.879 --> 00:02:06.400
<v Speaker 2>Yeah, the core of doctrine.

46
00:02:06.599 --> 00:02:11.520
<v Speaker 1>Now, in doctrine, the actual data objects like your blog

47
00:02:11.560 --> 00:02:15.840
<v Speaker 1>posts or user profiles. Those are called entities. But these entities,

48
00:02:15.840 --> 00:02:17.919
<v Speaker 1>they're just plain old PHP objects.

49
00:02:18.080 --> 00:02:21.599
<v Speaker 2>Right, They don't extend some massive database library or anything.

50
00:02:21.759 --> 00:02:25.080
<v Speaker 1>No, they have absolutely no idea that a database even exists.

51
00:02:25.439 --> 00:02:28.280
<v Speaker 2>And you know that is very much by design. It

52
00:02:28.360 --> 00:02:31.560
<v Speaker 2>relies on a specific architectural pattern called the data mapper.

53
00:02:31.840 --> 00:02:34.879
<v Speaker 1>Okay, data mapper, How is that different from what other

54
00:02:34.919 --> 00:02:35.560
<v Speaker 1>frameworks do?

55
00:02:35.719 --> 00:02:38.280
<v Speaker 2>Well? In some other frameworks, you might see the active.

56
00:02:37.960 --> 00:02:41.199
<v Speaker 1>Record pattern, oh right, where the object itself has a save.

57
00:02:41.120 --> 00:02:44.520
<v Speaker 2>Mefit exactly, it talks directly to the database, but doctrine

58
00:02:44.520 --> 00:02:46.479
<v Speaker 2>deliberately separates those concerns.

59
00:02:46.520 --> 00:02:48.800
<v Speaker 1>So the entity's only job is to just hold your data.

60
00:02:48.960 --> 00:02:51.960
<v Speaker 2>Yes, and the entity manager is a completely separate service

61
00:02:52.000 --> 00:02:55.159
<v Speaker 2>that handles the really complex job of mapping that data

62
00:02:55.199 --> 00:02:55.960
<v Speaker 2>and saving it.

63
00:02:56.039 --> 00:02:57.400
<v Speaker 1>Using something called the unit of work.

64
00:02:57.479 --> 00:02:59.759
<v Speaker 2>Right, Yes, the unit of work. It's a crucial concept here.

65
00:02:59.879 --> 00:03:03.520
<v Speaker 1>The unit of work concept is honestly brilliant. I like

66
00:03:03.560 --> 00:03:06.199
<v Speaker 1>to think of the entity manager as a highly experienced

67
00:03:06.240 --> 00:03:08.800
<v Speaker 1>waiter taking your order at a really busy restaurant.

68
00:03:08.960 --> 00:03:09.919
<v Speaker 2>Okay, walk me through that.

69
00:03:10.039 --> 00:03:13.479
<v Speaker 1>Well, the application you're making changes. You tell the waiter

70
00:03:13.560 --> 00:03:17.039
<v Speaker 1>you want a steak, then you change your mind to chicken.

71
00:03:17.639 --> 00:03:20.360
<v Speaker 1>Then you add a side of fries that maybe cancel

72
00:03:20.439 --> 00:03:24.199
<v Speaker 1>a drink order. That waiter doesn't sprint back to the kitchen,

73
00:03:24.599 --> 00:03:27.199
<v Speaker 1>which is our database in this case, every single time

74
00:03:27.199 --> 00:03:27.960
<v Speaker 1>you open your mouth.

75
00:03:28.039 --> 00:03:29.800
<v Speaker 2>Now, if the waiter did that, the kitchen would be

76
00:03:29.800 --> 00:03:33.039
<v Speaker 2>completely overwhelmed exactly, which is exactly what happens when an

77
00:03:33.039 --> 00:03:37.919
<v Speaker 2>application fires off a new sequel query for every single

78
00:03:38.199 --> 00:03:39.680
<v Speaker 2>tiny variable chain.

79
00:03:39.719 --> 00:03:43.159
<v Speaker 1>It's chaos. So instead, the waiter stays at your table

80
00:03:43.360 --> 00:03:45.039
<v Speaker 1>writing everything down on their night pad.

81
00:03:45.199 --> 00:03:47.520
<v Speaker 2>And that notepad is the unit of work spot on.

82
00:03:47.759 --> 00:03:50.400
<v Speaker 1>It tracks all the modifications, the new additions, the items

83
00:03:50.439 --> 00:03:53.120
<v Speaker 1>you want removed. And it is only when you are

84
00:03:53.159 --> 00:03:56.039
<v Speaker 1>completely finished and the waiter walks over to the kitchen

85
00:03:56.199 --> 00:04:00.560
<v Speaker 1>enhance in that final consolidated ticket that the database actually

86
00:04:00.639 --> 00:04:01.319
<v Speaker 1>gets to work.

87
00:04:01.520 --> 00:04:04.599
<v Speaker 2>And in doctrine, handing in that ticket is done by

88
00:04:04.599 --> 00:04:08.840
<v Speaker 2>calling a specific method called flush flush. And what's fascinating

89
00:04:08.840 --> 00:04:11.280
<v Speaker 2>here is the optimization that happens when you call that

90
00:04:11.360 --> 00:04:14.199
<v Speaker 2>flush method. It's really the core power of doctrine.

91
00:04:14.280 --> 00:04:16.360
<v Speaker 1>You mean an example of that optimization.

92
00:04:15.960 --> 00:04:19.040
<v Speaker 2>Well, let's say you retrieve a blog post entity from

93
00:04:19.120 --> 00:04:22.000
<v Speaker 2>the database. In your code, you execute a change like

94
00:04:22.160 --> 00:04:25.720
<v Speaker 2>you set my entity name to my name. Then maybe

95
00:04:25.720 --> 00:04:29.519
<v Speaker 2>immediately after a different function changes that name again to

96
00:04:29.839 --> 00:04:30.560
<v Speaker 2>let's say.

97
00:04:30.519 --> 00:04:32.360
<v Speaker 1>Kevin, so change twice in memory.

98
00:04:32.480 --> 00:04:35.639
<v Speaker 2>Right, Doctrine's unit of work is tracking both of those

99
00:04:35.720 --> 00:04:38.920
<v Speaker 2>changes on its notepad. But when you finally call flush,

100
00:04:39.079 --> 00:04:42.040
<v Speaker 2>Doctrine doesn't waste time running two separate update queries.

101
00:04:42.160 --> 00:04:44.879
<v Speaker 1>Wait, really, it just skips the first one exactly.

102
00:04:45.120 --> 00:04:47.600
<v Speaker 2>It compares the original state of the object to its

103
00:04:47.680 --> 00:04:51.600
<v Speaker 2>final state, realizes only the final name matters, and issues

104
00:04:51.639 --> 00:04:55.040
<v Speaker 2>a single sequel query. Oh wow, yeah, just one update

105
00:04:55.079 --> 00:04:58.439
<v Speaker 2>my entity set name equals kevin where it equals thirteen

106
00:04:58.480 --> 00:05:00.639
<v Speaker 2>to twelve using prepared statements.

107
00:05:00.680 --> 00:05:01.879
<v Speaker 1>That is incredibly efficient.

108
00:05:02.000 --> 00:05:05.240
<v Speaker 2>It is, and it handles the other entity manager states

109
00:05:05.319 --> 00:05:09.240
<v Speaker 2>just as seamlessly, like find to retrieve an object, or

110
00:05:09.319 --> 00:05:12.279
<v Speaker 2>if you create a brand new entity, you use persist.

111
00:05:12.199 --> 00:05:15.199
<v Speaker 1>Which basically introduces it to the entity manager. Right tilling

112
00:05:15.240 --> 00:05:16.839
<v Speaker 1>Doctrine like, hey, watch this new.

113
00:05:16.720 --> 00:05:18.879
<v Speaker 2>Object exactly, and if you want to delete something, you

114
00:05:18.920 --> 00:05:19.959
<v Speaker 2>call removed.

115
00:05:19.639 --> 00:05:22.399
<v Speaker 1>So you just schedule it for deletion. Doctrine notes it

116
00:05:22.439 --> 00:05:24.399
<v Speaker 1>on the notepad and waits for the flesh.

117
00:05:24.519 --> 00:05:29.000
<v Speaker 2>Yes, and by sinking everything in one massive batch during

118
00:05:29.040 --> 00:05:32.360
<v Speaker 2>that flesh call, Doctrine can wrap all those changes in

119
00:05:32.399 --> 00:05:34.120
<v Speaker 2>a single database transaction.

120
00:05:34.439 --> 00:05:36.639
<v Speaker 1>Okay, why is that transaction part so important?

121
00:05:36.720 --> 00:05:40.000
<v Speaker 2>Because if anything goes wrong during the flesh, say you're

122
00:05:40.000 --> 00:05:43.000
<v Speaker 2>saving a new user, generating their profile, and creating a

123
00:05:43.040 --> 00:05:46.160
<v Speaker 2>welcome post, all at once, a lot of inserts, right,

124
00:05:46.480 --> 00:05:49.000
<v Speaker 2>and if the very last insertion fails because of a

125
00:05:49.040 --> 00:05:53.160
<v Speaker 2>network kickup, the database automatically rolls back to its exact

126
00:05:53.360 --> 00:05:54.199
<v Speaker 2>previous state.

127
00:05:54.519 --> 00:05:55.240
<v Speaker 1>Oh that's huge.

128
00:05:55.360 --> 00:05:59.519
<v Speaker 2>Yeah, keeping data perfectly consistent, you are completely protected from

129
00:05:59.600 --> 00:06:00.959
<v Speaker 2>ending up with half saved.

130
00:06:00.800 --> 00:06:03.920
<v Speaker 1>Data, which could corrupt your entire system. Okay, so the

131
00:06:04.040 --> 00:06:08.120
<v Speaker 1>entity manager is our waiter, perfectly batching our changes. But

132
00:06:08.160 --> 00:06:10.040
<v Speaker 1>it can only do that if it has a map.

133
00:06:10.800 --> 00:06:14.279
<v Speaker 1>It has to know that the title property in our

134
00:06:14.519 --> 00:06:18.639
<v Speaker 1>PHP object corresponds to a specific text column in our

135
00:06:18.639 --> 00:06:22.720
<v Speaker 1>SQL database. Naturally, but we just established that our entities

136
00:06:22.839 --> 00:06:27.879
<v Speaker 1>are plain PHP objects with no database logic. So where

137
00:06:27.879 --> 00:06:29.279
<v Speaker 1>does this map actually live?

138
00:06:29.639 --> 00:06:33.199
<v Speaker 2>So doctrine uses doc block annotations to build this map

139
00:06:33.319 --> 00:06:38.199
<v Speaker 2>annotations right, These are specially formatted comments placed directly inside

140
00:06:38.199 --> 00:06:41.600
<v Speaker 2>your PHP code, right above your classes and properties.

141
00:06:41.759 --> 00:06:44.519
<v Speaker 1>Wait, like literally in the code comments exactly.

142
00:06:44.560 --> 00:06:47.879
<v Speaker 2>You use specific tags like at entity or at table

143
00:06:48.000 --> 00:06:50.600
<v Speaker 2>above your class to declare it as a database table,

144
00:06:51.079 --> 00:06:54.959
<v Speaker 2>and tags like at column at id or at generated

145
00:06:55.079 --> 00:06:58.079
<v Speaker 2>value above your properties to define exactly how they map

146
00:06:58.120 --> 00:06:58.800
<v Speaker 2>to the columns.

147
00:06:58.879 --> 00:07:02.160
<v Speaker 1>Here's where it gets really interesting, though, because putting database

148
00:07:02.199 --> 00:07:06.720
<v Speaker 1>configurations right inside PHP comments doesn't that clutter up the code?

149
00:07:07.000 --> 00:07:10.160
<v Speaker 1>I mean, because my entity is suddenly deeply aware of

150
00:07:10.199 --> 00:07:13.439
<v Speaker 1>my database schema. We're usually taught to keep those things

151
00:07:13.480 --> 00:07:14.759
<v Speaker 1>strictly separated, right.

152
00:07:14.680 --> 00:07:16.879
<v Speaker 2>Yeah, I mean that's a very common point of friction

153
00:07:16.959 --> 00:07:20.319
<v Speaker 2>for developers transitioning to doctrine. I can imagine the purest

154
00:07:20.480 --> 00:07:23.720
<v Speaker 2>view is that your domain objects should be completely oblivious

155
00:07:23.759 --> 00:07:27.160
<v Speaker 2>to how they're stored, maybe using separate XML or Yammel

156
00:07:27.240 --> 00:07:32.240
<v Speaker 2>configuration files right. But practically speaking, keeping the mapping information

157
00:07:32.399 --> 00:07:36.680
<v Speaker 2>right next to the code vastly improves readability and maintenance.

158
00:07:36.319 --> 00:07:37.519
<v Speaker 1>Because it's right there in front of you.

159
00:07:37.600 --> 00:07:41.120
<v Speaker 2>Exactly when you look at a property, you instantly know

160
00:07:41.199 --> 00:07:43.639
<v Speaker 2>how it behaves in the database. You don't have to

161
00:07:43.720 --> 00:07:46.639
<v Speaker 2>hunt through massive external files to see if a string

162
00:07:46.680 --> 00:07:47.319
<v Speaker 2>can be null.

163
00:07:47.600 --> 00:07:49.279
<v Speaker 1>I guess I could see the practical trade off there,

164
00:07:49.480 --> 00:07:52.759
<v Speaker 1>And the way doctrine handles mapping types is pretty cool too. Oh.

165
00:07:52.879 --> 00:07:55.639
<v Speaker 2>The type casting is a massive timesaver.

166
00:07:55.519 --> 00:08:00.319
<v Speaker 1>Because doctrine types aren't purely PHP, and they are purely SQL.

167
00:08:00.199 --> 00:08:03.480
<v Speaker 2>Right right. They act as a transparent bridge. For instance,

168
00:08:03.639 --> 00:08:07.279
<v Speaker 2>Doctrine's text type becomes a standard string in your PHP.

169
00:08:06.959 --> 00:08:09.519
<v Speaker 1>App, but in the SQL database.

170
00:08:09.279 --> 00:08:12.839
<v Speaker 2>It's automatically stored as a c lobrie, a character large

171
00:08:12.879 --> 00:08:15.920
<v Speaker 2>object suitable for massive blocks of texts.

172
00:08:15.959 --> 00:08:17.639
<v Speaker 1>That's so seamless it is.

173
00:08:18.360 --> 00:08:20.240
<v Speaker 2>But we do need to point out a really critical

174
00:08:20.279 --> 00:08:23.879
<v Speaker 2>limitation here. While Doctrine handles the mapping, it does not

175
00:08:23.920 --> 00:08:27.279
<v Speaker 2>do data validation. If your annotation says a column has

176
00:08:27.319 --> 00:08:31.040
<v Speaker 2>a max length of fifty characters, Doctrine builds the database

177
00:08:31.079 --> 00:08:34.360
<v Speaker 2>column to that exact spec. But if a user submits

178
00:08:34.360 --> 00:08:37.759
<v Speaker 2>one hundred characters, Doctrine won't stop them. It won't stop them,

179
00:08:38.080 --> 00:08:39.960
<v Speaker 2>it will try to save it, and the database will

180
00:08:39.960 --> 00:08:42.919
<v Speaker 2>throw a fatal error. You still have to validate user

181
00:08:42.960 --> 00:08:44.759
<v Speaker 2>inputs separately, So it's.

182
00:08:44.679 --> 00:08:46.799
<v Speaker 1>A mapp or not a bouncer. Yeah, good to know.

183
00:08:47.480 --> 00:08:50.759
<v Speaker 1>But honestly, the absolute best part of defining all those

184
00:08:50.799 --> 00:08:52.919
<v Speaker 1>annotations is the command line magic.

185
00:08:53.039 --> 00:08:54.240
<v Speaker 2>Oh, the schema tool.

186
00:08:54.519 --> 00:08:58.200
<v Speaker 1>Yeah, by running I think it's a an orm colon

187
00:08:58.720 --> 00:09:01.639
<v Speaker 1>schema tool Colon create, that's the one. Doctrine reads all

188
00:09:01.639 --> 00:09:06.240
<v Speaker 1>those annotations and automatically generates the entire underlying database schema.

189
00:09:06.000 --> 00:09:07.840
<v Speaker 2>Cables, columns, primary.

190
00:09:07.480 --> 00:09:10.559
<v Speaker 1>Keys, without the developer writing a single line of sequel.

191
00:09:11.159 --> 00:09:12.919
<v Speaker 1>It completely removes the busy work.

192
00:09:13.000 --> 00:09:15.240
<v Speaker 2>It's incredible for bootstrapping a new project.

193
00:09:15.399 --> 00:09:18.200
<v Speaker 1>Okay, so mapping a single blog post is straightforward, but

194
00:09:18.279 --> 00:09:21.480
<v Speaker 1>things get complicated fast when we add comments and tags right.

195
00:09:21.600 --> 00:09:24.240
<v Speaker 2>Enterprise apps aren't just isolated tables exactly.

196
00:09:24.720 --> 00:09:27.600
<v Speaker 1>Relational databases use foreign keys and joining tables for this.

197
00:09:28.320 --> 00:09:31.200
<v Speaker 1>How does doctrine handle these complex webs well.

198
00:09:31.279 --> 00:09:36.919
<v Speaker 2>It manages them through association types, also defined using annotations

199
00:09:37.320 --> 00:09:40.960
<v Speaker 2>like at onetominy for a post to comments relationship.

200
00:09:40.480 --> 00:09:43.120
<v Speaker 1>Or at minutomany for posts to tags exactly.

201
00:09:43.559 --> 00:09:46.200
<v Speaker 2>But to make these relationships work, you have to understand

202
00:09:46.200 --> 00:09:49.120
<v Speaker 2>a concept that trips up almost everyone.

203
00:09:49.200 --> 00:09:52.240
<v Speaker 1>At first, let me guess the owning side versus the

204
00:09:52.279 --> 00:09:53.000
<v Speaker 1>inverse side.

205
00:09:53.080 --> 00:09:56.440
<v Speaker 2>Yes, using the inverse buy and mapped by attributes, it

206
00:09:56.480 --> 00:09:58.200
<v Speaker 2>is a notorious stumbling block.

207
00:09:58.440 --> 00:10:00.679
<v Speaker 1>It totally is. I like to use a dog leash

208
00:10:00.720 --> 00:10:01.559
<v Speaker 1>analogy for this one.

209
00:10:01.679 --> 00:10:02.399
<v Speaker 2>Okay, let's hear it.

210
00:10:02.399 --> 00:10:04.759
<v Speaker 1>So the owning side is like holding the leash of

211
00:10:04.759 --> 00:10:07.840
<v Speaker 1>a dog. The dog, which is the inverse side, might

212
00:10:07.919 --> 00:10:10.279
<v Speaker 1>know it belongs to you. But if you don't actually

213
00:10:10.360 --> 00:10:13.799
<v Speaker 1>grab the leash, meaning setting the owning side and the

214
00:10:13.840 --> 00:10:16.000
<v Speaker 1>codoctrine won't save the connection.

215
00:10:16.320 --> 00:10:18.080
<v Speaker 2>That's a great way to put it because the crucial

216
00:10:18.120 --> 00:10:20.639
<v Speaker 2>rule is that doctrine only manages the owning side of

217
00:10:20.679 --> 00:10:23.759
<v Speaker 2>an association. So if you have a method like ad

218
00:10:23.799 --> 00:10:26.679
<v Speaker 2>comment on your post entity, you can't just push the

219
00:10:26.720 --> 00:10:30.600
<v Speaker 2>comment into an array. That method must include a line

220
00:10:30.720 --> 00:10:32.360
<v Speaker 2>like comment arrow set post.

221
00:10:32.840 --> 00:10:35.679
<v Speaker 1>You have to explicitly grab the leash exactly.

222
00:10:36.080 --> 00:10:38.879
<v Speaker 2>If you don't, the relationship evaporates when you flush.

223
00:10:39.159 --> 00:10:42.720
<v Speaker 1>And speaking of arrays, doctrine doesn't actually use standard PHP

224
00:10:42.879 --> 00:10:45.000
<v Speaker 1>arrays for these relationships right now.

225
00:10:45.080 --> 00:10:47.399
<v Speaker 2>It uses a special class called array collection.

226
00:10:48.039 --> 00:10:50.360
<v Speaker 1>Why is that If it's just a list of comments,

227
00:10:50.360 --> 00:10:52.000
<v Speaker 1>why not just use a normal array?

228
00:10:52.840 --> 00:10:57.519
<v Speaker 2>Because a normal array is completely passive. Doctrine needs to

229
00:10:57.559 --> 00:11:00.000
<v Speaker 2>track the internal state of that collection.

230
00:10:59.759 --> 00:11:02.240
<v Speaker 1>Oh, to know what to sync with the database.

231
00:11:02.399 --> 00:11:05.720
<v Speaker 2>Right it acts like a standard PHP array, but provides

232
00:11:05.799 --> 00:11:08.159
<v Speaker 2>the hidden hooks. Doctrine needs to figure out what to

233
00:11:08.279 --> 00:11:09.840
<v Speaker 2>update or delete.

234
00:11:09.440 --> 00:11:13.039
<v Speaker 1>And that enables some really cool features like orphan removal equals.

235
00:11:12.720 --> 00:11:16.159
<v Speaker 2>True yes, automatically deleting tags that are no longer linked

236
00:11:16.159 --> 00:11:17.120
<v Speaker 2>to any post.

237
00:11:17.080 --> 00:11:18.960
<v Speaker 1>Or cascade equals persist.

238
00:11:18.720 --> 00:11:21.399
<v Speaker 2>Or saving a post automatically saves all the brand new

239
00:11:21.399 --> 00:11:22.440
<v Speaker 2>comments attached to it.

240
00:11:22.799 --> 00:11:25.480
<v Speaker 1>But if we connect this to the bigger picture, all

241
00:11:25.519 --> 00:11:28.559
<v Speaker 1>these webs of objects must impact performance.

242
00:11:28.120 --> 00:11:32.519
<v Speaker 2>Right massively, especially regarding how the data is loaded. By default,

243
00:11:32.759 --> 00:11:35.720
<v Speaker 2>Doctrine uses lazy loading, meaning.

244
00:11:35.480 --> 00:11:38.279
<v Speaker 1>It only fetches the relations when you explicitly ask for them.

245
00:11:38.440 --> 00:11:41.159
<v Speaker 2>Right, If you ask for a post, it leaves the

246
00:11:41.159 --> 00:11:44.559
<v Speaker 2>comments behind. If your code asks for them later, it

247
00:11:44.639 --> 00:11:47.720
<v Speaker 2>quietly fires off a second query to fetch them, which.

248
00:11:47.559 --> 00:11:50.000
<v Speaker 1>Saves memory initially. But what if I know I need

249
00:11:50.039 --> 00:11:52.159
<v Speaker 1>all the comments? Isn't that second query wasteful?

250
00:11:52.320 --> 00:11:55.559
<v Speaker 2>It is? That's the classic N plus one query problem.

251
00:11:55.919 --> 00:11:58.559
<v Speaker 2>To fix it, you switch to eager loading using fetch

252
00:11:58.639 --> 00:12:01.000
<v Speaker 2>equals eager in your annotations, so.

253
00:12:00.960 --> 00:12:03.960
<v Speaker 1>It fishes them immediately in one big joint exactly. Okay,

254
00:12:03.960 --> 00:12:07.440
<v Speaker 1>so we successfully save these interconnected webs of objects, but

255
00:12:07.519 --> 00:12:10.320
<v Speaker 1>how do we get them back out efficiently without resorting

256
00:12:10.360 --> 00:12:11.200
<v Speaker 1>to raw SQL.

257
00:12:11.399 --> 00:12:13.279
<v Speaker 2>We need a new way to ask for our data,

258
00:12:13.440 --> 00:12:17.480
<v Speaker 2>and that is Doctrine query language or BIKIL. Yes, and

259
00:12:17.519 --> 00:12:20.519
<v Speaker 2>the massive paradigm shift here is that DQL queries the

260
00:12:20.639 --> 00:12:23.600
<v Speaker 2>object models entities, not the database tables.

261
00:12:23.679 --> 00:12:27.240
<v Speaker 1>Right, So you're not writing select star from posts left

262
00:12:27.320 --> 00:12:28.159
<v Speaker 1>joint comments on.

263
00:12:28.399 --> 00:12:32.480
<v Speaker 2>Now you write something like select pc from blog slash

264
00:12:32.600 --> 00:12:36.279
<v Speaker 2>entity slash post p lft joinp dot comments c.

265
00:12:36.840 --> 00:12:39.320
<v Speaker 1>That is wild. You just use the properties.

266
00:12:39.360 --> 00:12:42.840
<v Speaker 2>It's extremely powerful. And to keep these organized, Doctrine uses

267
00:12:42.960 --> 00:12:44.639
<v Speaker 2>entity repositories, which.

268
00:12:44.440 --> 00:12:47.159
<v Speaker 1>Is the table data gateway pattern exactly.

269
00:12:47.279 --> 00:12:50.039
<v Speaker 2>The base repositories give you magic methods like fine by

270
00:12:50.120 --> 00:12:52.200
<v Speaker 2>title where you don't even write query logic.

271
00:12:52.399 --> 00:12:55.200
<v Speaker 1>But for complex stuff we use custom repositories and the

272
00:12:55.279 --> 00:12:55.879
<v Speaker 1>career builder.

273
00:12:55.960 --> 00:12:58.960
<v Speaker 2>Right. Yes, you build a query by calling methods like create,

274
00:12:59.080 --> 00:13:04.000
<v Speaker 2>query builder left, join ad select. It translates seamlessly into SQL.

275
00:13:04.519 --> 00:13:07.200
<v Speaker 1>So what does this all mean for security? Because I'm

276
00:13:07.240 --> 00:13:10.600
<v Speaker 1>thinking about SQL injection? Like if someone types quote r

277
00:13:10.799 --> 00:13:13.039
<v Speaker 1>quote a quote equals quote.

278
00:13:12.759 --> 00:13:14.759
<v Speaker 2>A ah, the classic exploit.

279
00:13:14.879 --> 00:13:16.480
<v Speaker 1>Right, does the query builders stop that?

280
00:13:16.720 --> 00:13:20.559
<v Speaker 2>Absolutely? The query builders set parameter method acts as a

281
00:13:20.600 --> 00:13:24.679
<v Speaker 2>built in security guard. It automatically escapes inputs some malicious

282
00:13:24.720 --> 00:13:25.960
<v Speaker 2>code can't sneak through.

283
00:13:26.120 --> 00:13:27.080
<v Speaker 1>Oh that's such relief.

284
00:13:27.200 --> 00:13:31.559
<v Speaker 2>And to boost performance, Doctrine caches these generated DQL queries.

285
00:13:31.600 --> 00:13:34.480
<v Speaker 2>It parses at once, cashes the sequel, and just swaps

286
00:13:34.519 --> 00:13:35.600
<v Speaker 2>the parameters next time.

287
00:13:35.799 --> 00:13:36.120
<v Speaker 1>Wow.

288
00:13:36.240 --> 00:13:39.559
<v Speaker 2>You can even use aggregate functions like count dc dot

289
00:13:39.600 --> 00:13:42.799
<v Speaker 2>I to return custom two dimensional arrays, like getting a

290
00:13:42.799 --> 00:13:44.480
<v Speaker 2>post with its total comment count.

291
00:13:44.639 --> 00:13:47.759
<v Speaker 1>Okay, so we've mathtered standard relations, but let's push the limits.

292
00:13:48.200 --> 00:13:51.000
<v Speaker 1>What happens when our data model hits a classic OOP

293
00:13:51.279 --> 00:13:54.080
<v Speaker 1>concept that relational databases despise.

294
00:13:54.559 --> 00:13:59.320
<v Speaker 2>You mean inheritance inheritance? Yea, relational databases really hate inheritance, right?

295
00:13:59.360 --> 00:14:01.159
<v Speaker 1>So if I have a post author and a common

296
00:14:01.159 --> 00:14:06.159
<v Speaker 1>author that both extend an abstract auter class, how does

297
00:14:06.200 --> 00:14:07.039
<v Speaker 1>doctrine handle that?

298
00:14:07.320 --> 00:14:11.720
<v Speaker 2>It offers three inheritance strategies. The first is mapped superclasses.

299
00:14:11.879 --> 00:14:12.519
<v Speaker 1>Okay, what's that?

300
00:14:13.120 --> 00:14:16.240
<v Speaker 2>It's good for sharing properties in your PHP code, but

301
00:14:16.440 --> 00:14:20.440
<v Speaker 2>at the database level, the child classes get completely separate.

302
00:14:20.080 --> 00:14:22.960
<v Speaker 1>Tables, so no real relation in the dB. What's the second?

303
00:14:23.039 --> 00:14:27.279
<v Speaker 2>Single table inheritance. It puts everyone in one massive table,

304
00:14:27.559 --> 00:14:30.960
<v Speaker 2>using a discriminator column usually called D type to tell

305
00:14:31.000 --> 00:14:31.480
<v Speaker 2>them apart.

306
00:14:31.679 --> 00:14:33.639
<v Speaker 1>I love my closet analogy for this one, but you're

307
00:14:33.679 --> 00:14:36.960
<v Speaker 1>the closets. So single table inheritance is like throwing everyone's

308
00:14:36.960 --> 00:14:40.360
<v Speaker 1>clothes into one giant closet, but slapping a sticky note

309
00:14:40.399 --> 00:14:41.759
<v Speaker 1>like the D type on them.

310
00:14:42.000 --> 00:14:44.960
<v Speaker 2>Highly performant, but a bit messy because.

311
00:14:44.679 --> 00:14:47.039
<v Speaker 1>You have a lot of empty space for properties that

312
00:14:47.039 --> 00:14:50.840
<v Speaker 1>don't apply to everyone exactly. But the third strategy class

313
00:14:50.840 --> 00:14:54.360
<v Speaker 1>table inheritance. Yeah, that's like giving everyone their own separate closet. Right.

314
00:14:54.559 --> 00:14:56.039
<v Speaker 2>Each class gets its own table.

315
00:14:56.240 --> 00:14:58.120
<v Speaker 1>But if you want to put together an outfit, you

316
00:14:58.200 --> 00:15:00.120
<v Speaker 1>have to open all the closets, which.

317
00:15:00.000 --> 00:15:04.360
<v Speaker 2>Which means your queries require massive joy and ends. It's flexible,

318
00:15:04.519 --> 00:15:05.559
<v Speaker 2>but much slower.

319
00:15:05.960 --> 00:15:08.799
<v Speaker 1>This raises an important question, which one should I actually use?

320
00:15:09.120 --> 00:15:12.480
<v Speaker 2>Well, it's about architectural trade offs. Single table is usually

321
00:15:12.519 --> 00:15:16.000
<v Speaker 2>best for performance. Class table is really reserved for highly

322
00:15:16.039 --> 00:15:19.519
<v Speaker 2>complex models where performance isn't the primary bottleneck.

323
00:15:19.799 --> 00:15:22.960
<v Speaker 1>Makes sense now real quick? Before we wrap, we have

324
00:15:23.039 --> 00:15:24.519
<v Speaker 1>to mention doctrine's event.

325
00:15:24.399 --> 00:15:27.200
<v Speaker 2>System AH life cycle events right.

326
00:15:27.159 --> 00:15:30.080
<v Speaker 1>Like using at hass life cycle callbacks and at pre

327
00:15:30.159 --> 00:15:34.200
<v Speaker 1>persist to automatically stamp a publication date right before insertion.

328
00:15:34.559 --> 00:15:38.480
<v Speaker 2>It's very handy, but be careful. More complex logic like

329
00:15:38.480 --> 00:15:41.519
<v Speaker 2>an insult event listener that scans for bad words or

330
00:15:41.600 --> 00:15:44.000
<v Speaker 2>emailing an author, look at that in the entity exactly,

331
00:15:44.080 --> 00:15:48.000
<v Speaker 2>Keep entities clean. That logic belongs in external event subscribers.

332
00:15:48.159 --> 00:15:51.080
<v Speaker 1>Good rule of thumb. Wow, Okay, we've journeyed from the

333
00:15:51.200 --> 00:15:54.639
<v Speaker 1>raw connection of PHP to the elegant abstraction of doctrine.

334
00:15:54.639 --> 00:15:56.759
<v Speaker 2>Today we covered a lot of ground we did.

335
00:15:56.919 --> 00:16:00.639
<v Speaker 1>We saw how the Entity Manager Association's DQL and advanced

336
00:16:00.679 --> 00:16:04.000
<v Speaker 1>inheritance make handling data an object oriented dream.

337
00:16:04.240 --> 00:16:08.799
<v Speaker 2>And for you listening, this means faster development, cleaner code bases,

338
00:16:09.159 --> 00:16:11.399
<v Speaker 2>and letting the framework handle the heavy lifting.

339
00:16:11.480 --> 00:16:14.919
<v Speaker 1>Yeah, database, synchronization, security, you name it. You can just

340
00:16:15.000 --> 00:16:18.360
<v Speaker 1>focus on building features rather than writing boilerplate sequel.

341
00:16:18.519 --> 00:16:22.399
<v Speaker 2>But consider this, doctrine does provide native queries and DBL

342
00:16:22.440 --> 00:16:28.080
<v Speaker 2>access for when you need raw database specific power, right, which.

343
00:16:27.840 --> 00:16:30.960
<v Speaker 1>Makes me think, As ORMs become more magical and do

344
00:16:31.000 --> 00:16:33.679
<v Speaker 1>more of the thinking for us, do we risk losing

345
00:16:33.720 --> 00:16:36.919
<v Speaker 1>our fundamental understanding of what makes it database actually performant

346
00:16:36.960 --> 00:16:37.519
<v Speaker 1>under the hood.

347
00:16:37.639 --> 00:16:38.639
<v Speaker 2>It's a valid concern.

348
00:16:38.759 --> 00:16:42.879
<v Speaker 1>Does the ultimate abstraction eventually abstract away our core engineering skills?

349
00:16:42.879 --> 00:16:45.159
<v Speaker 1>Something for you to ponder before your next deep dive.
