1Maypole::Manual::InheriUtsaenrceC(o3n)tributed Perl DocuMmaeynptoaltei:o:nManual::Inheritance(3)
2
3
4

NAME

6       Maypole::Manual::Inheritance - structure of a Maypole application
7

DESCRIPTION

9       Discusses the inheritance structure of a basic and a more advanced
10       Maypole application.
11

CONVENTIONS

13       inheritance
14                   +
15                   |
16                +-   -+
17                   |
18                   +
19
20       notes
21               target *-------- note about the target
22
23       association
24               source ------> target
25

Structure of a standard Maypole application

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

An advanced Maypole application

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

Advanced applications - building the model by hand ** TODO

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

Method inheritance ** TODO

282       More description of Perl's left-to-right, depth-first method lookup,
283       and where it's particularly important in Maypole.
284

AUTHOR

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.30.0                      2019-07-26   Maypole::Manual::Inheritance(3)
Impressum