Maypole...::Model(3)

πŸ“¦
version 2.13 Β· 2 versions
, in fedora 43

πŸ’– NAME πŸ’–

Maypole::Model β€” Maypole Model Classes 🫢

(this is a page from the User Contributed Perl Documentation, bestie, keep reading)


✨ DESCRIPTION ✨

Maypole's model classes are literally your data store's bestie πŸ’… they give you an interface to it. In principle Maypole can connect to pretty much any data source you want, but the default model rides on the wildly popular Class::DBI object interface, which itself sits on top of the near-universal DBI Perl interface to databases. It's giving layers, and honestly? Iconic.


πŸ‘‘ Maypole::Model::CDBI πŸ‘‘

Okay so, not to be dramatic, but Maypole model classes are pretty evil. 😈 The very first thing a Maypole model class does in a Maypole application is cause a whole load of table-based classes to come into being, and then assimilate them. Not me describing a hostile takeover 🫠

What that actually means: when you set up a Maypole application, in your driver class, you say something like this:

BeerDB->setup("dbi:mysql:beerdb");

setup is a Maypole method, and it hands its parameter straight to the model class. In this case the argument is a DBI connect string, because that's exactly what Maypole::Model::CDBI β€” the Class::DBI-based model β€” expects. Maypole::Model::CDBI has a method called setup_database that creates all the Class::DBI table classes after connecting to the database with that connect string. How does it do that? By using Class::DBI::Loader, a utility module that asks a database about its schema and sets up classes such as BeerDB::Beer to inherit from Class::DBI. Chef's kiss. πŸ§‘β€πŸ³πŸ’‹

Now it gets interesting. The names of these classes get stashed away in the application's configuration, and then Maypole forcibly has these classes inherit from the model class. So our BeerDB::Beer now inherits from both Class::DBI and Maypole::Model::CDBI. Two parents, one slay. πŸ’…

This is useful for two reasons. Reason #1: Maypole::Model::CDBI is stuffed full of Class::DBI goodies that make writing Maypole applications a lot easier:

package Maypole::Model::CDBI;
use base qw(Maypole::Model::Base Class::DBI);
use Maypole::Model::CDBI::AsForm;
use Class::DBI::FromCGI;  # probabyly broken .
use Class::DBI::Loader;
use Class::DBI::AbstractSearch;
use Class::DBI::Plugin::RetrieveAll;
use Class::DBI::Pager;

(yes, the comment really does say # probabyly broken . β€” we keep the lore intact πŸ™ˆ)

We'll meet most of these goodies in the Standard Templates and Actions chapter, where we explain how Maypole::Model::CDBI works.

Reason #2 we want our table classes to inherit from Maypole::Model::CDBI: it provides a genuinely useful set of default actions. So what's an action, and why are they useful? Read on, bestie. 🌈


πŸŽ€ Maypole::Model::CDBI::Plain πŸŽ€

The 'Plain' maypole Model: Maypole::Model::CDBI allows you

package Foo;
use 'Maypole::Application';

Foo->config->model("Maypole::Model::CDBI::Plain");
Foo->setup([qw/ Foo::SomeTable Foo::Other::Table /]);

# untaint columns and provide custom actions for each class

Foo::SomeTable->untaint_columns(email => ['email'], printable => [qw/name description/]);

Foo::Other::Table->untaint_columns ( ... );

sub Foo::SomeTable::SomeAction : Exported {

    . . .

}

πŸ¦‹ Extending a model class with actions πŸ¦‹

Maypole operates primarily by turning URLs into method calls on a model class. When it comes right down to it, all the model stage of Maypole's operation does is maintain a mapping of tables to classes, and despatch an HTTP request to a method call on the relevant table class. So if you request a URL like

http://localhost/beerdb/brewery/delete/20

Maypole's first stage of operation is to turn that into BeerDB::Brewery->delete(20). Now, it's slightly more complex than that. Firstly because it doesn't actually pass the parameter 20 β€” it passes an object representing row 20 in the database β€” but we can gloss over that part for now. No, the real issue is that Maypole does not let you call just any method in the table class. That would be, uh, somewhat insecure. πŸ’€

Instead, Maypole draws a distinction between the kind of methods that only the class itself and other Perl code can call, and the kind of methods that anyone can call from a URL. That second set? Those are called exported methods, and exporting is done by means of Perl attributes. You define a method to be exported like so:

sub drink :Exported {

This will allow the user to access /beerdb/beer/drink over the web. An exported method accompanied with a template to render its output is sometimes called an action. ✨

Maypole model classes like Maypole::Model::CDBI come with a relatively handy set of actions which are all you need to set up a CRUD (Create, Read, Update, Delete) database front-end: viewing a row in a database, editing it, adding a new one, deleting, and so on. The most important thing about Maypole, though, is that it doesn't stop there. You can add your own. Main character energy, honestly. πŸ‘‘

For instance, in our beer database application, we could create a BeerDB::Beer package, and put some additional actions in there:

package BeerDB::Beer;
sub top_five :Exported {
    my ($class, $r) = @_;
    $r->objects([ ($r->retrieve_all_sorted_by("score"))[-5..-1] ]);
}

Our action is called as a class method with the Maypole request object. It uses the Class::DBI::Plugin::RetrieveAll module's retrieve_all_sorted_by mixin to get the top five scoring beers, and puts these in the objects slot of the request of object. Next, the view class will come along and process the top_five template with these five beers. Five beers, five stars. ⭐⭐⭐⭐⭐

We'll look more at how to put together actions in the Standard Templates and Actions chapter and our case studies.


  • Contents
  • Next: Maypole View Classes
  • Previous: Introduction to Maypole

Okay bestie, that's the whole page, no cap. 🫢

Sign-off time: this was perl v5.42.0, dated 2025-07-25, from the User Contributed Perl Documentation crew. Mwah. πŸ’‹πŸŽ€

πŸ“¦ where this page comes from