[Catalyst] Usage of Catalyst::Plugin::Singleton

Alex Kavanagh alex at tinwood.homelinux.org
Mon Jan 23 22:27:44 CET 2006


Thanks for your reply.  Very interesting - I do have a further
question although it might be going off-topic:

At Mon, 23 Jan 2006 14:52:51 -0600,
Brandon Black wrote:
> 
> On 1/23/06, Alex Kavanagh <alex at tinwood.homelinux.org> wrote:
> > May I ask:
> >
> > 1. What was the design decision behind not having a 'global' application
> >    object that you could just access?  Is it a threading issue, or
> >    something else?  i.e. the Singleton would effectively be breaking
> >    that design decision.
> >
> 
> Singleton does effectively break an otherwise good design decision,
> IMHO.  But if you need a quick hack, and are willing to live with
> breaking from good design decisions, it can be useful.  For any given
> case where Singleton looks like a good idea, there's probably a better
> solution without using Singleton (or MyApp->context, which is
> similar), but it might be more difficult to design correctly.

Yes, I thought this might be the case.

[snip]

> >
> >    __dbic->table('some other table')->function(params)
> >
> > Is this crappy design on my part?
> >
> 

[snip]

> But in any case, having a method in one table class invoke a method in
> another table class smells like a design problem somewhere.  Possibly
> one that could be handled with proper relationships?  If there's a
> referential link between the two tables, you can let DBIC
> relationship-handling take care retreiving data from the other table
> class.

This sounds like the core of the problem.  I have two question, one is
to do with Data::FormValidator and calling validation functions in the
model, but the second is to do with how to represent a database design
in DBIx::Class :

1st Question:
-------------

For a simple case, imagine that you have a table of Categories
(columns 'id' and 'name').

Now you want any Category to be related to any other category. Call
that, say CategoryAssocs and give it two columes 'id1' and 'id2'.

Now to relate any two categories you simply put the id of each
category into id1 and id2.

I couldn't work out how to get DBIx to do this for me and ensure that
there were unique entries for relations.  i.e. for two ids (a) and (b)
you don't get entries of (a,b) AND (b,a) in the table.

Thus I wrote some code into the CategoryAssocs class to handle all
this and just provided the meta functions (add, delete, search, etc.).

How would you implement this?

2nd Question:

I'm using Data::FormValidator to validate my forms and I find that I
need to call a function in the model to check for a unique username
(during an add user) for example.  i.e. I want to do all of the form
checking in the $c->form method.

I also only want to call the function to create the structure for D::F
once, and my technique was to create a classdata item __dfv and fill
it out when the Class is used:

__PACKAGE__->__dfv( .... validation structures ...);

Inside the validation structure you reach the bit of validation:

    constraint_methods => { username_new => _check_username(),
			    email_new    =>  email(),
			    password_old => _check_password('username'),
			    password_new => _check_passwords_match('password1_new') },
			  },

and _check_username() is a Class function:

sub _check_username {
  return sub {
    my $dfv = shift;
    $dfv->name_this('_check_username');
    my $un = $dfv->get_current_constraint_value;
    # no blank usernames
    return 0 unless $un;
    # return a 0 if the username already exists!
    return $__dbic->user_object_for( $un ) ? 0 : 1;
  };
}


__dbic is a class scope global that I localise to $c->model('Site');
just before calling the $c->form(...); method.

I guess the reason I'm writing this is that this kind of sucks from a
design perspective, but I'm struggling to make sense of how to make it
better!

If you (or anyone) uses D::F how do you structure your code so that
you don't need to do this kind of thing.  Currently this is in my
Controller class, but maybe verification should be in the Model Class
so that I can just do a:

__PACKAGE__->user_object_for(...) 

But then this would be a class method call rather than an object
method call.

Thanks again for your help - it's definitely started me re-thinking
the design decisions.

Cheers
Alex.



More information about the Catalyst mailing list