From aabe8506b6746fca2c204828673d0d04968a9a9d Mon Sep 17 00:00:00 2001 From: Dmitry Latin Date: Sat, 4 Jan 2014 16:43:11 +0400 Subject: [PATCH 1/2] Extend sub-classing receipt and add examples --- examples/ComponentHooks/db/db.db | Bin 0 -> 8192 bytes examples/ComponentHooks/db/db.sql | 56 +++++++ examples/ComponentHooks/dbtest.pl | 150 ++++++++++++++++++ examples/ComponentHooks/lib/DB.pm | 14 ++ .../lib/DB/Result/Realty/Apartment/Rent.pm | 9 ++ .../DB/Result/Realty/Apartment/Rent/Daily.pm | 9 ++ .../lib/DB/Result/Realty/Apartment/Sell.pm | 9 ++ .../lib/DB/Result/Realty/House/Rent.pm | 9 ++ .../lib/DB/Result/Realty/House/Sell.pm | 9 ++ .../lib/DB/ResultRole/Realty/Rent.pm | 11 ++ .../lib/DB/ResultRole/Realty/Rent/Daily.pm | 12 ++ .../lib/DB/ResultRole/Realty/Sell.pm | 12 ++ .../lib/DB/Shared/Result/Realty.pm | 11 ++ .../lib/DB/Shared/Result/Realty/Apartment.pm | 9 ++ .../lib/DB/Shared/Result/Realty/House.pm | 9 ++ lib/DBIx/Class/Manual/Cookbook.pod | 109 +++++++++++++ 16 files changed, 438 insertions(+) create mode 100644 examples/ComponentHooks/db/db.db create mode 100644 examples/ComponentHooks/db/db.sql create mode 100644 examples/ComponentHooks/dbtest.pl create mode 100644 examples/ComponentHooks/lib/DB.pm create mode 100644 examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent.pm create mode 100644 examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent/Daily.pm create mode 100644 examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Sell.pm create mode 100644 examples/ComponentHooks/lib/DB/Result/Realty/House/Rent.pm create mode 100644 examples/ComponentHooks/lib/DB/Result/Realty/House/Sell.pm create mode 100644 examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent.pm create mode 100644 examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent/Daily.pm create mode 100644 examples/ComponentHooks/lib/DB/ResultRole/Realty/Sell.pm create mode 100644 examples/ComponentHooks/lib/DB/Shared/Result/Realty.pm create mode 100644 examples/ComponentHooks/lib/DB/Shared/Result/Realty/Apartment.pm create mode 100644 examples/ComponentHooks/lib/DB/Shared/Result/Realty/House.pm diff --git a/examples/ComponentHooks/db/db.db b/examples/ComponentHooks/db/db.db new file mode 100644 index 0000000000000000000000000000000000000000..57821ca1ec3813cd36f2cdadfb59c636fef543e9 GIT binary patch literal 8192 zcmeI0O>fgM7{~26UAk&8aX`I#T%ahRv{N>15>g8bsA|_i35cDl(0W=#vvf&%n8c}T z?|iVGrXA4^ocI9TxL`XgNnau{Y3fR6D{*Y+d1CvQ$N5`%|E_8W1RuDbV+D8}$N)l6 z#25fDpl6z%!<3>0d03%W7%L|g2jJO{9CL)|H?vW~OOm;07X$=>*%3&gd$co%z5{xQ zBnSus3q;_Nq<~E*NpM5cw0uFur5zRTc*Iw+kM$LVp)7^#{}Q0f1)8{+iXb2ePy!e( zfz9CuAV2m2KtKPk|8s!O1u#KC5cvNIsB#)qpahpg*`?zB4tK7$Gwdgj+k2#sfCzDl&K5B*=I`$DC zO`}6R+hsk`hjX{ckX1XL-6YJY;kb0{wB>bmuf!}PJO03M`2KO%@(6RBX2@C(+OF%x z2RPAjCf9o)adxLpjGa%~?W;~`2~Ay8-XW}3wo6Et$L(atNSYF`J zaN=)qv?hpCN$H4$6kD&a90KIkeR5B^Lp-2a~ebawr< zFFXVRL12Ce6y#;5{Mmn!{=BxWX`%j0@IH_}((KNso{{3}(>KP{hiPu4FsznNP@lO| z=i5+~)5o%s)pa;H9^=S%hK-hGw@)IPQQJ}i)5u~zb-iVsaDkoNGqUF9A!&YbyFnwc wop8@+mUj(yXKZhI8*eJq{|kUF?)>5x00e=BAn+6^ppx_hK#zU^pnhTX53`Qconnect('dbi:SQLite:db/db.db'); + +# Clear DB +$s->resultset('Realty::House::Sell')->delete; +$s->resultset('Realty::House::Rent')->delete; +$s->resultset('Realty::Apartment::Sell')->delete; +$s->resultset('Realty::Apartment::Rent')->delete; +$s->resultset('Realty::Apartment::Rent::Daily')->delete; + + +# House Sell +my $house_sell_data = { + id => 1, + address => '600011, AD, Dres, s 5', + house_square => 260, + floors => 2, + price_per_meter => 1000, +}; + +ok( + my $house = $s->resultset('Realty::House::Sell')->create($house_sell_data), + 'Realty::House::Sell Added' +); + +$house = $house->get_from_storage( + { + result_class => 'DBIx::Class::ResultClass::HashRefInflator' + } +); + +is_deeply( $house, $house_sell_data, 'Realty::House::Sell retrieved' ); + + +# House Rent +my $house_rent_data = { + id => 2, + address => '600012, AD, Dres, s 5', + house_square => 260, + floors => 2, + min_rent_period => 12, + price_per_month => 800, +}; + +ok( $house = $s->resultset('Realty::House::Rent')->create($house_rent_data), + 'Realty::House::Rent Added' ); + +$house = $house->get_from_storage( + { + result_class => 'DBIx::Class::ResultClass::HashRefInflator' + } +); + +is_deeply( $house, $house_rent_data, 'Realty::House::Rent retrieved' ); + + +# Apartment Sell +my $apartment_sell_data = { + id => 3, + address => '600013, AD, Dres, s 5, app. 255', + square => 50, + rooms => 1, + floor => 15, + price_per_meter => 900, +}; + +ok( + my $apartment + = $s->resultset('Realty::Apartment::Sell') + ->create($apartment_sell_data), + 'Realty::Apartment::Sell Added' +); + +$apartment = $apartment->get_from_storage( + { + result_class => 'DBIx::Class::ResultClass::HashRefInflator' + } +); + +is_deeply( $apartment, $apartment_sell_data, + 'Realty::Apartment::Sell retrieved' ); + + +# Apartment Rent +my $apartment_rent_data = { + id => 4, + address => '600013, AD, Dres, s 6, app. 255', + square => 50, + rooms => 1, + floor => 15, + min_rent_period => 12, + price_per_month => 500, +}; + +ok( + $apartment + = $s->resultset('Realty::Apartment::Rent') + ->create($apartment_rent_data), + 'Realty::Apartment::Rent Added' +); + +$apartment = $apartment->get_from_storage( + { + result_class => 'DBIx::Class::ResultClass::HashRefInflator' + } +); + +is_deeply( $apartment, $apartment_rent_data, + 'Realty::Apartment::Rent retrieved' ); + +# Apartment Rent Daily +my $apartment_rent_daily_data = { + id => 4, + address => '600013, AD, Dres, s 6, app. 255', + square => 50, + rooms => 1, + floor => 15, + min_rent_period => 12, + price_per_day => 500, + checkout_time => '10:00', +}; + +ok( + $apartment + = $s->resultset('Realty::Apartment::Rent::Daily') + ->create($apartment_rent_daily_data), + 'Realty::Apartment::Rent::Daily Added' +); + +$apartment = $apartment->get_from_storage( + { + result_class => 'DBIx::Class::ResultClass::HashRefInflator' + } +); + +is_deeply( $apartment, $apartment_rent_daily_data, + 'Realty::Apartment::Rent::Daily retrieved' ); + +done_testing(); diff --git a/examples/ComponentHooks/lib/DB.pm b/examples/ComponentHooks/lib/DB.pm new file mode 100644 index 000000000..9261042f1 --- /dev/null +++ b/examples/ComponentHooks/lib/DB.pm @@ -0,0 +1,14 @@ +package DB; + +use base qw/DBIx::Class::Schema/; + +__PACKAGE__->load_namespaces; + +__PACKAGE__->load_classes( + { + 'DB::Shared::Result' => + [ 'Realty', 'Realty::Apartment', 'Realty::House', ] + } +); + +1; diff --git a/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent.pm b/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent.pm new file mode 100644 index 000000000..c354aa665 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent.pm @@ -0,0 +1,9 @@ +package DB::Result::Realty::Apartment::Rent; + +use base qw/DB::Shared::Result::Realty::Apartment/; + +__PACKAGE__->table('apartment_rent'); + +__PACKAGE__->load_components('+DB::ResultRole::Realty::Rent'); + +1; diff --git a/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent/Daily.pm b/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent/Daily.pm new file mode 100644 index 000000000..df4478185 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Rent/Daily.pm @@ -0,0 +1,9 @@ +package DB::Result::Realty::Apartment::Rent::Daily; + +use base qw/DB::Result::Realty::Apartment::Rent/; + +__PACKAGE__->table('apartment_rent_daily'); + +__PACKAGE__->load_components('+DB::ResultRole::Realty::Rent::Daily'); + +1; diff --git a/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Sell.pm b/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Sell.pm new file mode 100644 index 000000000..c0772ca3f --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Result/Realty/Apartment/Sell.pm @@ -0,0 +1,9 @@ +package DB::Result::Realty::Apartment::Sell; + +use base qw/DB::Shared::Result::Realty::Apartment/; + +__PACKAGE__->table('apartment_sell'); + +__PACKAGE__->load_components('+DB::ResultRole::Realty::Sell'); + +1; diff --git a/examples/ComponentHooks/lib/DB/Result/Realty/House/Rent.pm b/examples/ComponentHooks/lib/DB/Result/Realty/House/Rent.pm new file mode 100644 index 000000000..ac7517c60 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Result/Realty/House/Rent.pm @@ -0,0 +1,9 @@ +package DB::Result::Realty::House::Rent; + +use base qw/DB::Shared::Result::Realty::House/; + +__PACKAGE__->table('house_rent'); + +__PACKAGE__->load_components('+DB::ResultRole::Realty::Rent'); + +1; diff --git a/examples/ComponentHooks/lib/DB/Result/Realty/House/Sell.pm b/examples/ComponentHooks/lib/DB/Result/Realty/House/Sell.pm new file mode 100644 index 000000000..55315d081 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Result/Realty/House/Sell.pm @@ -0,0 +1,9 @@ +package DB::Result::Realty::House::Sell; + +use base qw/DB::Shared::Result::Realty::House/; + +__PACKAGE__->table('house_sell'); + +__PACKAGE__->load_components('+DB::ResultRole::Realty::Sell'); + +1; diff --git a/examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent.pm b/examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent.pm new file mode 100644 index 000000000..0fdca22c8 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent.pm @@ -0,0 +1,11 @@ +package DB::ResultRole::Realty::Rent; + +use base qw/DBIx::Class::Core/; + +use Class::C3::Componentised::ApplyHooks + -after_apply => sub { + my ($class, $component) = @_; + + $class->add_columns(qw/ min_rent_period price_per_month /); + }; +1; diff --git a/examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent/Daily.pm b/examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent/Daily.pm new file mode 100644 index 000000000..479aaedc7 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/ResultRole/Realty/Rent/Daily.pm @@ -0,0 +1,12 @@ +package DB::ResultRole::Realty::Rent::Daily; + +use base qw/DBIx::Class::Core/; + +use Class::C3::Componentised::ApplyHooks + -after_apply => sub { + my ($class, $component) = @_; + + $class->add_columns(qw/ checkout_time price_per_day /); + $class->remove_columns(qw/ price_per_month /); + }; +1; diff --git a/examples/ComponentHooks/lib/DB/ResultRole/Realty/Sell.pm b/examples/ComponentHooks/lib/DB/ResultRole/Realty/Sell.pm new file mode 100644 index 000000000..eddb67b7c --- /dev/null +++ b/examples/ComponentHooks/lib/DB/ResultRole/Realty/Sell.pm @@ -0,0 +1,12 @@ +package DB::ResultRole::Realty::Sell; + +use base qw/DBIx::Class::Core/; + +use Class::C3::Componentised::ApplyHooks + -after_apply => sub { + my ($class, $component) = @_; + + $class->add_columns(qw/ price_per_meter /); + }; + +1; diff --git a/examples/ComponentHooks/lib/DB/Shared/Result/Realty.pm b/examples/ComponentHooks/lib/DB/Shared/Result/Realty.pm new file mode 100644 index 000000000..17df9e4d3 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Shared/Result/Realty.pm @@ -0,0 +1,11 @@ +package DB::Shared::Result::Realty; + +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('__dummy'); + +__PACKAGE__->add_columns(qw/ id address /); + +__PACKAGE__->set_primary_key('id'); + +1; diff --git a/examples/ComponentHooks/lib/DB/Shared/Result/Realty/Apartment.pm b/examples/ComponentHooks/lib/DB/Shared/Result/Realty/Apartment.pm new file mode 100644 index 000000000..c3ace3168 --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Shared/Result/Realty/Apartment.pm @@ -0,0 +1,9 @@ +package DB::Shared::Result::Realty::Apartment; + +use base qw/DB::Shared::Result::Realty/; + +__PACKAGE__->table('__dummy'); + +__PACKAGE__->add_columns(qw/ square rooms floor /); + +1; diff --git a/examples/ComponentHooks/lib/DB/Shared/Result/Realty/House.pm b/examples/ComponentHooks/lib/DB/Shared/Result/Realty/House.pm new file mode 100644 index 000000000..fee6fdb9c --- /dev/null +++ b/examples/ComponentHooks/lib/DB/Shared/Result/Realty/House.pm @@ -0,0 +1,9 @@ +package DB::Shared::Result::Realty::House; + +use base qw/DB::Shared::Result::Realty/; + +__PACKAGE__->table('__dummy'); + +__PACKAGE__->add_columns(qw/ house_square floors /); + +1; diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index 0cb560bdf..dfcad7a46 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -833,6 +833,115 @@ B 1; +But, what if some of your model has certain columns/relationships/etc, which +another model does not? + +E.g. a DB with realty objects, where each object could be sold or rented. All of +the objects have the same columns (like id, creation date, description, etc.). +In addition selling objects have full price, but renting objects have minimum rent period +and price per month. Lets say there are two types of objects: houses and +apartments (which could have own columns). So, we have the next classes diagram: + + +-------> Realty <-------+ + | | + +--> House <--+ +--> Apartment <--+ + | | | | + Rent Sell Rent Sell + +Realty is the base class. House and Apartment are sub-classed from Realty. Lets +subclass Rent::House, Sell::House, Rent::Apartment and Sell::Apartment. + + package DB::Shared::Result::Realty; + use base 'DBIx::Class::Core'; + + __PACKAGE__->load_components(qw/InflateColumn::DateTime/); + + __PACKAGE__->table('__dummy'); + __PACKAGE__->add_columns(qw/ id created description /); + + __PACKAGE__->add_primary_key('id'); + + 1; + + + package DB::Shared::Result::Realty::House; + use base 'DB::Shared::Result::Realty'; + + __PACKAGE__->table('__dummy'); + + __PACKAGE__->add_columns( + # Add columns specified for houses + ); + + 1; + + + package DB::Shared::Result::Realty::Apartment; + use base 'DB::Shared::Result::Realty'; + + __PACKAGE__->table('__dummy'); + + __PACKAGE__->add_columns( + # Add columns specified for apartment + ); + + 1; + +The good way to add renting/selling properties into needed class is using +components. + + package DB::Component::Rent; + use base 'DBIx::Class::Core'; + + use Class::C3::Componentised::ApplyHooks + -after_apply => sub { + my ($class, $component) = @_; + + $class->add_columns(qw/ min_rent_period price_per_month /); + }; + + 1; + + package DB::Component::Sell; + use base 'DBIx::Class::Core'; + + use Class::C3::Componentised::ApplyHooks + -after_apply => sub { + my ($class, $component) = @_; + + $class->add_columns(qw/ full_price /); + }; + + 1; + +Components could be used via C + + package DB::Result::Realty::House::Rent; + use base 'DB::Shared::Result::Realty::House'; + + __PACKAGE__->table('house_rent'); + + + # NOTE components with add_columns/add_relationship hooks must be loaded + # after ->table(...) calling + + __PACKAGE__->load_components('+DB::Component::Rent'); + + 1; + + package DB::Result::Realty::House::Sell; + use base 'DB::Shared::Result::Realty::House'; + + __PACKAGE__->table('house_sell'); + __PACKAGE__->load_components('DB::Component::Sell'); + + 1; + +You should to use hooks, because without it on C in +C columns will be added into C, +but NOT into C class. Without hooks you +have to copy all columns/relationships/etc into your C<__PACKAGE__> manually. + =head2 Dynamic Sub-classing DBIx::Class proxy classes AKA multi-class object inflation from one table From c7d431e7f0671e30206b325f098ee13583b524a1 Mon Sep 17 00:00:00 2001 From: Dmitry Latin Date: Sat, 4 Jan 2014 16:51:20 +0400 Subject: [PATCH 2/2] Small fix in cookbook --- lib/DBIx/Class/Manual/Cookbook.pod | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index dfcad7a46..8d3da2817 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -921,10 +921,8 @@ Components could be used via C __PACKAGE__->table('house_rent'); - # NOTE components with add_columns/add_relationship hooks must be loaded # after ->table(...) calling - __PACKAGE__->load_components('+DB::Component::Rent'); 1; @@ -933,7 +931,7 @@ Components could be used via C use base 'DB::Shared::Result::Realty::House'; __PACKAGE__->table('house_sell'); - __PACKAGE__->load_components('DB::Component::Sell'); + __PACKAGE__->load_components('+DB::Component::Sell'); 1;