aphenine: Teresa and Claire (Default)
aphenine ([personal profile] aphenine) wrote in [site community profile] dw_dev2010-08-01 11:56 am
Entry tags:

Models

I was idly musing on my journal that DW is approaching the MVC model of web development, as it's got Controllers, in the form of functions that get called to handle pages, and Views in the form of the Template Toolkit (or even BML). It even has Routing (which doesn't get a letter in MVC ;) ) to bind the right url to the right Controller. What DW doesn't have is the M part, the Model, which is normally a class that handles access to a table in a database, and I was musing about this because some of the functionality would have been a bit useful for the work I was doing then, even if I do enjoy the fact that not fitting into the MVC concept means DW is a bit more freeform. Anyway, I was wondering if there was any reason that DW didn't have a Model class. [staff profile] denise suggested that I ask, as often something doesn't exist because nobody's done it yet.

Do you think that a general database interface class, like a Model would be useful to DW? Do you think DW should go that way, or are people genuinely happy just hard-coding their SQL queries in various objects, as is done now? Admittedly, the DBI layer does a lot of what Models do, such as quote escaping and input sanitisation, although it doesn't quite do the same job, and it won't error check your SQL query input to check whether you are using the right fields and values, or automatically make your SQL queries for you if you provide it with sensible data and want to do a common operation. If something like a Model is useful, how far should one go with them? Do you find things like behaviours and relationships useful, or do they just get in your way?

Further, if I wrote a simple Model class, would anyone other than me use it? Would anyone know how? Would anyone even care? ;)

#The model fetches all the columns from the database and figures out all the primary keys and datatypes
my $userkeywordstable = Model->new("userkeywords");

#Gets the whole table
my $results = $userkeywordstable->find("all");

#Returns just the keywords column belonging to userid 1
my keywords = $userkeywordstable->find( "all", "keywords", { "userid" => 1 } );

#Return the primary key columns for this table
my $pkeys = $userkeywords->primary_keys;

#Prepare some new tags for a user and add them
#If the userkeywords table was autoincrement, I wouldn't need to work out the keys as the model would do it for me.
for( my $i=0; $i<@newtags; ++$i )
{
push $data, \{ userid=>$user->{userid}, "kwid"=>$maxkw+$i, "keyword"=>$newtags[$i]};
}
$userkeywordstable->insert($data);
if( $userkeywordstable->err )
{
print $userkeywordstable->errmsg;
}

#Update keyword 0, userid 34 with keyword anime
$userkeywordstable->update( { kwid => 0, userid => 34 }, {"keyword"=>"anime"} );

#Produces error, parentkwid isn't in this table!
$userkeywordstable->update( {kwid=>0, userid => 34}, {"parentkwid"=>9} );

#Produces error, didn't you know keyword isn't a number, numbskull!
$userkeywordstable->update( { kwid => 0, userid => 34 }, {"keyword"=>9} );

#I have some data I have worked on that needs updating, figure it out by yourself which fields are the primary keys and update as necessary
my $data = $userkeywordstable->find("all");
... #Work done here
$userkeywordstable->update($data);

#Activate advanced behaviours for this table
$userkeywordstable->behavesAs("user", "threaded");

#Find all tags belonging to user name aphenine and return them in threaded mode
#The user behaviour automatically pulls the userid for a name, which is why userid is no longer in the WHERE clause (third argument), but user is in the options (fourth).
$userkeywordstable->find( "threaded", undef, undef, {"user" => "aphenine"} );

#Get the tag containing the word anime, and all entries which have those tags in them (using relationships).
$userkeywordstable->manyToMany("logtags2", "logkeywords"); #relates to posts (in logtags) via logkeywords table
$userkeywordstable->find("all", undef, {"keyword"=>"anime"}, {"user" => "aphenine", "recurse"=>1}); #return the tag and the entries it appears in
allen: (pooka)

I miss Hibernate

[personal profile] allen 2010-08-01 06:47 pm (UTC)(link)
First, in general, yes. I actually had decided that the next time I added a new top-level DB table and object class that I was going to make something like DbObject.pm and pull together all the generic SQL handling code in one place. I mean, how many cut and pastes of absorb_row do we really need? And combine it with Memcacheable and you should be able to create a new cached, DB-backed class with just a 'use base', some table and column name configuration, and a little bit of accessor programming.

That having been said... A lot of LJ/DW really does follow the Model part of MVC ok: LJ::Entry, LJ::User, and even LJ::Userpic are all Model implementations, if maybe not the cleanest examples of them. They're just all hand-coded Model implementations. So they're ugly, and have lots of duplicated code, and in some cases (keywords in particular) try to handle too much at once. But they are valid Models; you can, in a Controller, load up an Entry object, read it, and modify it without actually writing any new SQL. (edit: as [personal profile] pauamma says above.)
mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)

Re: I miss Hibernate

[staff profile] mark 2010-08-01 07:14 pm (UTC)(link)
I went to a number of Moose talks at OSCON this year, and the urge to rewrite everything as proper OO (which would help enforce proper Models) was really strong.
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)

Re: I miss Hibernate

[personal profile] afuna 2010-08-01 11:25 pm (UTC)(link)
Oh yeah, resisting the same urge. It wouldn't quite abstract things to the extent of the ORM -- we'd still be hand-writing the SQL, but it would take care of a lot of the basic object structure.

Mmm, temptation.
sophie: A cartoon-like representation of a girl standing on a hill, with brown hair, blue eyes, a flowery top, and blue skirt. ☀ (Default)

Re: I miss Hibernate

[personal profile] sophie 2010-08-03 12:07 am (UTC)(link)
As far as I know, LJ::User (the package) doesn't have the functions to load users; on the other hand, LJ/User.pm (the file) does. Yes, the file mixes packages. The actual function to load a user is LJ::load_user(), which is located in the LJ/User.pm file. So really, they need separating for more than Model reasons. ;p But it's going to be a fair bit of work to do that for everything...