7

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?

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • 1
    A new release of DBIx::Class::Schema::Loader that handles your sample schema should be posted within the next day. As you work on the real thing it would really be great if you could continue testing Schema::Loader against it and report any bugs you find (or supply failing tests), as there's still plenty of room for improvement in Schema::Loader's SQLite support. – hobbs Feb 19 '10 at 12:16
  • 1
    Schema::Loader 0.05003 which fixes this bug is uploaded: http://pause.perl.org:81/incoming/DBIx-Class-Schema-Loader-0.05003.tar.gz – Rafael Kitover Feb 20 '10 at 06:52
  • @Rafael Thanks for the heads up. I downloaded it. Will check it out this weekend. – Sinan Ünür Feb 20 '10 at 13:17

1 Answers1

6

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"
);
hobbs
  • 223,387
  • 19
  • 210
  • 288
  • It writes those too, based on your foreign key constraints and linking tables. – hobbs Feb 18 '10 at 22:49
  • For some reason, I am not able to get that either using your command line or with `dbicdump` using a SQLite database. – Sinan Ünür Feb 19 '10 at 04:19
  • @Sinan: It's due to a bug in Schema::Loader -- see above. – hobbs Feb 19 '10 at 09:20
  • Also a note, the LEFT JOIN annotation at one point there is because your `event_creator` is nullable. Not sure if you meant that. :) – hobbs Feb 19 '10 at 09:44
  • Thank you very much. And, no, I had not meant `event_creator` to be nullable. Missed it when I was simplifying. Good catch. – Sinan Ünür Feb 19 '10 at 10:24