[Catalyst] Retrieve all users belong to a category and all its sub categories

Emmanuel Otton otton at mines-albi.fr
Fri Dec 10 09:12:52 GMT 2010


Le 09/12/2010 09:37, linuxsupport a =E9crit :
> I have 3 tables, users, user_cat, and cat, table structure and =

> relationship are setup as follows.
> User.pm
> __PACKAGE__->add_columns(
>   "id", { data_type =3D> "integer", is_nullable =3D> 0 },
>   "username",{ data_type =3D> "text", is_nullable =3D> 1 },
>   "password",  { data_type =3D> "text", is_nullable =3D> 1 },
>   "email_address",  { data_type =3D> "text", is_nullable =3D> 1 },
>   "first_name",  { data_type =3D> "text", is_nullable =3D> 1 },
>   "last_name",  { data_type =3D> "text", is_nullable =3D> 1 },
>   "active",  { data_type =3D> "integer", is_nullable =3D> 1 },
> );
> __PACKAGE__->set_primary_key("id");
> __PACKAGE__->has_many("usercats", "Example::Schema::Result::UserCat",{ =

> "foreign.user_id" =3D> "self.id <http://self.id/>" },);
> __PACKAGE__->many_to_many(cats =3D> 'usercats', 'cat');
>
> UserCat.pm
> __PACKAGE__->add_columns(
>   "user_id",  { data_type =3D> "integer", is_nullable =3D> 0 },
>   "cat_id",  { data_type =3D> "integer", default_value =3D> 0, is_nullabl=
e =

> =3D> 0 },
> );
> __PACKAGE__->set_primary_key("user_id", "cat_id");
> __PACKAGE__->belongs_to("user", "Example::Schema::Result::User", { id =

> =3D> "user_id" },{ join_type =3D> "LEFT" },);
> __PACKAGE__->belongs_to("cat", "Example::Schema::Result::Cat", { id =3D> =

> "cat_id" },{ join_type =3D> "LEFT" },);
>
> Cat.pm
> __PACKAGE__->add_columns(
>   "id",  { data_type =3D> "integer", is_nullable =3D> 0 },
>   "cat_name",  { data_type =3D> "text", is_nullable =3D> 0 },
>  "parent_id",  { data_type =3D> "integer", is_nullable =3D> 0 },
> );
> __PACKAGE__->set_primary_key("id","parent_id");
> __PACKAGE__->has_many("usercat","Example::Schema::Result::UserCat",{ =

> "foreign.cat_id" =3D> "self.id <http://self.id/>" },);
> __PACKAGE__>many_to_many("allcat", "usercat', "cat");
>
> I am able to retrieve all users in any particular category using =

> "allcat" many_to_many relationship.
Are you, really ?
The navigation path you indicate in your "allcat" many_to_many =

relationship does not seem to lead from Cat to Users but from Cat to Cat =

itself, which seems of no practical use.

I rather would have written:
__PACKAGE__->many_to_many("users","usercat","user");

So that I can get the users in any category this way:
my $cat =3D $schema->resultset("Cat)->find($my_cat_id);
my @users_belonging_directly_to_that_category =3D $cat->users;

> In cat table I have both category and sub category, if a row have =

> parent_id > 0 then it is sub category.
> How can I get all users belong to one category and its sub categories?
Now to get recursively all users belonging to this category AND all =

sub-categories is another problem.

The procedural/Perlish/DBIx::Classish solution would be:
# first, add to Cat.pm an accessor to the sub-categories
__PACKAGE__->has_many("subcats",""Example::Schema::Result::Cat",{ =

"foreign.parent_id" =3D> "self.id <http://self.id/>" });

# then, in your program:
my $cat =3D $schema->resultset("Cat)->find($my_cat_id);
my @users_belonging_to_that_category_and_its_subcats =3D $cat->users;

my @subcats =3D $cat->subcats;
for my $subcat (@subcats)
     push @users_belonging_to_that_category_and_its_subcats , $cat->users;
}
# assuming the reflexive relationship is one level only
# i.e. no sub-sub-..etc..-categories


A (better in my -humble- opinion) solution would be more SQL-oriented =

(after all, isn't the declarative SQL way the reason why we all use =

RDBMSes ?)

First of all, create a view giving what you want:

create view users_by_cat_and_subcats as
select cat.id cat_id, user.*
   from cat join usercat on usercat.cat_id =3D cat.id
   join user on user.id =3D usercat.user_id
union select parent.cat.id cat_id , user.*
   from cat parent_cat join cat child_cat on child_cat.parent_id =3D =

parent_cat.cat_id
   join usercat on usercat.cat_id =3D child_cat.id
   join user on user.id =3D usercat.user_id ;

Then create (automatically, by make_schema_at) a UsersByCatAndSubcat.pm =

to map this table to a DBIx::Class and just use it:
my @users_belonging_to_that_category_and_its_subcats =3D
    $schema->resultset('UsersByCatAndSubcat')->search( { cat_id =3D> =

$my_category } )->all;

It's always difficult to use the right tool, and I've seen ugly things =

like simple left joins replaced by 15 lines of while, if defined, =

push,etc...
RDBMSes have dominated our domain for nearly forty years for a reason, =

which is the expressive power of SQL, itself based on strong theoretical =

mathematical background.
My advice is: don't forget you have a RDBMS, and thus the full power of =

SQL, at your disposal.

Sorry for this answer to a slightly off-catalyst-topic question; I will =

subscribe to the DBIx::Class list right away.

-- =

Emmanuel Otton - Ecole des mines d'Albi-Carmaux - d=E9partement informatiqu=
e - T=E9l: 05 63 49 30 86

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20101210/4b804=
06f/attachment.htm


More information about the Catalyst mailing list