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

Getting HASHES out of Class::DBI

by jdtoronto (Prior)
on Jan 06, 2004 at 21:36 UTC ( [id://319251]=perlquestion: print w/replies, xml ) Need Help??

jdtoronto has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed monks,

My poor little brain is having trouble wrapping itself around things to do with OO Perl specifically where it concerns Class::DBI.

My task:

  • To get a hash from a Class::DBI method seems impossible.

    Currently I use:
    my %data; my $obj = App::User->search( username => 'fred' ); while ( my $usr = $obj->next ) { foreach ( $user->columns ) { $data{$_} = $usr->$_; } }
    But this seems rather left handed and backwards if I am to do this every time I want a hash! If I need to do this then why am I using Class::DBI at all, it is actually costing me code in many cases. Surely there must be a better way?
Anybody help please?

jdtoronto

Replies are listed 'Best First'.
Re: Getting HASHES out of Class::DBI
by PodMaster (Abbot) on Jan 07, 2004 at 00:30 UTC
    Born from Re: CGI::Formbuilder and Class::DBI, here it is, hashy :)
    sub hashy { my $obj = shift; return { map { my $g = $obj->get($_); $g = hashy($g) if ref $g; ( $_ => $g ); } $obj->columns }; } __END__ $VAR1 = { 'artist' => { 'artistid' => '1', 'name' => 'U2' }, 'cdid' => '1', 'title' => 'October', 'year' => '1981' };

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Re: Getting HASHES out of Class::DBI
by edoc (Chaplain) on Jan 07, 2004 at 00:05 UTC

    I'd have to ask, what do you need the hash for? If you really do need a hash, then yes, why are you using CDBI? Ideally you want to use the object, not the hash.

    If you use CDBI objects most of the time but need the hash for a particular function, then you could add another method to your class:

    Not Tested:

    __PACKAGE__->set_sql( retrieve_hash => qq{ select * from __TABLE__ where id=? }, 'Main'); sub retrieve_hash { my ($class,$id) = @_; my $sth = $class->sql_retrieve_hash(); $sth->execute($id); my $hashref = $sth->fetchrow_hashref(); $sth->finish; return $hashref; }

    again, though, I would recommend having a close look at your hash usage to see if it can't be modified to use the object instead.

    cheers,

    J

      I'd have to ask, what do you need the hash for? If you really do need a hash, then yes, why are you using CDBI? Ideally you want to use the object, not the hash.
      Well, now you mention it. HTML::Template takes hashes, Data::FormValidate returns hashes, HTML::FillInForm takes hashes, or these tajke hashrefs, whatever. But it seems that everything I am using for validation and output wants hashes. So, indeed, why am I using Class::DBI. Class::DBI was represented as being "the way" to handle data intensive web-apps. But I am beginning to doubt that now. For small databases, few fields (some of my tables have 100-120 fields) and few relationships Class::DBI is great (my test application for example!), but as I have tried to move it into the big league here it is suffering.

      I am quite prepared for someone to tell me it is me that doesn't know how to do things, I am totally new at OO Perl! But I think I am wasting too much time trying to get CDBI shoe-horned into my app right now.

      jdtoronto

        If you are going to use objects to access a database, then Class::DBI will typically make that easier and save you code. Before I started using it, I would write my own data access objects from scratch. Class::DBI has made that unnecessary, or at least removed the boring parts of it.

        I don't think that the usefulness of Class::DBI for a particular application has much to do with the size or complexity of your database. I do think it has a lot to do with your approach to coding and whether or not you like accessing your data as objects.

        When I've used Class::DBI with HTML::Template, I found it pretty easy to copy data from a list of objects into a hash structure that H::T could handle. I definitely was writing code to handle that translation, but that's the price you pay for the strong abstraction layer that H::T provides you. If you want a templating system that doesn't require you to arrange your data like this, Template Toolkit is happy to work on Class::DBI objects directly.

        Have you tried passing the CDBI object into HTML::Template? It's just like passing in a reference to a list or hash. This is how I send database results into my templates (I'm using Petal however).

        William
