[Catalyst] Dispatching based on path and host/domain

Matt Pitts mpitts at a3its.com
Tue Apr 22 02:28:57 BST 2008


> -----Original Message-----
> From: Curtis Fletcher [mailto:curtis.fletcher at pharmaventures.com]
> Sent: Monday, April 21, 2008 7:08 PM
> To: catalyst at lists.scsys.co.uk
> Subject: [Catalyst] Dispatching based on path and host/domain
> 
> Hi again guys.
> 
> I've got a moderate sized Catalyst App in production now which I'm
> almost happy with :)
> It responds to one domain at the moment, and I'm pondering how to
break
> out into two or more doing mostly the same thing on the same codebase
> and hopefully the same app instance.
> 
> The app has a few "usual" controllers that handle specifics like the
> e-commerce, admin and soon, some database product access stuff. Any
URI
> that fails to match a action defaults to the root controller and is
> then
> stripped and handled with a few calls to my model which builds the
> closest page from CMS style content.
> 
> E.G.
> 
> http://mydomain.com/cart/view
> Ends up calling the Local "view" method of myapp::Controller::Cart as
> you might expect but
> http://mydomain.com/businessunit1/product1/specialofferpage
> 
> Ends up in the root controller and queries tables that holds dynamic
> URI-to-page content object mappings which then uses common code to
> render the page
> 
> So for the CMS style data, if I add another domain "mydomain2" to the
> apache config for my myapp and another field to the UriPage table for
> "domain" and I'm almost there.
> But I'm at a bit of a loss at to what to do about:
> http://mydomain2.com/cart/view
> Ending up at myapp::Controller::Cart->view because that domain isn't
> supposed to have the e-commerce bit.
> 
> I've been pondering how to make this distinction, maybe there is
> something I could get the dispatcher to do like:
> 
> package myapp::Controller::Cart;
> 
> sub view : Local Domain('mydomain.com')
> {
> }
> 
> Or something with the namespaces so that
> http://mydomain.com/cart/view got mapped to
> myapp::mydomain::Controller::Cart->view and
> http://mydomain2.com/search got mapped to
> myapp::mydomain2::Controller::Search
> 
> still making sure that anything that didn't match still ended up in
the
> root controller unmangled.
> 
> What I think I'd like if to be able to do is opt a namespace out of a
> particular domain's dispatcher
> Something like:
> __PACKAGE__->config->{'opt_out'}->
> {
> 	'mydomain2.com' => ['/cart',],
> 	'mydomain.com' => ['/search',],
> }
> 
> That way common actions still work on both domains. Worst comes to the
> worst I'll set config options that disable methods based on ENV
> variables then run two app instances on the same codebase but I'd
> prefer
> to avoid that.
> 
> Is this approach even sane? any suggestions/pointers/thoughts are
> welcomed.
> 

You've probably heard this before on the list, but...

Ideally, you shouldn't have enough code in your Controllers to justify
"sharing" the app across domains that need different functionality. The
meat of the app should be in the Models, then you can just run multiple
Cat apps - one with Cart controllers and one without - that use the same
"shared" Models.

If you're thinking that you're "stuck with what you've got" think about
the time it will take you to implement per-domain dispatching vs. the
time to extract out functionality to a set of Models. Usually for me,
"fancy" things like dispatching based on domain take much more time to
implement than they appear, are more buggy and are harder to maintain.
Ultimately, you'll end up with more maintainable applications if you
keep the "sharable" functionality in the Models.

If you're looking to share the app for other reasons (i.e. hosting
costs) then I would look at it from a "white-labeling" perspective.
Rather than have domain information in my Controllers, I would create
the concept of "sites" inside the app and create a Plugin to interface
the current site's config interface via something like $c->site. Then in
my Cart controllers I might do something like

MyApp::Controller::Cart;

...

sub auto : Private {
    my ( $self, $c ) = @_;
  
    if ( ! $c->site->has_function('cart') ) {
        // something here to drop the request back to your "best guess"
logic
    }
}

...

1;

You could even implement $c->site to use a Model to pull in site config
info, which means changes on-the-fly.

v/r
-matt pitts



More information about the Catalyst mailing list