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.
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
![[staff profile]](https://www.dreamwidth.org/img/silk/identity/user_staff.png)
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
no subject
push $data, \{ userid=>$user->{userid}, "kwid"=>$maxkw+$i, "keyword"=>$newtags[$i]};
Er, really? A reference to a reference to an anonymous hash? What does the extra \ get you?
Also, doesn't it have to be
push @$data, ...
orpush @data, ...
?no subject
no subject
That said, I have been doing some work recently to make sure our SQL statements are packaged in subroutines. There are some places in the legacy LJ stuff where it's embedded directly into BML files, and that makes me cringe.
no subject
(It uses colours to explain what's going on, I think it's pretty clear and easy to understand.)
no subject
no subject
no subject
If done properly, I think that in theory a class like LJ:User and LJ:Entry would be subclasses of Models themselves (if they fit one database table exactly, which I don't think they do) or would use use an instance of a Model whenever they need to talk to the database table(s) that make up each Object. So, for example, User would have user and the userprop table Models at a minimum.
no subject
However, I do think is that having a generic class for those tables where no interface exists would be time saving and efficient and would prevent duplicating code and therefore worth it. This is really where I feel the argument justifying it lies, and what I was asking whether it would be useful to write.
no subject
no subject
The best explanation is given by Mark in this tutorial post (which I wasted a few minutes trying to find in Google before remembering DW has its own search now, yay!) It might be worth taking a look and seeing if there's anything there that might get in the way, or otherwise.
no subject
P.S. Thanks for the link, it's very good.
I miss Hibernate
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
Re: I miss Hibernate
Re: I miss Hibernate
Mmm, temptation.
Re: I miss Hibernate
Like you said, my original motivation was to make talking to the database easier for those things which don't have Objects of their own.
Re: I miss Hibernate
no subject
I've used this system before in a Catalyst application I built and when I was at Six Apart they used it for things. It certainly has a lot of nice functionality, and you can make it do some interesting things.
I never found it particularly useful, though. It's easier for me to see the raw SQL and know what it's doing.
Now, one thing I did always want to do was to create objects for the various classes of data (models basically) but not use an ORM. The system would not automatically scan the tables for keys and such. I would just define classes that had all of the SQL self-contained. Then the rest of the application would be 100% free of SQL, which would be a boon if we ever wanted to change backends or whatnot.
Eh. Not a useful comment, I suppose. I have vague low-level distrust and dislike of ORMs. I think it's hard to use them well and they're mostly a crutch.
no subject
no subject
no subject
That's a pretty good page explaining what a model should encapsulate in MVC paradigms, although it's written from a PHP/Zend Framework perspective -- from the article: