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:
Anybody help please?
jdtoronto
Re: Getting HASHES out of Class::DBI
by PodMaster (Abbot) on Jan 07, 2004 at 00:30 UTC
|
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. |
| [reply] [d/l] |
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.
| [reply] [d/l] |
|
| [reply] |
|
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.
| [reply] |
|
|
| [reply] |
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. | [reply] [d/l] [select] |
|
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
| [reply] [d/l] [select] |
|
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
| [reply] |
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;
}
| [reply] [d/l] |
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).
| [reply] |
|
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
| [reply] [d/l] [select] |
|
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.
| [reply] |
|
|