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 aware
112 that some people like the convenience of Class::DBI::Relationship,
113 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 "require".
167
168 Note: by default, this will create Maypole/CDBI classes for all the
169 tables in the database. You can control this by passing options for
170 Class::DBI::Loader in the call to setup().
171
172 For each class in the model, you need to create a separate file. So for
173 "BeerDB2::Beer", you would write:
174
175 package BeerDB2::Beer;
176 use strict;
177 use warnings;
178 use base 'OfflineBeer::Beer';
179 1;
180
181 From Maypole 2.11, this package will be loaded automatically during
182 setup(), and "BeerDB2::Maypole::Model" is pushed onto it's @ISA.
183
184 Configure relationships either in the individual "OfflineBeer::*"
185 classes, or else all together in "OfflineBeer" itself i.e. not in the
186 Maypole model. This way, you only define the relationships in one
187 place.
188
189 The resulting model looks like this:
190
191 Class::DBI
192 MAYPOLE 'MODEL' |
193 |
194 Maypole::Model::Base |
195 + |
196 | +-----------------+----+-----------------+
197 | | | |
198 | | | |
199 Maypole::Model::CDBI | | OFFLINE
200 + | | MODEL
201 | | |
202 BeerDB2::Maypole::Model Class::DBI::<db_driver> OfflineBeer
203 + + +
204 | | |
205 +-----------------------------+ |
206 | |
207 +--- BeerDB2::Pub --------+ OfflineBeer::Pub --------+
208 | beers(); |
209 | |
210 | OfflineBeer::Handpump ---+
211 | beer(); |
212 | pub(); |
213 | |
214 +--- BeerDB2::Beer -------+ OfflineBeer::Beer -------+
215 | pubs(); |
216 | brewery(); |
217 | style(); |
218 | |
219 +--- BeerDB2::Style ------+ OfflineBeer::Style ------+
220 | beers(); |
221 | |
222 +--- BeerDB2::Brewery ----+ OfflineBeer::Brewery ----+
223 beers();
224
225 Features
226
227 1. Non-Maypole applications using the Offline model are completely
228 isolated from the Maypole application, and need not know it exists at
229 all.
230
231 2. Methods defined in the Maypole table classes, override methods
232 defined in the Offline table classes, because "BeerDB2::Maypole::Model"
233 was pushed onto the end of each Maypole table class's @ISA. Perl's
234 depth first, left-to-right method lookup from e.g. "BeerDB2::Beer"
235 starts in "BeerDB2::Beer", then "BeerDB2::Maypole::Model",
236 "Maypole::Model::CDBI", "Maypole::Model::Base", and "Class::DBI",
237 before moving on to "OfflineBeer::Beer" and finally "OfflineBeer".
238
239 CAVEAT - if your Offline model overrides Class::DBI methods, these
240 methods will not be overridden when called from the Maypole
241 application, because the Maypole model provides an alternative path to
242 Class::DBI which is searched first. The solution is to place such
243 methods in a separate package, e.g. "OfflineBeer::CDBI". Place this
244 first in the @ISA of both "BeerDB2::Maypole::Model" and "OfflineBeer".
245 Note that "OfflineBeer::CDBI" does not itself need to inherit from
246 Class::DBI.
247
248 3. Methods defined in the Maypole model base class
249 ("BeerDB2::Maypole::Model"), override methods in the individual Offline
250 table classes, and in the Offline model base class ("Offline").
251
252 4. Relationships defined in the Offline classes are inherited by the
253 Maypole model.
254
255 5. The Maypole model has full access to the underlying Offline model.
256
257 Theory
258
259 This layout illustrates more clearly why the Maypole model may be
260 thought of as part of the controller, rather than part of the model of
261 MVC. Its function is to mediate web requests, translating them into
262 method calls on the Offline model, munging the results, and returning
263 them via the Maypole request object.
264
265 Another way of thinking about it is that Maypole implements a two-layer
266 controller. The first layer translates a raw request into a single
267 method call on the Maypole model layer, which then translates that call
268 into one or more calls on the underlying model.
269
270 Whatever label you prefer to use, this approach provides for clear
271 separation of concerns between the underlying model and the web/user
272 interface, and that's what it's all about.
273
275 - using Maypole::Model::CDBI::Plain or
276 Maypole::FormBuilder::Model::Plain - setup_model() and
277 load_model_subclass() - cutting out all those separate paths to CDBI -
278 they're confusing
279
281 More description of Perl's left-to-right, depth-first method lookup,
282 and where it's particularly important in Maypole.
283
285 David Baird, "<cpan@riverside-cms.co.uk>"
286
288 Copyright 2005 David Baird, All Rights Reserved.
289
290 This text is free documentation; you can redistribute it and/or modify
291 it under the same terms as the Perl documentation itself.
292
293
294
295perl v5.38.0 2023-07-20 Maypole::Manual::Inheritance(3)