Re: Getting HASHES out of Class::DBI
by castaway (Parson) on Jan 07, 2004 at 00:28 UTC
    Since the documentation for Class::DBI and Ima::DBI are a little too simple, the information is kinda difficult to piece together, however, having experimented around for a few minutes.. You can define your own arbitrary SQL statements, execute and then call Ima::DBI methods on them.. Something like this:
    App::User->set_sql('getusers', 'select * from __TABLE__ where username + = ?');
    To set up a method named sql_getusers, which can be passed a username, and later to use it:
    my $sth = App::User->sql_getusers(); $sth->execute('fred'); my $hash = $sth->fetchall_hash();
    Or any of the other fetch methods listed in Ima::DBI. This one actually gets you an array of hashrefs, one for each of the answers, containing column names as keys, and values in the values.

    Class::DBI seems to be good in simple cases, but complex in complicated ones.. (whereas DBI is complex all over, but once you learn it, you'll be able to do everything..

    C.

      Just to clarify, what castaway is proposing is to use Ima::DBI methods, in effect skipping Class::DBI. That approach is fine, but it really just sort of skips the power of Class::DBI.

      Class::DBI is about getting to your data using methods, not raw hash entries. If you do need to populate a hash (say to pass off to a templating system), then I'd explicitly show the mapping in the code:

      # untested code my %hash = (name => lc $user->name, address => $user->address, groupname => $user->group->name);
      In this snippet, you can see I changed the case of the name, and grabbed additional data for the template from another table with an implicit (hidden) join via Class::DBI. (I'm assuming the User class and the Group class were defined with a Class::DBI "has-a" relationship.)

      Chaining method calls like this can be very powerful and time-saving.

      On the other hand, if you really have 100s of fields in a table, and just want to punt them all over to a hash to go raw into a template system, then I'd suggest subclassing Class::DBI to add the "hashy" function (or an equivalent) offered below.

      You'd add the code in one place, and have it in all your Class::DBI::WithHashy classes:

      # untested code my %hash = $user->hashy; # pump all the fields into a hash

      I'm a fan of Class::DBI, I guess.

      Best of luck

      rkg

      Class::DBI seems to be good in simple cases, but complex in complicated ones.. (whereas DBI is complex all over, but once you learn it, you'll be able to do everything..
      Thanks for the input castaway, I have been working with DBI for five years at least, and using things like SQL::Abstract to make life easier for some time as well. I have a feeling I am going back to the DBI.

      John jdtoronto

Re: Getting HASHES out of Class::DBI
by Anonymous Monk on Jan 07, 2004 at 03:55 UTC
    You can simplify things somewhat by letting get() do its work in one go:
    sub data_hash { my $obj = shift; my(@cols) = $obj->columns; my %hash; @hash{@cols} = $obj->get(@cols); \%hash; }
Re: Getting HASHES out of Class::DBI
by theorbtwo (Prior) on Jan 07, 2004 at 07:39 UTC

    Having done some Data::Dumper debugging lately, I've noticed that you could just cheat and derefence the object, assuming that you first do enough method calls to force it to be populated. (It tries to be intelegently lazy, so it won't always have all the data in it.)

    If you want to be transparent to future changes in Ima::DBI or Class::DBI, though, don't use this method.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      If you want to be transparent to future changes in Ima::DBI or Class::DBI, though, don't use this method.

      I don't think this point can be emphisised enough: breaking the encapsulation on an object like that is a Really Bad Idea(tm). Even ignoring future versions of Ima::DBI/Class::DBI, all your inflate/deflate fields won't work (and I'm convinced that you should use them more often than not).

      ----
      I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
      -- Schemer

      : () { :|:& };:

      Note: All code is untested, unless otherwise stated

        Amen and amen and ++ and ++.

        <soapbox>

        Breaking encapsulation should be only used as a dire last resort -- and certainly not in production code.

        Going uninvited into the innards of an object is like buying a car with airbags then disabling them, or removing your car's seatbelts. Yeah, you can do it, and eveything will seem fine for a while, and then something goes wrong and you get badly hurt.

        Objects are more than a funny way to call a useful subroutine -- they are an agreement between caller and callee as to a strict API, with everything behind the API wall treated as a black box.

        </soapbox>

        Apologies if I am preaching to the converted, the spirit moved me.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://319251]
Approved by bassplayer
Front-paged by Enlil
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (6)
As of 2024-04-19 11:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found