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 aware
112       that some people like the convenience of Class::DBI::Relationship,
113       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 "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

Advanced applications - building the model by hand ** TODO

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

Method inheritance ** TODO

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

AUTHOR

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.36.0                      2023-01-20   Maypole::Manual::Inheritance(3)
Impressum