On 9/11/06 4:04 AM, Jonathan Vanasco wrote:
> i've spent the past 4hours trying to coerce some sql into Rose, and
> its not working.  perhaps someone here can point me in the right
> direction.
> 
> My SQL is such:
> SELECT
> account.* ,
> wants_item.*,
> item.*
> FROM
> account2item_has has_item
> LEFT JOIN
> account2item_wants wants_item ON has_item.item_id = wants_item.item_id
> LEFT JOIN
> account accountWanting ON wants_item.account_id = accountWanting.id
> LEFT JOIN
> item ON wants_item.item_id = item.id
> WHERE
> accountWanting.id = ?
> 
> i have rose objects / managers created for all of the associated tables
> account
> item
> has_item
> wants_item
>
> i can't seem to figure out how to load this via ROSE though.  i'm not
> sure on which class I should start the search on.
  
You should start the search on the class of the "parent" ("primary table")
objects that you expect to be returned.  It's usually the first table in the
FROM clause if you were to write the SQL by hand.  Above, it's the
account2item_has table.  Let's assume classes like:

account2item_has    - My::ItemHas
account2item_wants  - My::ItemWants
account             - My::Account
item                - My::Item

Let's further assume relationship like this:

    My::ItemHas:
        relationships:
            want_items =>
            {
              type   => 'one to many',
              class  => 'My::ItemWants',
              column_map => { item_id => 'item_id' },
            }
    
    My::ItemWants:
        relationships:
            account =>
            {
              type  => 'many to one',
              class => 'My::Account',
              column_map => { account_id => 'id' },
            },
            item =>
            {
              type  => 'many to one',
              class => 'My::Item',
              column_map => { item_id => 'id' },
            },

Now assume your My::ItemHas::Manager version of the get_objects() method is
named get_item_has_records().  Here's the call:

    $item_has_recs =
      My::ItemHas::Manager->get_item_has_records(
        with_objects => [ 'want_items.account', 'want_items.item' ],
        query        => [ 'want_items.account.id' => 123 ]);

You could make the same call directly on Rose::DB::Object::Manager's
get_objects() method if you pass an explicit object_class parameter:

    $item_has_recs =
      Rose::DB::Object::Manager->get_objects(
        object_class => 'My::ItemHas',
        with_objects => [ 'want_items.account', 'want_items.item' ],
        query        => [ 'want_items.account.id' => 123 ]);

Either one produces SQL that looks roughly like this:

SELECT 
  t1.id AS t1_id,
  t1.item_id AS t1_item_id,
  ...
  t2.account_id AS t2_account_id,
  t2.id AS t2_id,
  t2.item_id AS t2_item_id,
  ...
  t3.aname AS t3_aname,
  t3.id AS t3_id,
  ...
  t4.id AS t4_id,
  t4.iname AS t4_iname,
  ...
FROM
  account2item_has t1
  LEFT OUTER JOIN account2item_wants t2 ON (t1.item_id = t2.item_id)
  LEFT OUTER JOIN account t3 ON (t2.account_id = t3.id)
  LEFT OUTER JOIN item t4 ON (t2.item_id = t4.id)
WHERE
  t3.id = ?

which is what you were asking for above, as far as I can tell.  The objects
returned will be My::ItemHas objects with related "stuff" hanging off them.

All these left joins mean that the stuff hanging off may or may not be
there, so you'll have to do a lot of checking when you reach into each
My::ItemHas record.  You can do that by calling each related object accessor
method and checking for a defined value, but that will cause a database
query if there were no related objects found during the manager query.
Example:

    $item_has_recs =
      My::ItemHas::Manager->get_item_has_records(...);

    foreach my $hi_rec (@$item_has_recs)
    {
      # No db query sent *if* some related My::ItemWants objects were found
      # for this My::ItemHas record during the Manager query.  If not, then
      # this will cause a query to be run to check for such items.
      my $want_item_recs = $hi_rec->want_items;
      ...

If you want to avoid that second check and just accept the situation when no
related objects were found during the Manager query, use the
has_loaded_related() Helper method:

http://search.cpan.org/dist/Rose-DB-Object/lib/Rose/DB/Object/Helpers.pm#has
_loaded_related

    foreach my $hi_rec (@$item_has_recs)
    {
      if($hi_rec->has_loaded_related('want_items'))
      {
        # No database query sent; objects already fetched
        my $want_item_recs = $hi_rec->want_items;
        
        foreach my $wi_rec (@$want_item_recs)
        {
          next  unless($wi_rec->has_loaded_related('account'));

          # No database query sent; object already fetched
          print $wi_rec->account->first_name; # or whatever
        }
        ...
      }
    }

and so on, for each possibly-loaded related object hanging off each
My::ItemHas record.

> is something like this even reasonable in Rose ( i'm sure its possible.
> everything seems possible.  it just seems like it could be a bit awkward)

It's only as awkward as your table structure, classes, and relationships may
make it :)  I just guessed at the relationship names, types, and so on.
Your setup may be different.  For example, in the many-to-one relationships
my code above could be foreign keys in your code.  And the one-to-many
relationship could be many-to-one or one-to-one or a foreign key.  All it'd
change is perhaps the name of the relationships (singular/plural).  The
Manager query would be the same, using the relationship names chained in dot
notation.

The Manager interface is designed to allow people to think only in terms of
objects and relationships.  If you think in SQL instead, you just need to
know what SQL is generated for each Manager param.  In this case, you'd need
to know that with_objects => [ ... ] causes the table for the class of each
listed relationship to be LEFT JOINed, with the join conditions pulled from
the relationship definitions.  After that, it's just a matter of defining
the right relationships and stringing them together with dots in the right
places in the Manager call.

-John



-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Rose-db-object mailing list
Rose-db-object@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/rose-db-object

Reply via email to