Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Add a method to a ResultSet Class in DBIx::Class?

by matija (Priest)
on Jan 09, 2008 at 11:47 UTC ( #661319=perlquestion: print w/ replies, xml ) Need Help??
matija has asked for the wisdom of the Perl Monks concerning the following question:

Oh glorious and incredibly patient fellow monks, can anybody please enlighten me how to add a method to a ResultSet class in DBIx::Class?

Here is what I'm trying to do:
package ThreadedDB::Article; use strict; use warnings; use base 'DBIx::Class'; __PACKAGE__->load_components("Core"); __PACKAGE__->table("article"); __PACKAGE__->add_columns( "id", { data_type => "INT", default_value => undef, is_nullable => 0, size + => 11 }, # etc, etc, etc ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->has_many( "article_texts", "ThreadedDB::ArticleText", { "foreign.article" => "self.id" }, ); # Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-01-03 18:12:1 +2 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Y0WFrRSlgOqaz/fbhRt98A #package ThreadedDB::Article::ResultSet; #use base 'DBIx::Class::ResultSet'; sub insert_article { my ($self, $topic, $parent, $msgtext) = @_; my $articles = $self->resultset('Article'); eval { $self->txn_do (sub { # a complex operation that is not relevant yet }) }; } 1;

In other words, I'd like to find a way to put a method into the Article.pm file in such a way that I could access it by doing $schema->resultset('Article')->insert_article(...)

I've tried directly declaring the class (as commented out, above) but it didn't work. Either I'm misunderstanding what the resulting class should be called, or I'm misunderstanding something else.

I did read Re^2: [DBIX::Class] problem with Arbitrary SQL through a custom ResultSource, but it didn't help since the method I need contains a bunch of code (not all of which can be in SQL), and all of which should be done inside a transaction.

I'd be grateful for any pointers...

Comment on Add a method to a ResultSet Class in DBIx::Class?
Select or Download Code
Re: Add a method to a ResultSet Class in DBIx::Class?
by Corion (Pope) on Jan 09, 2008 at 11:58 UTC

    I have only done marginal work with DBIx::Class, but I think simply declaring the methods in the appropriate ResultSet subclass should work:

    package ThreadedDB::Article::ResultSet; #use base 'DBIx::Class::ResultSet'; sub insert_article { my ($self, $topic, $parent, $msgtext) = @_; my $articles = $self->resultset('Article'); eval { $self->txn_do (sub { # a complex operation that is not relevant yet }) }; }

    I left out the use base statement, because DBIx::Class will hopefully set up @ISA correctly when it's creating the subclasses.

      Nope, that doesn't seem to be it:

      my $articles = $schema->resultset('ThreadedDB::Article'); DB<3> x $articles->can('create') 0 CODE(0x88b1cd4) -> &DBIx::Class::ResultSet::create in /usr/share/perl5/DBIx/Class/R +esultSet.pm:1625-1630 DB<4> x $articles->can('insert_article') 0 undef

        I forgot to say that in my first reply, sorry.

        If that namespace does not work, find out the real namespace of your resultset:

        warn ref ($schema->resultset('ThreadedDB::Article'));

        and then put your routines into that namespace, whatever it is.

      I left out the use base statement, because DBIx::Class will hopefully set up @ISA correctly when it's creating the subclasses.

      This only works automatically if you are using load_namespaces instead of load_classes from DBIx::Class::Schema, then they will be detected if they are named ThreadedDB::Result::Article and ThreadedDB::ResultSet::Article. If you aren't doing it this way, then the resultset_class will always be DBIx::Class::ResultSet, which you probably don't want to add things to...


      We're not surrounded, we're in a target-rich environment!
Re: Add a method to a ResultSet Class in DBIx::Class?
by castaway (Parson) on Jan 09, 2008 at 14:35 UTC
    Almost there:
    __PACKAGE__->resultset_class('ThreadedDB::Article::ResultSet'); ## uncomment these package ThreadedDB::Article::ResultSet; use base 'DBIx::Class::ResultSet';
    (And don't forget that $self isa ResultSet after that)

    Done.

    C.

      That's IT!

      It works now.


      Thank you!

      My favorite often-overlooked method of avoiding having to set resultset_class on all your resultsets is to use load_namespaces instead of load_classes in your DBIx::Class::Schema subclass. For example, I usually set my schema up like this:

      # lib/MyDB.pm package MyDB; use strict; use warnings; use base qw( DBIx::Class::Schema ); __PACKAGE__->load_namespaces( default_resultset_class => 'ResultSet', ); 1; # lib/MyDB/ResultSet.pm package MyDB::ResultSet; use strict; use warnings; use base qw( DBIx::Class::ResultSet ); 1; # lib/MyDB/Result.pm package MyDB::Result; use strict; use warnings; use base qw( DBIx::Class ); __PACKAGE__->load_components(qw( FormFu InflateColumn::DateTime UUIDColumns Core )); 1;

      This way I have a custom base class for both resultsets and results, and if I create a new result class or a new resultset class, it will just work, without having to do anything extra.

      This also lets you add common functionality to your base classes, like I did with loading components I use everywhere in MyDB::Result. If you add custom functionality to your base MyDB::ResultSet class, that will get used whenever you don't have a specific resultset class for a given result class, which is handy for adding things like advanced searching to all your resultsets.

      # lib/MyDB/Result/Article.pm package MyDB::Result::Article; use strict; use warnings; use base qw( MyDB::Result ); __PACKAGE__->table( 'articles' ); __PACKAGE__->columns(qw( updated_time created_time ));

      Now, if I do this:

      my $schema = MyDB->connect( @connection_info ); my $article = $rs->schema( 'Article' )->new({});

      I'll get back a MyDB::Result::Article object that automatically has it's resultset_class set to MyDB::ResultSet. If I later decide I have functionality to add to the resultset just for articles (as you did), I can just create the new class and it will be detected automatically at startup.

      # lib/MyDB/ResultSet/Article.pm package MyDB::ResultSet::Article; use strict; use warnings; use base qw( MyDB::ResultSet ); sub insert_article { my ($self, $topic, $parent, $msgtext) = @_; eval { $self->txn_do( sub {} ) }; } 1;

      And now when doing my $article = $rs->schema( 'Article' )->new({});, the object I get back has it's resultset_class set to MyDB::ResultSet::Article instead, since there is a specific class for it now...


      We're not surrounded, we're in a target-rich environment!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://661319]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (7)
As of 2014-12-27 06:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (176 votes), past polls