Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I set up my DBIx::Class result classes in this simple case?

Let's suppose I have a the following simplified example database consisting of three tables:

CREATE TABLE people (
    person_id   INTEGER PRIMARY KEY,
    person_name VARCHAR(100)
);

CREATE TABLE events (
    event_id       INTEGER PRIMARY KEY,
    event_name     VARCHAR(100),
    event_creator  INTEGER
                   CONSTRAINT fk_event_creator REFERENCES people(person_id)
);

CREATE TABLE event_attendees (
    event_id  INTEGER NOT NULL
              CONSTRAINT fk_event_attendee_event
              REFERENCES events(event_id),
    person_id INTEGER NOT NULL
              CONSTRAINT fk_event_attendee_person
              REFERENCES people(person_id),
    role      CHAR(1), -- O: organizer, P: performer, S: speaker, G: guest
    CONSTRAINT pk_event_attendees PRIMARY KEY (event_id, person_id)
);

Given an event_id, I might want to query for the names of all organizers, given a person_id I might want to find names of all events where this person is a guest or creator of the event so on and so forth.

I know how to do all that using simple SQL. Could you tell me which result classes I need to set up and what kinds of relationships I need to specify when using DBIx::Class?

like image 201
Sinan Ünür Avatar asked Feb 18 '10 18:02

Sinan Ünür


1 Answers

Are you familiar with DBIx::Class::Schema::Loader? Although it can be used in one-off scripts to create a DBIC schema dynamically in memory, it also has the ability to run in a "one-shot" mode where it writes the schema definitions to disk for you to edit and build on, and it's way more advanced than you might think.

First, you want to have the schema actually present in a database, so that the loader can scan it. Then you do something like:

perl -MDBIx::Class::Schema::Loader=make_schema_at \
-e 'make_schema_at("MyApp::Schema", {dump_directory=>"schema_out"},' \
-e '["dbi:DBType:connstring", "user", "pass"]);'

(where "MyApp::Schema" is the package name you want the generated schema classes to share, and "schema_out" is the directory you want them to be generated in).

After this, you can either edit the generated schema classes, or, if you find that the loader does a good enough job (or at least a good enough job that you don't need to edit anything above the "DON'T EDIT ABOVE THIS LINE" line), you can decide that the schema in the DB is your primary source, and save the Schema::Loader script to be run again to re-generate the classes automatically if the DB changes.

Update

Parts of the above schema don't get processed correctly with DBIx::Class::Schema::Loader v0.05002 because Sinan managed to find a bug! Foreign key constraints weren't parsed correctly if the "references" part and the column name weren't on the same line.

The bug is fixed in DBICSL git, but since the fix isn't released yet, here's what the relations should look like (I'm leaving out the column definitions to save space; they should be just as the loader currently generates them).

EventAttendee.pm

__PACKAGE__->set_primary_key(qw(event_id person_id));

__PACKAGE__->belongs_to(
    "event" => "MyApp::Schema::Result::Event",
    { event_id => "event_id" },
    {}
);

__PACKAGE__->belongs_to(
    "person" => "MyApp::Schema::Result::Person",
    { person_id => "person_id" },
    {}
);

Event.pm

__PACKAGE__->set_primary_key("event_id");

__PACKAGE__->belongs_to(
    "event_creator" => "MyApp::Schema::Result::Person",
    { person_id => "event_creator" },
    { join_type => "LEFT" },
);

__PACKAGE__->has_many(
    "event_attendees" => "MyApp::Schema::Result::EventAttendee",
    { "foreign.event_id" => "self.event_id" },
);

# Not auto-generated, but you probably want to add it :)
__PACKAGE__->many_to_many(
    "people_attending" => "event_attendees" => "person"
);

People.pm

__PACKAGE__->has_many(
    # It might be wise to change this to "events_created"
    "events" => "MyApp::Schema::Result::Event",
    { "foreign.event_creator" => "self.person_id" },
);

__PACKAGE__->has_many(
    "event_attendees" => "MyApp::Schema::Result::EventAttendee",
    { "foreign.person_id" => "self.person_id" },
);

# Not auto-generated, but you probably want to add it :)
__PACKAGE__->many_to_many(
    "events_attending" => "event_attendees" => "event"
);
like image 160
hobbs Avatar answered Oct 05 '22 20:10

hobbs