1Maypole::Manual::InheriUtsaenrceC(o3n)tributed Perl DocuMmaeynptoaltei:o:nManual::Inheritance(3)
2
3
4
6 Maypole::Manual::Inheritance - structure of a Maypole application
7
9 Discusses the inheritance structure of a basic and a more advanced
10 Maypole application.
11
13 inheritance
14 +
15 |
16 +- -+
17 |
18 +
19
20 notes
21 target *-------- note about the target
22
23 association
24 source ------> target
25
27 A minimal Maypole application (such as the Beer database example from
28 the Maypole synopsis) consists of a custom driver (or controller) class
29 (BeerDB.pm), a set of auto-generated model classes, and a view class:
30
31 THE DRIVER
32 +------- init() is a factory method,
33 1 Maypole | it sets up the view
34 Maypole::Config <----- config(); | classes
35 model(); init(); *-------+ THE VIEW
36 | view_object(); -------+
37 | +--------------* setup(); | Maypole::View::Base
38 | | + | +
39 | | | | 1 |
40 | | PLUGINS Apache::MVC *-----+ +-----> Maypole::View::TT
41 | | + + | (or another view class)
42 | | | | |
43 | | +-----+-----+ |
44 | | | |
45 | | BeerDB +----- or CGI::Maypole
46 | | or MasonX:::Maypole
47 | |
48 | setup() is a factory method,
49 | it sets up the model
50 | classes
51 |
52 | THE MODEL
53 |
54 | Maypole::Model::Base Class::DBI
55 | + + +
56 | | | |
57 +-------> Maypole::Model::CDBI Class::DBI::<db_driver>
58 + +
59 | |
60 +------------+--------+-------+---------+
61 | | | | |
62 BeerDB::Pub | BeerDB::Beer | BeerDB::Brewery
63 beers(); | pubs(); | beers();
64 | brewery(); |
65 | style(); |
66 BeerDB::Handpump |
67 pub(); BeerDB::Style
68 beer(); beers();
69
70 Ouch, that's a lot of inheritence!
71 Yes, that's a lot of inheritence, at some point in the future -
72 probably Maypole 3.x we will move to Class::C3
73
74 What about Maypole::Application - loading plugins
75 The main job of Maypole::Application is to insert the plugins into the
76 hierarchy. It is also the responsibility of Maypole::Application to
77 decide which frontend to use. It builds the list of plugins, then
78 pushes them onto the driver's @ISA, then pushes the frontend onto the
79 end of the driver's @ISA. So method lookup first searches all the
80 plugins, before searching the frontend and finally Maypole itself.
81
82 From Maypole 2.11, Maypole::Application makes no appearance in the
83 inheritance structure of a Maypole application. (In prior versions,
84 Maypole::Application would make itself inherit the plugins, and then
85 insert itself in the hierarchy, but this was unnecessary).
86
87 Who builds the model?
88 First, remember we are talking about the standard, unmodified Maypole
89 here. It is possible, and common, to override some or all of this stage
90 and build a customised model. See below - An advanced Maypole
91 application - for one approach. Also, see Maypole's "setup_model()"
92 method.
93
94 The standard model is built in 3 stages.
95
96 First, "Maypole::setup_model" calls "setup_database" on the Maypole
97 model class, in this case Maypole::Model::CDBI. "setup_database" then
98 uses Class::DBI::Loader to autogenerate individual Class::DBI classes
99 for each of the tables in the database ("BeerDB::Beer", "BeerDB::Pub"
100 etc). Class::DBI::Loader identifies the appropriate Class::DBI
101 subclass and inserts it into each of these table classes' @ISA (
102 "Class::DBI::<db_driver>" in the diagrams)..
103
104 Next, "Maypole::setup" pushes Maypole::Model::CDBI onto the @ISA array
105 of each of these classes.
106
107 Finally, the relationships among these tables are set up. Either do
108 this manually, using the standard Class::DBI syntax for configuring
109 table relationships, or try Class::DBI::Relationship (which you can use
110 via Maypole::Plugin::Relationship). If you use the plugin, you need to
111 set up the relationships configuration before calling "setup()". Be
112 aware that some people like the convenience of
113 Class::DBI::Relationship, others dislike the abstraction. YMMV.
114
116 We'll call it "BeerDB2".
117
118 Maypole is a framework, and you can replace different bits as you wish.
119 So what follows is one example of good practice, other people may do
120 things differently.
121
122 We assume this application is being built from the ground up, but it
123 will often be straightforward to adapt an existing Class::DBI
124 application to this general model.
125
126 The main idea is that the autogenerated Maypole model is used as a
127 layer on top of a separate Class::DBI model. I am going to refer to
128 this model as the 'Offline' model, and the Maypole classes as the
129 'Maypole' model. The idea is that the Offline model can (potentially or
130 in actuality) be used as part of another application, perhaps a command
131 line program or a cron script, whatever. The Offline model does not
132 know about the Maypole model, whereas the Maypole model does know about
133 the Offline model.
134
135 Let's call the offline model "OfflineBeer". As a traditional Class::DBI
136 application, individual table classes in this model will inherit from a
137 common base ("OfflineBeer"), which inherits from Class::DBI).
138
139 One advantage of this approach is that you can still use Maypole's
140 autogenerated model. Another is that you do not mix online and offline
141 code in the same packages.
142
143 Building it
144 Build a driver in a similar way as for the basic app, calling "setup()"
145 after setting up all the configuration.
146
147 It is a good habit to use a custom Maypole model class for each
148 application, as it's a likely first target for customisation. Start it
149 like this:
150
151 package BeerDB2::Maypole::Model;
152 use strict;
153 use warnings;
154 use base 'Maypole::Model::CDBI';
155 1;
156
157 You can add methods which should be shared by all table classes to this
158 package as and when required.
159
160 Configure it like this, before the "setup()" call in the driver class:
161
162 # in package BeerDB2
163 __PACKAGE__->config->model('BeerDB2::Maypole::Model');
164 __PACKAGE__->setup;
165
166 The "setup()" call will ensure your custom model is loaded via
167 "require".
168
169 Note: by default, this will create Maypole/CDBI classes for all the
170 tables in the database. You can control this by passing options for
171 Class::DBI::Loader in the call to "setup()".
172
173 For each class in the model, you need to create a separate file. So for
174 "BeerDB2::Beer", you would write:
175
176 package BeerDB2::Beer;
177 use strict;
178 use warnings;
179 use base 'OfflineBeer::Beer';
180 1;
181
182 From Maypole 2.11, this package will be loaded automatically during
183 "setup()", and "BeerDB2::Maypole::Model" is pushed onto it's @ISA.
184
185 Configure relationships either in the individual "OfflineBeer::*"
186 classes, or else all together in "OfflineBeer" itself i.e. not in the
187 Maypole model. This way, you only define the relationships in one
188 place.
189
190 The resulting model looks like this:
191
192 Class::DBI
193 MAYPOLE 'MODEL' |
194 |
195 Maypole::Model::Base |
196 + |
197 | +-----------------+----+-----------------+
198 | | | |
199 | | | |
200 Maypole::Model::CDBI | | OFFLINE
201 + | | MODEL
202 | | |
203 BeerDB2::Maypole::Model Class::DBI::<db_driver> OfflineBeer
204 + + +
205 | | |
206 +-----------------------------+ |
207 | |
208 +--- BeerDB2::Pub --------+ OfflineBeer::Pub --------+
209 | beers(); |
210 | |
211 | OfflineBeer::Handpump ---+
212 | beer(); |
213 | pub(); |
214 | |
215 +--- BeerDB2::Beer -------+ OfflineBeer::Beer -------+
216 | pubs(); |
217 | brewery(); |
218 | style(); |
219 | |
220 +--- BeerDB2::Style ------+ OfflineBeer::Style ------+
221 | beers(); |
222 | |
223 +--- BeerDB2::Brewery ----+ OfflineBeer::Brewery ----+
224 beers();
225
226 Features
227
228 1. Non-Maypole applications using the Offline model are completely
229 isolated from the Maypole application, and need not know it exists at
230 all.
231
232 2. Methods defined in the Maypole table classes, override methods
233 defined in the Offline table classes, because "BeerDB2::Maypole::Model"
234 was pushed onto the end of each Maypole table class's @ISA. Perl's
235 depth first, left-to-right method lookup from e.g. "BeerDB2::Beer"
236 starts in "BeerDB2::Beer", then "BeerDB2::Maypole::Model",
237 "Maypole::Model::CDBI", "Maypole::Model::Base", and "Class::DBI",
238 before moving on to "OfflineBeer::Beer" and finally "OfflineBeer".
239
240 CAVEAT - if your Offline model overrides Class::DBI methods, these
241 methods will not be overridden when called from the Maypole
242 application, because the Maypole model provides an alternative path to
243 Class::DBI which is searched first. The solution is to place such
244 methods in a separate package, e.g. "OfflineBeer::CDBI". Place this
245 first in the @ISA of both "BeerDB2::Maypole::Model" and "OfflineBeer".
246 Note that "OfflineBeer::CDBI" does not itself need to inherit from
247 Class::DBI.
248
249 3. Methods defined in the Maypole model base class
250 ("BeerDB2::Maypole::Model"), override methods in the individual Offline
251 table classes, and in the Offline model base class ("Offline").
252
253 4. Relationships defined in the Offline classes are inherited by the
254 Maypole model.
255
256 5. The Maypole model has full access to the underlying Offline model.
257
258 Theory
259
260 This layout illustrates more clearly why the Maypole model may be
261 thought of as part of the controller, rather than part of the model of
262 MVC. Its function is to mediate web requests, translating them into
263 method calls on the Offline model, munging the results, and returning
264 them via the Maypole request object.
265
266 Another way of thinking about it is that Maypole implements a two-layer
267 controller. The first layer translates a raw request into a single
268 method call on the Maypole model layer, which then translates that call
269 into one or more calls on the underlying model.
270
271 Whatever label you prefer to use, this approach provides for clear
272 separation of concerns between the underlying model and the web/user
273 interface, and that's what it's all about.
274
276 - using Maypole::Model::CDBI::Plain or
277 Maypole::FormBuilder::Model::Plain - setup_model() and
278 load_model_subclass() - cutting out all those separate paths to CDBI -
279 they're confusing
280
282 More description of Perl's left-to-right, depth-first method lookup,
283 and where it's particularly important in Maypole.
284
286 David Baird, "<cpan@riverside-cms.co.uk>"
287
289 Copyright 2005 David Baird, All Rights Reserved.
290
291 This text is free documentation; you can redistribute it and/or modify
292 it under the same terms as the Perl documentation itself.
293
294
295
296perl v5.32.0 2020-07-28 Maypole::Manual::Inheritance(3)