Thanks for the thoughtful reply. First of all, the mock-method you wrote as an example here is pretty close to what I did to get where I needed. So I think we're on the same page. But my question is, why not make that method and expose it for the sql-flavored adapters? Maybe that should go in DO, or somewhere closer to the sql adapters, true. But in the somewhat rare case that someone needs more power at their fingertips, why make them come up with that helper method themselves?
Here's my use case: I have site where small messages can be posted to different feeds. Basically I need to pull the last message from each feed, but I need to do it in one query. A feed is not a database object, so I don't have a previous list of feeds. Here's my sql query: "SELECT messages.* FROM messages INNER JOIN (SELECT MAX(id) AS id FROM messages WHERE created_by = ? GROUP BY feed) AS ids ON messages.id = ids.id" I'm sure you can figure out what it does, but for helpers, it basically grabs the highest (incrementing) id from each feed made by the current user, then grabs the entire record for each of those ids. If there's a better way to do this, go ahead and let me know, but I believe the use case is still a valid one, assuming there may be other complex queries that fill a similar need. I guess you'd call that a "report" method, and it's being used in the controller, not the model. (I confess this might be a structural/layering inconsistency, and yes, I could move it into a method on the model itself, but it is a single-use need so I didn't.) But of course to be objects of the Message model, I need to be able to pull that sql query into objects the same way DM normally does. One question in my head is, Why run DM.query and get back Structs when there's really no logical roadblock to getting real model objects? I'm sure there will be cases in the future where I will want to do a custom sql query just because it will take less queries than DM would have taken to do the same thing. You have to admit, there are some limitations to the query syntax, and sometimes there are better ways to write a query that you can't accomplish with the query syntax provided. Will I have to write my own method to short-circuit some DM intestines? Or can I write my own sql when I really need to? ~Daniel On Tue, Sep 2, 2008 at 3:12 PM, Sam Smoot <[EMAIL PROTECTED]> wrote: > > On Sep 2, 11:47 am, "Daniel Parker" <[EMAIL PROTECTED]> wrote: > > I poked around in DataObjects, and I've seen a version where it injects > > another method or two when you're using sql-specific repositories. Not > hard > > to do it that way. Of course nobody would want to compose raw SQL when > > they're not using a sql engine. > > Daniel, > > I think you mean DataMapper. Anyways. Yeah, so what's your use-case > for this? > > No offense, just going to lay out my reasoning for why DM is the way > it is. > > First, lemme clarify something: The order of the properties is not the > order that an object should be materialized in. That depends on > Query#fields. If you try to materialize just by "lining up the fields" > you'll quickly run into issues with composite fields or lazy-loaded > attributes and eventually prefetching. > > I ask because AR::find_by_sql is wrong. It's a bleeding of > responsibilities. DM is for CRUD. It's an O/R Mapper, not a reporting > tool. The objects returned from a find_by_sql are often not > persistable objects and it completely subverts the majority of the > code in DM. > > As Adam suggested, if all you want is a convenience to translate query > results into object instances a simple helper in your application > could manage that with very little work. Execute a DO query, map the > results to objects. Something like: > > : # This is probably close, but untested, caveat emptor. > : module ApplicationHelper > : def query(model, sql, *args) > : connection = DataObjects::Connection.new(Merb.config[:databases] > [:default]) > : reader = connection.create_command(sql).execute_reader(*args) > : results = [] > : while(reader.next!) do > : results << > model.new(Hash[*reader.fields.zip(reader.values).flatten]) > : end > : return results > : ensure > : connection.close > : end > : end > > You could even pass the fields explicitly to ensure everything's type- > cast properly with Command#set_types. > > Reporting tools are valuable for sure. But when you don't have one > handy, the solution is not a half-baked extension to your O/RM. Just > use the database-drivers directly. It only adds a line or two of code > to use the DO connections directly, and the performance pay-off is > well worth it. > > So. Why do the objects that come back need to be model instances? > > I'll share an example of how we've done it wrong on an app or two... > > We have a Photo model with a #preview(max_width, max_height) method. > It's awesome. It takes a look at itself to determine wether it's > published or not, and if so, generates a preview image or thumbnail > at the appropriate size (retaining aspect-ratio) in the public folder > so Apache can serve it. If it's unpublished, it generates a file in a > private folder and returns the path so the app can X-SENDFILE it. > > So it depends on knowing the source-path of the original image, and > wether it's published. > > Problem is, when we want to do a custom, fast query on our /photos/ > index page to bring back the photos for a specific user, we get these > raw Arrays, not Photos. > > Ok, easy enough, we move it to a class-method that takes the id, > created_at, and published_at so we know where on the file-system to > find the source file, whether to watermark it, etc. > > The problem is, we're making it the responsibility of the Photo model > to know how to generate preview images, and that just doesn't make > sense. > > What we need here is a separate class that lives outside of our domain- > model. A Preview class that can take these attributes. It's easier to > test. It's cleaner with a clear separation of responsibilities. It's > easier to re-use between projects as long as our Photo implements a > fairly simple interface. > > Most importantly, it just works with a few pieces of data, and it > doesn't matter where it comes from. It's cake to use it with models, > Arrays, Structs, whatever. And it lives in the right Layer of our > application. Not the Business Logic Layer. It lives much higher in the > stack, in the Application Layer. > > And so... it's a problem you can work around, and I'd wager that in > many if not most cases, your application code will be the better for > it. So, with that said, it's difficult for me to anticipate a "I can't > write this page I need because DM doesn't have this feature" sort of > use-case, but if you can come up with one I'll do my best to give it > some genuine consideration. > > Thanks, -Sam > > > -- "You have granted me life and steadfast love, and your care has preserved my spirit." Job 10:12 "The LORD is my chosen portion and my cup . . . indeed, I have a beautiful inheritance." Psalm 16:5-6 "Give what you can ... take nothing back!" --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "DataMapper" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/datamapper?hl=en -~----------~----~----~----~------~----~------~--~---
