package Storm::Meta::Relationship::ManyToMany; { $Storm::Meta::Relationship::ManyToMany::VERSION = '0.19'; } use Moose; use MooseX::StrictConstructor; use MooseX::Types::Moose qw( Str Undef); extends 'Storm::Meta::Relationship'; use Storm::Types qw( StormForeignKeyConstraintValue ); has 'junction_table' => ( is => 'rw', isa => Str, writer => '_set_junction_table', lazy_build => 1, ); has 'local_match' => ( is => 'rw' , isa => Str, writer => '_set_local_match', lazy_build => 1, ); has 'foreign_match' => ( is => 'rw' , isa => Str, writer => '_set_foreign_match', lazy_build => 1, ); has 'on_delete' => ( is => 'rw', isa => StormForeignKeyConstraintValue, default => 'RESTRICT', ); has 'on_update' => ( is => 'rw', isa => StormForeignKeyConstraintValue, default => 'CASCADE', ); sub _build_junction_table { my ( $self ) = @_; my $local = $self->associated_class->meta->storm_table->name; my $foreign = $self->foreign_class->meta->storm_table->name; return join '_', sort $local, $foreign; } sub _build_local_match { my ( $self ) = @_; my $table = $self->associated_class->meta->storm_table->name; my $column = $self->associated_class->meta->primary_key->column->name; return $table . '_' . $column; } sub _build_foreign_match { my ( $self ) = @_; my $table = $self->foreign_class->meta->storm_table->name; my $column = $self->foreign_class->meta->primary_key->column->name; return $table . '_' . $column; } sub _add_method { my ( $self, $instance, @objects ) = @_; my $orm = $instance->orm; confess "$instance must exist in the database" if ! $orm; my $table = $self->junction_table; my $primary_key = $self->local_match ? $self->local_match : $self->associated_class->meta->primary_key->column->name; my $foreign_key = $self->foreign_match? $self->foreign_match : $self->foreign_class->meta->primary_key->column->name; my $sth = $orm->source->dbh->prepare("INSERT INTO $table ($primary_key, $foreign_key) VALUES (?, ?)"); eval { for (@objects) { $sth->execute($instance->identifier, $_->identifier); } }; confess $@ if $@; return 1; } sub _remove_method { my ( $self, $instance, @objects ) = @_; my $orm = $instance->orm; confess "$instance must exist in the database" if ! $orm; my $table = $self->junction_table; my $primary_key = $self->local_match ? $self->local_match : $self->associated_class->meta->primary_key->column->name; my $foreign_key = $self->foreign_match? $self->foreign_match : $self->foreign_class->meta->primary_key->column->name; my $sth = $orm->source->dbh->prepare("DELETE FROM $table WHERE $primary_key = ? AND $foreign_key = ?"); for (@objects) { $sth->execute($instance->identifier, $_->identifier); } return 1; } sub _iter_method { my ( $self, $instance ) = @_; my $orm = $instance->orm; confess "$instance must exist in the database" if ! $orm; my $link_table = $self->junction_table; my $foreign_table = $self->foreign_class->meta->storm_table->name; my $primary_key = $self->local_match ? $self->local_match : $self->associated_class->meta->primary_key->column->name; my $foreign_key = $self->foreign_match? $self->foreign_match : $self->foreign_class->meta->primary_key->column->name; my $foreign_primary_key = $self->foreign_class->meta->primary_key->column->name; my $query = $orm->select($self->foreign_class); $query->join($link_table); $query->where("`$link_table.$foreign_key`", '=', "`$foreign_table.$foreign_primary_key`"); $query->where("`$link_table.$primary_key`", '=', $self->associated_class->meta->primary_key->get_value($instance)); return $query->results; } sub _build_handle_methods { my ( $self ) = @_; my %methods; for my $method_name ($self->_handles) { my $action = $self->get_handle($method_name); my $code_ref; if ($action eq 'add') { $code_ref = sub { &_add_method($self, @_) } } elsif ($action eq 'remove') { $code_ref = sub { &_remove_method($self, @_) } } elsif ($action eq 'iter' ) { $code_ref = sub { &_iter_method($self, @_) } } else { confess "could not create handle $method_name because $action is not a valid action" } # wrap the method my $wrapped_method = $self->associated_class->meta->method_metaclass->wrap( name => $method_name, package_name => $self->associated_class, body => $code_ref, ); $methods{$method_name} = $wrapped_method; } return \%methods; } no Moose; __PACKAGE__->meta()->make_immutable(); 1;