[Dbix-class] RE: Inheritance question..

Howe, Tom (IDEAS PRACTICE AREAS) Tom.Howe at MorganStanley.com
Thu Jan 22 11:12:34 GMT 2009


Hi Oleg,
Thanks for your advice. I've tried this and it might be the way to go for me. Id prefer to be able to have a resultset object for the subclasses as well so that I can do $rs = $schema->resultset('ClassA')->new(...);

That way it would ensure that the only the correct columns were available when creating a new row for classA.

I would also be able to search on the subclass columns - why the need to use frozen columns, why cant you just use add_columns to add extra cols to the already defined baseclass ones for the subclass? Maybe this comes down to having just one result_source under the hood.

I have managed via a custom resultset object to use set proxy methods at row creation by intercepting the new_result method.

sub new_result {
  my ($self, at args) = @_;

  #get actual cols for the table
  my @cols = $self->result_source->columns;

  # look for provided args that are not in table
  # assume they are proxied args
  my %proxyArgs;
   foreach my $k (keys %{$args[0]}) {
      if (!grep /$k/, @cols) {
        $proxyArgs{$k} = delete $args[0]{$k};
     }
  }
  # create new row
  my $row = $self->next::method(@args);

  # set proxied args
  foreach my $k (keys %proxyArgs) {
    $row->$k($proxyArgs{$k});
  }
}


with this in place, I can now do:

$schema->resultset('SubClass')->create( subClassCol1=> 'xxx', subClassCol2=>'yyy', baseClassCol=> 'zzz');

The subClassCol1 and subClassCol2 live in the sub class's table and the baseClassCol lives in the base class's Table.
By this method I also ensure that a base class row exists for every sub class.

I need to experiment more, but this should allow me to have Subclass and baseclass resultsets, both searchable on all columns. Just need to ensure I can get the relationships correct to my other objects.


________________________________
From: Oleg Pronin [mailto:syber.rus at gmail.com]
Sent: 22 January 2009 01:40
To: DBIx::Class user and developer list
Subject: Re: [Dbix-class] RE: Inheritance question..

1) DBIx::Class::DynamicSubclass will automaticaly subclass your row object when
     a) fetched from DB
     b) created (->new or ->create)
     c) changed subclassing column
         For example when type = null row is BaseClass, when type = 1 row is ClassA, when 2 row is ClassB.
         my $row = $rs->new({type => 1}); # $row is ClassA
         $row->type(2); #$row is ClassB
         $row->type(undef); #$row is BaseClass

2) You can set up dynamic columns with subclassing if you use DBIx::Class::DynamicSubclass + DBIx::Class::FrozenColumns. But there is caveat: you will not be able to use 'frozen' columns in WHERE clause in SQL.
    The idea is:

      package BaseClass;
      ->load_components(qw/DynamicSubclass FrozenColumns Core/);
      ->add_columns(qw/all_columns_that_are_common_to_all_subclasses + type + data/);
      #data must be of type blob or text (depends on storage engine)
      ->typecast_map(type => {1 => 'ClassA', 2 => 'ClassB'});

      package ClassA;
      use parent 'BaseClass';
      ->add_frozen|dumped|json_columns(data => qw/all_columns_that_are_specific_to_ClassA    for_example/);

      package MyCode;

      my $row = $rs->create({type => 1});
      $row->for_example('hello'); #ok
      $row->type(2);
      $row->for_example('hello'); # error

      $rs->new({ type=> 1, for_example => 'hello' }); #that's ok too

This is not ideal mechanism, but it quite useful and it helps me many times.


2009/1/21 Howe, Tom (IDEAS PRACTICE AREAS) <Tom.Howe at morganstanley.com<mailto:Tom.Howe at morganstanley.com>>
Further to my question on inheritance, I have been trying subclassing and the proxy method options and have a couple of other questions.


First, in an attempt to try the single flattened style where all Foo object reside in one table, I tried the cookbook entry on "Dynamic Sub-classing DBIx::Class proxy classes".

I set up a db table called foo, created a Foo class with an inflate_result() method and a Foo::Noisy class that inherited from the Foo class.

I can now create the Foo object and have it inflate into a Foo::Noisy class.

But, I cant get a Foo::Noisy resultset.

I thought maybe I could define Foo with only the common columns, then create a Foo::Noisy subclass that would inherit the columns of Foo and then I could create a default value for 'type' and use add_columns() to define the additional NoisyFoo column 'decibels'.

