Complex many to many example
From ClassDBI
The following program should illustrate some of the points in the ComplexManyToMany cookbook example.
Bugfixes are welcome!
#!/usr/bin/perl -w --
#
# storyimage.t - Class::DBI many-to-many relationship test script
#
#
# Usage: perl -MTest::Harness='$verbose,runtests' \
# -we '$verbose=1; runtests @ARGV' storyimage.t
#
# Usage: storyimage.t
#
#
# If you wish to KEEP the database at end of test, then
# set the STORYIMAGE_DONT_DELETE_DB environment variable.
#
# To inspect the database created by running the test, run:
# dbish dbi:SQLite:dbname=./storyimage.sqlite
use 5.005_62; # for using our()
use strict;
use warnings;
use diagnostics;
our ($VERSION) = sprintf '%d.%03d', q$Revision: 1.15 $ =~ /(\d+)/g;
our ($DEBUG) = 1;
our ($DBFILE) = "./storyimage.sqlite";
# Security stuff (to make script -T clean)
$ENV{PATH} = "/usr/bin:/bin:/usr/local/bin";
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
#
# Base class
#
package MyDB;
use base qw( Class::DBI::SQLite );
use Data::Dumper;
$Data::Dumper::Indent = 1;
use Test::More tests => 15;
our (%SQL_CREATE);
__PACKAGE__->set_db( 'Main', "dbi:SQLite:dbname=$DBFILE", , );
#
# Story
#
package Story;
use base qw( MyDB );
$MyDB::SQL_CREATE{stories} = "
CREATE TABLE stories (
story_id integer primary key,
title varchar,
author varchar,
content text
);
";
Story->table("stories");
Story->columns( All => qw(story_id title author content) );
Story->has_many( images => StoryImage => { order_by => 'priority' } );
#
# StoryImage
#
package StoryImage;
use base qw( MyDB );
$MyDB::SQL_CREATE{story_images} = "
CREATE TABLE story_images (
id integer primary key,
story integer,
image integer,
priority integer,
unique (story, image)
);
";
StoryImage->table("story_images");
StoryImage->columns( All => qw(id story image priority) );
StoryImage->columns( TEMP => qw(image_id file_name caption) );
StoryImage->has_a( image => 'Image' );
StoryImage->has_a( story => 'Story' );
StoryImage->add_trigger( after_update => sub { shift->image->update } );
StoryImage->add_trigger(
before_create => sub {
my ($data) = @_;
my %image_data;
foreach my $column (qw(image_id caption file_name)) {
$image_data{$column} = delete( $data->{$column} )
if exists $data->{$column};
}
$data->{image} = Image->find_or_create( \%image_data );
}
);
sub file_name { shift->image->file_name(@_) }
sub caption { shift->image->caption(@_) }
sub stories { shift->image->stories(@_) }
#
# Image
#
package Image;
use base qw( MyDB );
$MyDB::SQL_CREATE{images} = "
CREATE TABLE images (
image_id integer primary key,
file_name varchar,
caption varchar
);
";
Image->table("images");
Image->columns( All => qw(image_id file_name caption) );
Image->has_many( stories => [ StoryImage => 'story' ], "image" );
#
# Main program
#
package MyDB;
# Set up DB
if ( __PACKAGE__->can('db_Main') ) {
if ( !-f $DBFILE ) {
diag "DEBUG: Setting up DB '$DBFILE' (did not find old DB).\n"
if $DEBUG;
__PACKAGE__->db_Main->do($_) for values %MyDB::SQL_CREATE;
}
else {
diag "DEBUG: Found existing DB '$DBFILE'.\n" if $DEBUG;
}
}
else {
die "ERROR: db_Main method not available in "
. __PACKAGE__
. ". Setup failed.\n";
}
# Populate DB
my $story = Story->find_or_create(
{
title => "A Modest Proposal",
author => "Jonathan Swift",
content =>
"It is a melancholy object to those who walk through this...",
}
);
isa_ok( $story, "Story", q("A Modest Proposal") );
my $other_story = Story->find_or_create(
{
title => "The Devil's Dictionary",
author => "Ambrose Bierce",
content =>
"air, n.: A nutritious substance supplied by a bountiful Providence"
. " for the fattening of the poor."
}
);
isa_ok( $other_story, "Story", q("The Devil's Dictionary") );
my $first_storyimage = $story->add_to_images(
{
file_name => "pretty.jpg",
caption => "A pretty angel.",
priority => 100, # low
}
);
isa_ok( $first_storyimage, "StoryImage", q(first story's image 'pretty.jpg') );
my $other_storyimage = $other_story->add_to_images(
{
image_id => $first_storyimage->image,
priority => 200, # really, really low
}
);
isa_ok( $other_storyimage, "StoryImage", q(other story's image 'pretty.jpg') );
#
# Story tests
#
my $all_stories = Story->retrieve_all;
is( $all_stories->count, 2, "Number of stories" )
or $DEBUG
and diag Dumper $all_stories;
my $first_story = $all_stories->first;
isa_ok( $first_story, "Story", "First story" );
my $first_story_images = $first_story->images;
is( $first_story_images->count, 1, "Number of images in first story" )
or $DEBUG
and diag Dumper $first_story_images;
my $first_story_first_image = $first_story_images->first;
isa_ok( $first_story_first_image, "StoryImage", "First story's first image" );
is( $first_story_first_image->file_name,
"pretty.jpg", "First story image file_name" );
is_deeply(
[ $first_story_first_image->stories ],
[ 1, 2 ],
"Story id's where first story image is used"
)
or $DEBUG
and diag Dumper [ $first_story_first_image->stories ];
is_deeply(
[ map { $_->title } $first_story_first_image->stories ],
[ "A Modest Proposal", "The Devil's Dictionary" ],
"Story titles where first story image is used"
)
or $DEBUG
and diag Dumper [ map { $_->title } $first_story_first_image->stories ];
is(
$first_story_first_image->caption,
"A pretty angel.",
"First storyimage caption (before change)"
);
$first_story_first_image->caption("A pretty devil.");
$first_story_first_image->image->update; # Boo! Hiss!
is(
$first_story_first_image->caption,
"A pretty devil.",
"First storyimage caption (after change)"
);
#
# Image tests
#
my $all_images = Image->retrieve_all;
is( $all_images->count, 1, "Total number of images" );
my $first_image = $all_images->first;
isa_ok( $first_image, "Image", "First found image" );
#
# Functions starteth yonder
#
END {
if ( !defined $ENV{STORYIMAGE_DONT_DELETE_DB} ) {
diag
"DEBUG: Pulling down DB '$DBFILE' (set \$ENV{STORYIMAGE_DONT_DELETE_DB} to keep DB.)"
if $DEBUG;
unlink $DBFILE || die "Cannot unlink dbfile '$DBFILE': $!\n";
}
else {
diag
"DEBUG: Will not delete DB file '$DBFILE' (unset \$ENV{STORYIMAGE_DONT_DELETE_DB} to delete the DB at end of test run.)
DEBUG: run 'dbish dbi:SQLite:dbname=$DBFILE' to inspect DB contents.\n"
if $DEBUG;
}
}

