“All the Word that's fit to Press”

For the last couple weeks, I’ve talked about creating database abstractions. In the first article, I spoke about the need for creating a high-level API, on top of standard WordPress APIs to act as a CRUD interface for your projects. The second was about using classes with all static methods for validation and storage of options. In part three, I want to talk about dependency injection and illustrate the value of this concept by offering a different way to create a database abstraction than I did before.

What I showed in the last article works for its purpose. But, that system was strongly coupled to the options API and required a new class declaration for each option. Because of the use of class inheritance, there is little redundancy in code, but that approach has limited usage.

What Is Dependency Injection

Before we begin, we must go over what a dependency injection is.

Basically, it is a fancy sounding term that just means that a class takes the data it needs to function from outside of the class, instead of constructing this data itself. Dependency injections are helpful for several reasons.

A dependency injection encourages following the single responsibility principle. You might think “this class outputs popular posts in the footer of a post” when it actually assembles a WP_Query object based on the current HTTP request, queries the database, and outputs some HTML on the_content filter.

That’s a lot of responsibilities for one class. Here is the kind of class I’m talking about:

PHP

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

classpopular_posts{

publicfunction__construct(){

if(!is_admin()&&is_single()){

add_filter('the_content',[$this,'output']);

}

}

publicfunctioncontent($content){

$posts=$this->query()->posts;

ob_start();

include'path/to/view.php';

$view=ob_get_clean();

return$content.$view;

}

protectedfunctionpost_type(){

returnget_post_type();

}

protectedfunctionquery_args(){

return[

'orderby'=>'comment_count',

'post_type'=>$this->post_type(),

'posts_per_page'=>5

];

}

protectedfunctionquery(){

returnnewWP_Query($this->query_args());

}

}

newpopular_posts;

This class does not use dependency injection. As a result, it’s basically impossible to write a unit test for this class. Also, if you wanted to add AJAX based pagination, you’d be in trouble. If you wanted to reuse this class with a different view or post type, you’d have to rewrite it quite a bit. What if you wanted to use the same query, but return the posts as JSON?

This is the beauty of dependency injection. We can inject our query arguments and the view path into this class. Then we can take the HTML it generates and use that as a dependency of another class. This gives us a modular, testable, and reusable system.

Here is a refactored example of this class that takes the WP_Query arguments and the path for the view file as dependencies:

PHP

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

classpopular_posts{

protected$html;

protected$query_args;

protected$view_path;

publicfunction__construct(array$query_args,$view_path){

$this->args=$query_args;

$this->view_path=$view_path;

}

publicfunctionget_html(){

return$this->html;

}

publicfunctionmake_html(){

$posts=$this->query()->post;

ob_start();

include$this->view_path;

$view=ob_get_clean();

return$view;

}

protectedfunctionquery(){

returnnewWP_Query($this->query_args);

}

}

Now this class can be used with different query arguments or layouts. We just make a new instance with different values passed to the constructor. Notice that this class does not add any hooks. The HTML property can be accessed by another class whose responsibility is to hook to the_content filter.

This is an example of constructor dependency injection because the dependencies are injected in the constructor. We could also have added a public set_query() method that took a WP_Query object or an array of WP_Query arguments. That would be an example of method injection.

The third type of dependency injection is property injection. In this case, class properties are intentionally left public to allow for dependencies to be set on them externally. This is probably the least useful form of dependency injection because it can lead to the wrong type of dependency being injected.

I normally prefer constructor dependency injection. In some cases, a class needs an array of objects of a specific class. In that case, method injection is better, as you can take one object at a time and ensure it is correct using type hinting or other validation.

Let’s start with an example database abstraction for data stored as posts and posts meta:

PHP

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

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

abstractclassPostType_Store{

/**

* Post object for queried post

*

* @var WP_Post

*/

protected$post;

/** @var string */

protected$name;

/** @var string */

protected$title;

/** @var string */

protected$content;

/**

* Create object

*

* @param int $id Post ID. Optional. If ID is passed, post will be queried and set in post property

This class makes use of both constructor dependency injection and method dependency injection. All of the class properties can be set via the magic __get() method, and retrieved via the magic __set() method. We inject the ID of the post into the constructor and let the class assemble the data we need from post and post_meta.

Here is an example class that extends this to implement the system:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

classitemsextendsPostType_Store{

protected$size;

protected$year;

/**

* @inheritdoc

*/

protectedfunctionget_post_meta(){

return[

'size'=>$this->size,

'year'=>$this->year,

];

}

/**

* @inheritdoc

*/

protectedfunctionpost_type_name(){

return'slug_items';

}

}

This code is still completely tied to the way that the WP_Post class functions and stores data. I’m OK with that in this case because post and post meta storage is pretty specific to WordPress and hard to escape without moving to custom tables.

Despite the strong coupling I still like this because it makes the WordPress APIs invisible outside the encapsulation of this class. Look at this example of how we would use our example implementation:

PHP

1

2

3

4

5

6

7

8

9

10

//create a new item

$big_item=newitems();

$big_item->size='large';

$big_item->year='2008';

$big_item->title='Big old 2008 thing';

$big_item->save();

//get size of saved item

$true_item=newitems(42);

echo$true_item->size;

In this code I used various properties of the class, via the magic __get() and __set() methods to create a new item in the database as well as to query for and echo data. The data is stored in a post, which is invisible to the outside world.

If I wrote a plugin using this system and in a later version to store data as posts and then later I switched to storing data in custom tables, that code would not break. Yes, I’d need to rewrite the internals of the class and create a migration system. But, all the code using that database abstraction would still function the same way.

The Power Of Invisibility

In my first article in this series, I talked about the reason behind making your own database abstractions. A good database abstraction makes the API that interfaces with WordPress and by extension the database invisible. By making these APIs “invisible” the way they function becomes irrelevant to the outside user.

A good database abstraction is essentially a black box. Data goes in, data goes out and we don’t really know why or how. We don’t need to know why and how. It just works and we can build on top of it any way we want to.

I hope in this article and the last two you have shown the value of creating database abstractions on top of WordPress APIs. The sooner you adopt a database abstraction in your work, the more stable your project will be and the easier it will be to make a change to how you store data in the future. If you want to replace options or post/ post meta storage with custom table storage, then that will be easy. If you do, be sure to read Pippin Williamson’s excellent series on creating custom table APIs that begins here.

Josh is a WordPress developer and educator. He is the founder of Caldera Labs, makers of awesome WordPress tools including Caldera Forms — a drag and drop, responsive WordPress form builder. He teaches WordPress development at Caldera Learn.

There are no comments

Start the conversation

Love it? Hate it? Tell us what you think by leaving a comment!And, don't worry, your email address will not be published.

CommentPlease enter a comment

Name *Please enter your name

Email *Please enter your email

Website

Continue the conversation via email!Get only replies to your comment, the best of the rest, as well as a daily recap of all comments on this post. No more than a few emails daily, which you can reply to/unsubscribe from directly from your inbox.

Torque is a news site featuring all things WordPress. We are dedicated to informing new and advanced WordPress professionals about the industry. Check in daily for tutorials, interviews, news, and more.