But it seems the only resultset I can get is for Foo. So anyone creating objects could inadvertently set a 'colour' for a NoisyFoo or a 'decibel' for ColouredFoo.





The second thing I tried was to create 3 separate tables for Foo, NoisyFoo and ColouredFoo. Then I created a Foo::Noisy class with a has_one relationship to the Foo class using a proxy=>[] defined to allow transparent setting/getting of the 'name'.

This works to a degree in that I can do :

my $nf = $schema->resultset('Foo::Noisy')->create({id=>1, decibels=>110});
$nf->name('mynoisyfoo');

But it fails if I try to do:
my $nf = $schema->resultset('Foo::Noisy')->create({id=>1, decibels=>110, name=>'noisyfoo'});

Giving the error:
DBIx::Class::ResultSet::create(): No such column name on DB::Foo::Noisy

The proxy doesn't seem to be in affect during creation.

Also, if I *don't* set the name, I don't get a row in the Foo table.
Is there a way to enforce that the row is created?

Otherwise, if I want to search for all Foos, I cant do it on the common table.

Is there anyway to ensure the Foo row is created and grn can be passed on create?





> -----Original Message-----
> From: Howe, Tom (IDEAS PRACTICE AREAS)
> Sent: 21 January 2009 09:42
> To: DBIx::Class user and developer list
> Subject: [Dbix-class] Inheritance question..
>
> Class structure is something like this..
>

+------+    +------------+              +-------------+
|  Bar |---o|  Relation  |o - - - - - - |     Foo     |abstract
+------+    +------------+              +-------------+class
|  id  |    |   attrs    |o             |      id     |
| name |    +------------+ \_________   |     name    |
+------+                    \        \  +-------------+
                            \        \___^_____    ^
                             \           |     \   |
                              \ +------------+  +-------------+
                               \|  NoisyFoo  |  | ColouredFoo |
                                +------------+  +-------------+
                                |     id     |  |     id      |
                                |  decibels  |  |    color    |
                                +------------+  +-------------+

> +-------------+
>
> Bar has many Relations
> NoisyFoo has many Relations
> ColouredFoo has many Relations
> Bar many-many NoisyFoos
> Bar many-many ColouredFoos
>
> (Bar many-many Foos?)
>
>
> NoisyFoo and ColouredFoo inherit from Foo
>
> It actually gets more complicated than this because
> - Bar and Foo both inherit from a base object.
> - All objects have a unique id are connected via Relation objects
> - The relation object also contains levels of inheritance
>
> I want to be able to do queries like
>
> 1) Given a Bar, find all related Foo objs
> 2) Given a NoisyFoo or Coloured Foo, find all related Bars
> 3) For all bars with name matching "%str%" find all NoisyFoos
> with decibels > '100'
>
> Im not sure whether it would be better to
> a) retain Foo, NoisyFoo and ColouredFoo as a 3 separate tables,
> b) flatten to 2 tables, so NoisyFoo and ColouredFoo get their
> own name column
> c) flatten all Foos to 1 Foo table and create a Type column
> to differentiate.
>
>
> Questions:
>
>  Is it possible to create Foo, NoisyFoo and ColouredFoo as
> separate tables (a), then create corresponding DBIx proxy
> classes for them in such a way that when I create a new
> NoisyFoo row, the 'name' value is automatically entered into
> the Foo table? And when I retrieve the row it is
> automatically read in from the Foo table?
>
>
> If I were to flatten to 1 table per subclass (b), how would I
> write a relationship that enabled me to perform query (1)
> above; find all Foos for a given Bar.
> --------------------------------------------------------
>
> NOTICE: If received in error, please destroy and notify
> sender. Sender does not intend to waive confidentiality or
> privilege. Use of this email is prohibited when received in error.
>
> _______________________________________________
> List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
> IRC: irc.perl.org#dbix-class<http://irc.perl.org#dbix-class>
> SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
> Searchable Archive:
> http://www.grokbase.com/group/dbix-class@lists.scsys.co.uk
>
--------------------------------------------------------

NOTICE: If received in error, please destroy and notify sender. Sender does not intend to waive confidentiality or privilege. Use of this email is prohibited when received in error.

_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class<http://irc.perl.org#dbix-class>
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@lists.scsys.co.uk
--------------------------------------------------------

NOTICE: If received in error, please destroy and notify sender. Sender does not intend to waive confidentiality or privilege. Use of this email is prohibited when received in error.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/dbix-class/attachments/20090122/efc3691c/attachment-0001.htm


More information about the DBIx-Class mailing list