DBIx-ObjectMapper

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

t/11_mapper/lib/MyTest/Basic/ArtistCompileError.pm
t/11_mapper/lib/MyTest/Basic/GenericAccessorArtist.pm
t/11_mapper/lib/MyTest/CO/Artist.pm
t/12_session/000_array.t
t/12_session/000_basic.t
t/12_session/001_query.t
t/12_session/010_relation_basic.t
t/12_session/011_relation_search.t
t/12_session/012_self_relation.t
t/12_session/013_lazyload.t
t/12_session/014_cascade.t
t/12_session/015_default_condition.t
t/12_session/016_belongs_to_cascade.t
t/12_session/017_default_value.t
t/12_session/018_no_cache.t
t/12_session/019_transaction.t
t/12_session/020_share_object.t
t/12_session/021_validation.t
t/12_session/022_change_checker.t
t/12_session/023_arrayref_obj.t
t/12_session/024_has_one_foreign_key.t
t/12_session/025_differ_prop_name_and_col_name.t
t/12_session/026_init_using_accessor_object.t

lib/DBIx/ObjectMapper/Mapper/Instance.pm  view on Meta::CPAN

            unless( $self->unit_of_work ) {
                my $uow = shift ||  confess "need UnitOfWork";
                $self->{unit_of_work} = $uow;
            }
        }
        elsif( $status_name eq 'detached' ) {
            my $class_mapper = $self->instance->__class_mapper__;
            for my $prop_name ( $class_mapper->attributes->property_names ) {
                my $prop = $class_mapper->attributes->property_info($prop_name);
                next unless $prop->type eq 'relation';
                if( $prop->{isa}->is_cascade_detach() ) {
                    if( my $instance = $self->get_val($prop_name) ) {
                        my @instance
                            = ref $instance eq 'ARRAY'
                            ? @$instance
                            : ($instance);
                        $self->unit_of_work->detach($_) for @instance;
                    }
                }
            }
            $self->_clear_cache;
        }
        elsif( $status_name eq 'expired' ) {
            my $class_mapper = $self->instance->__class_mapper__;
            for my $prop_name ( $class_mapper->attributes->property_names ) {
                my $prop = $class_mapper->attributes->property_info($prop_name);
                next unless $prop->type eq 'relation';
                if( $prop->{isa}->is_cascade_reflesh_expire() ) {
                    if( my $instance = $self->get_val($prop_name) ) {
                        my @instance
                            = ref $instance eq 'ARRAY'
                                ? @$instance
                                    : ($instance);
                        for ( @instance ) {
                            my $mapper = $_->__mapper__;
                            if (   $mapper->is_pending
                                || $mapper->is_persistent )
                            {

lib/DBIx/ObjectMapper/Mapper/Instance.pm  view on Meta::CPAN

    };

    $self->change_status('persistent');
    $self->_modify( $new_val );
    $self->unit_of_work->_set_cache($self);
    $self->initialize;

    for my $prop_name ( $class_mapper->attributes->property_names ) {
        my $prop = $class_mapper->attributes->property_info($prop_name);
        next unless $prop->type eq 'relation';
        if( $prop->{isa}->is_cascade_reflesh_expire() ) {
            if( my $instance = $self->get_val($prop_name) ) {
                my @instance
                    = ref $instance eq 'ARRAY'
                    ? @$instance
                    : ($instance);
                $_->__mapper__->reflesh for @instance;
            }
        }
    }
}

lib/DBIx/ObjectMapper/Mapper/Instance.pm  view on Meta::CPAN

    my ( $self ) = @_;
    confess 'it need to be "persistent" status.' unless $self->is_persistent;

    my $reduce_data = $self->reducing;
    my $modified_data = $self->modified_data;
    my $uniq_cond = $self->identity_condition;
    my $class_mapper = $self->instance->__class_mapper__;

    my $result;
    try {
        my @after_cascade;
        for my $prop_name ( $class_mapper->attributes->property_names ) {
            my $prop = $class_mapper->attributes->property_info($prop_name);
            next unless $prop->type eq 'relation';
            if (ref( $prop->{isa} ) eq
                'DBIx::ObjectMapper::Relation::BelongsTo' )
            {
                if ( $prop->{isa}->is_cascade_save_update() ) {
                    $prop->{isa}->cascade_update($self);
                }

                if( $modified_data->{$prop_name} ) {
                    $prop->{isa}->set_val_from_object(
                        $self,
                        $self->get_val($prop_name),
                    );
                }
            }
            elsif ( $prop->{isa}->is_cascade_save_update() ) {
                push @after_cascade, $prop;
            }
        }

        my $new_val;
        if( keys %$modified_data ) {
            my %mod_data;
            for ( keys %$modified_data ) {
                my $prop = $class_mapper->attributes->property_info($_);
                $mod_data{$prop->name} = $modified_data->{$_};
            }

            $result = $class_mapper->update( \%mod_data, $uniq_cond );
            $new_val = DBIx::ObjectMapper::Utils::merge_hashref(
                $reduce_data,
                $modified_data
            );
        }

        for my $c ( @after_cascade ) {
            $c->{isa}->cascade_update( $self );
        }

        $self->_release_many_to_many_event;
        $self->_modify($new_val) if $new_val;
    } catch {
        $self->change_status('detached');
        confess $_[0];
    };

    $self->{is_modified} = 0;
    $self->{modified_data} = +{};

    $self->change_status('expired'); # cascade expire if cascade_reflesh_expire
    return !$result || $self->instance;
}

sub save {
    my ( $self ) = @_;

    confess 'it need to be "pending" status.' unless $self->is_pending;
    my $class_mapper = $self->instance->__class_mapper__;

    try {
        my @after_cascade;
        for my $prop_name ( $class_mapper->attributes->property_names ) {
            my $prop = $class_mapper->attributes->property_info($prop_name);
            if ( $prop->type eq 'relation' ) {
                if (ref( $prop->{isa} ) eq
                    'DBIx::ObjectMapper::Relation::BelongsTo' )
                {
                    if ( my $instance = $self->get_val($prop_name) ) {
                        my @instances
                            = ref $instance eq 'ARRAY'
                            ? @$instance
                            : ($instance);
                        for my $i (@instances) {
                            if( $prop->{isa}->is_cascade_save_update() ) {
                                $prop->{isa}->cascade_save( $self, $i );
                            }
                            else {
                                $prop->{isa}->set_val_from_object( $self, $i );
                            }
                        }
                    }
                }
                elsif( $prop->{isa}->is_cascade_save_update() ) {
                    push @after_cascade, $prop;
                }
            }
        }

        my $reduce_data = $self->reducing;
        my $data = { %$reduce_data, %{$class_mapper->default_value} };
        my $comp_result = $class_mapper->insert(%$data);
        $self->_modify($comp_result);
        $self->initialize;

        for my $c ( @after_cascade ) {
            if ( my $instance = $self->get_val($c->name) ) {
                my @instances
                    = ref $instance eq 'ARRAY'
                    ? @$instance
                    : ($instance);
                for my $i (@instances) {
                    $c->{isa}->cascade_save( $self, $i );
                }
            }
        }

    } catch {
        $self->change_status('detached');
        confess $_[0];
    };

    $self->change_status('expired');

lib/DBIx/ObjectMapper/Mapper/Instance.pm  view on Meta::CPAN

sub delete {
    my $self = shift;
    confess 'it need to be "persistent" status.' unless $self->is_persistent;

    my $deleted_key = shift || +{};
    my $uniq_cond = $self->identity_condition;
    my $class_mapper = $self->instance->__class_mapper__;

    my $result;
    try {
        my @after_cascade;
        $deleted_key->{$self->primary_cache_key} = 1;
        for my $prop_name ( $class_mapper->attributes->property_names ) {
            my $prop = $class_mapper->attributes->property_info($prop_name);
            if (    $prop->type eq 'relation'
                and $prop->{isa}->is_cascade_delete() )
            {
                if (ref( $prop->{isa} ) eq
                    'DBIx::ObjectMapper::Relation::BelongsTo' )
                {
                    push @after_cascade, $prop;
                }
                else {
                    $self->_cascade_delete($prop, $deleted_key);
                }
            }
            elsif( $prop->is_multi ) {
                $prop->{isa}->deleted_parent($self);
            }
        }

        $result = $class_mapper->delete(@$uniq_cond);

        for my $c ( @after_cascade ) {
            $self->_cascade_delete($c, $deleted_key);
        }
    } catch {
        $self->change_status('detached');
        confess $_[0];
    };

    $self->change_status('detached');
    return $result;
}

sub _cascade_delete {
    my ( $self, $prop, $deleted_key ) = @_;

    $prop->{isa}->cascade_delete($self, $deleted_key);
    if ( my $instance = $self->get_val($prop->name) ) {
        my @instance
            = ref $instance eq 'ARRAY'
            ? @$instance
            : ($instance);
        $self->unit_of_work->detach($_) for @instance;
    }
}

sub _clear_cache {

lib/DBIx/ObjectMapper/Mapper/Instance.pm  view on Meta::CPAN


    return unless $prop->type eq 'relation';

    if (    $prop->{isa}->type eq 'many_to_many'
        and $obj->__mapper__->is_persistent )
    {
        my $mapper_addr  = refaddr($obj);
        $self->_regist_many_to_many_event($name, $mapper_addr, 'remove');
    }
    elsif( $self->is_persistent ) {
        if( $prop->{isa}->is_cascade_delete_orphan ) {
            $self->unit_of_work->delete($obj);
        }
        else {
            my $rel_val = $prop->{isa}->relation_value($self);
            for my $r ( keys %$rel_val ) {
                $obj->__mapper__->set_val_trigger( $r => undef );
                $obj->__mapper__->set_val( $r => undef );
            }
        }
        $self->unit_of_work->flush if $self->unit_of_work->autoflush;

lib/DBIx/ObjectMapper/Metadata/Table.pm  view on Meta::CPAN

}

sub delete {
    my $self = shift;
    my $query = $self->query_object->delete( $self->_delete_query_callback )
        ->table( $self->table_name );
    $query->where(@_) if @_;
    return $query;
}

sub _delete_query_callback { undef } # TODO cascade delete

sub update {
    my $self = shift;
    my ( $data, $cond ) = @_;
    my $query = $self->query_object->update( $self->_update_query_callback )
        ->table( $self->table_name );
    $query->set(%$data) if $data;
    $query->where( @$cond ) if $cond;
    return $query;
}

lib/DBIx/ObjectMapper/Relation.pm  view on Meta::CPAN

sub new {
    my ( $class, $rel_class, $option ) = @_;

    my $is_multi = $class->initial_is_multi || 0;

    my $self = bless +{
        name      => undef,
        rel_class => $rel_class,
        option    => $option || {},
        type      => 'relation',
        cascade   => +{},
        is_multi  => $is_multi,
        table     => undef,
        via       => [],
    }, $class;

    $self->_init_option;
    return $self;
}

sub is_multi { $_[0]->{is_multi} }

sub _init_option {
    my $self = shift;

    if( my $cascade_option = $self->option->{cascade} ) {
        $cascade_option =~ s/\s//g;
        my %cascade = map { $_ => 1 } split ',', $cascade_option;
        if( $cascade{all} ) {
            $self->{cascade}{$_} = 1
                for qw(save_update reflesh_expire delete detach);
        }

        for my $c ( keys %CASCADE_TYPES ) {
            $self->{cascade}{$c} = 1 if $cascade{$c};
        }
    }

    if( my $order_by = $self->option->{order_by} ) {
        $order_by = [ $order_by ] unless ref $order_by eq 'ARRAY';
        $self->{order_by} = $order_by;
    }
}

{
    no strict 'refs';
    my $pkg = __PACKAGE__;
    for my $cascade ( keys %CASCADE_TYPES ) {
        *{"$pkg\::is_cascade_$cascade"} = sub {
            my $self = shift;
            return $self->{cascade}{$cascade} || do {
                if( $self->is_multi ) {
                    $CASCADE_TYPES{$cascade}->[1];
                }
                else {
                    $CASCADE_TYPES{$cascade}->[0];
                }
            }
        };
    }
};

sub mapper    {
    my $self = shift;
    unless( DBIx::ObjectMapper::Mapper->is_initialized($self->rel_class) ) {
        confess 'the '

lib/DBIx/ObjectMapper/Relation.pm  view on Meta::CPAN

        @order_by = map { $rel_mapper->attributes->property($_) }
            @{ $rel_mapper->table->primary_key };
    }

    return $mapper->unit_of_work->search( $self->rel_class )->filter(@$cond)
        ->order_by(@order_by)->execute->all;
}

sub relation_condition {}

sub cascade_delete {
    my $self = shift;
    return unless $self->is_cascade_delete;
    my $mapper = shift;
    my $deleted_key = shift;
    for my $child ( $self->_get($mapper) ) {
        unless( $deleted_key->{$child->__mapper__->primary_cache_key} ) {
            $child->__mapper__->delete($deleted_key);
        }
    }
}

sub relation_value {

lib/DBIx/ObjectMapper/Relation.pm  view on Meta::CPAN

    my $rel_val = $self->relation_value($mapper);
    my $rel_mapper = $self->mapper;
    my @cond;
    for my $r ( keys %$rel_val ) {
        next unless defined $rel_val->{$r};
        push @cond, $rel_mapper->table->c( $r ) == $rel_val->{$r};
    }
    return @cond;
}

sub cascade_update {
    my $self = shift;
    my $mapper = shift;

    return unless $self->is_cascade_save_update and $mapper->is_modified;

    my $uniq_cond = $mapper->relation_condition->{$self->name};
    my $modified_data = $mapper->modified_data;

    my $class_mapper = $mapper->instance->__class_mapper__;
    my $rel_mapper = $self->mapper;
    my $fk = $self->foreign_key($class_mapper->table, $rel_mapper->table);
    my %foreign_key =
        map{ $fk->{refs}->[$_] => $fk->{keys}->[$_] } 0 .. $#{$fk->{keys}};

lib/DBIx/ObjectMapper/Relation.pm  view on Meta::CPAN

        my $prop = $class_mapper->attributes->property_info( $mkey );
        if( $foreign_key{$mkey} ) {
            $sets{$foreign_key{$mkey}} = $modified_data->{$mkey};
        }
    }
    return unless keys %sets;

    $self->mapper->update( \%sets, $uniq_cond );
}

sub cascade_save {
    my $self = shift;
    my $mapper = shift;
    my $instance = shift;

    return unless $self->is_cascade_save_update;

    my %sets;
    my $rel_val = $self->relation_value($mapper);
    for my $r ( keys %$rel_val ) {
        $instance->__mapper__->set_val( $r => $rel_val->{$r} );
    }

    $mapper->unit_of_work->add($instance);

    $instance->__mapper__->save;

lib/DBIx/ObjectMapper/Relation/BelongsTo.pm  view on Meta::CPAN

        my $prop = $class_mapper->attributes->property_info( $prop_name );
        next unless $prop->type eq 'column';
        if( $foreign_key{$prop->name} ) {
            $val{$foreign_key{$prop->name}} = $mapper->get_val( $prop_name );
        }
    }

    return \%val;
}

sub cascade_update { }

sub cascade_save {
    my $self = shift;
    my $mapper = shift;
    my $instance = shift;

    return unless $self->is_cascade_save_update;
    if( $instance->__mapper__->is_transient ) {
        $mapper->unit_of_work->add($instance);
        $instance->__mapper__->save;
    }

    $self->set_val_from_object($mapper, $instance);
}

sub set_val_from_object {
    my $self = shift;

lib/DBIx/ObjectMapper/Relation/ManyToMany.pm  view on Meta::CPAN

    my $cond = $mapper->relation_condition->{$self->name};
    my $query = $mapper->unit_of_work->search( $self->rel_class )
        ->filter(@$cond)
        ->order_by( map { $attr->p($_) }
            @{ $rel_mapper->table->primary_key } );
    push @{$query->{join}}, [ $self->assc_table => \@assc_cond ];

    return $query->execute->all;
}

sub cascade_save {
    my $self = shift;
    my $mapper = shift;
    my $instance = shift;
    return unless $self->is_cascade_save_update;

    my $class_mapper = $mapper->instance->__class_mapper__;
    my $rel_mapper = $self->mapper;

    $mapper->unit_of_work->add($instance);
    if( $instance->__mapper__->is_pending ) {
        $instance->__mapper__->save;
    }

    my %values;

lib/DBIx/ObjectMapper/Relation/ManyToMany.pm  view on Meta::CPAN

        $self->assc_table->get_foreign_key_by_table( $class_mapper->table );
    for my $i ( 0 .. $#{$fk2->{keys}} ) {
        $values{ $fk2->{keys}->[$i] } = $mapper->get_val( $fk2->{refs}->[$i] );
    }

    $self->assc_table->insert->values(\%values)->execute;

    return $instance;
}

sub cascade_update {
    my $self = shift;
    my $mapper = shift;
    return unless $self->is_cascade_save_update and $mapper->is_modified;

    my $uniq_cond = $mapper->relation_condition->{$self->name};
    my $modified_data = $mapper->modified_data;
    my $class_mapper = $mapper->instance->__class_mapper__;

    my $fk =
        $self->assc_table->get_foreign_key_by_table( $class_mapper->table );
    my %foreign_key =
        map{ $fk->{refs}->[$_] => $fk->{keys}->[$_] } 0 .. $#{$fk->{keys}};

lib/DBIx/ObjectMapper/Relation/ManyToMany.pm  view on Meta::CPAN

        my $prop = $class_mapper->attributes->property_info( $mkey );
        if( $foreign_key{$prop->name} ) {
            $sets{$foreign_key{$prop->name}} = $modified_data->{$mkey};
        }
    }
    return unless keys %sets;

    $self->assc_table->update->set(%sets)->where(@$uniq_cond)->execute;
}

sub cascade_delete {
    my $self = shift;
    my $mapper = shift;

    return unless $self->is_cascade_delete;

    my @cond = $self->identity_condition($mapper);
    return if !@cond || ( @cond == 1 and !defined $cond[0]->[2] );

    $self->assc_table->delete->where(@cond)->execute;
}

sub many_to_many_add {
    my $self = shift;
    my ($mapper, $instance) = @_;
    $self->cascade_save(@_);
}

sub many_to_many_remove {
    my $self = shift;
    my ($mapper, $instance) = @_;
    my $rel_mapper = $self->mapper;
    my $uniq_cond = $mapper->relation_condition->{$self->name};
    my @cond = @$uniq_cond;

    my $fk1 =

t/12_session/010_relation_basic.t  view on Meta::CPAN

    constructor => +{ auto => 1 },
    accessors   => +{ auto => 1 },
    attributes  => +{
        properties => +{
            children => +{
                isa => $mapper->relation(
                    has_many => 'MyTest010::Child',
                    {
                        order_by =>
                            $mapper->metadata->t('child')->c('id')->desc,
                        cascade => 'all,delete_orphan',
                    }
                ),
            }
        }
    }
);

ok $mapper->maps(
    $mapper->metadata->t('child') => 'MyTest010::Child',
    constructor => +{ auto => 1 },

t/12_session/012_self_relation.t  view on Meta::CPAN


ok $mapper->maps(
    $bbs => 'MyTest12::BBS2',
    constructor => { auto => 1 },
    accessors   => { auto => 1 },
    attributes  => {
        properties => {
            parent => {
                isa => $mapper->relation(
                    'belongs_to' => 'MyTest12::BBS2',
                    { cascade => 'all' },
                ),
            },
            children => {
                isa => $mapper->relation(
                    'has_many' => 'MyTest12::BBS2',
                    { cascade => 'all' },
                ),
            }
        }
    }
);

subtest 'cascade' => sub {
    $mapper->metadata->t('bbs')->delete->execute;
    my $session = $mapper->begin_session( autocommit => 0 );
    my $attr = $mapper->attribute('MyTest12::BBS2');

    # cascade_save
    my $bbs = MyTest12::BBS2->new( comment => 'cascade_save' );
    my @children = (
        map{ MyTest12::BBS2->new( comment => 'cascade_save child' . $_ ) }
            ( 1 .. 5 )
    );
    $bbs->children(\@children);
    $session->add($bbs);
    is $session->search('MyTest12::BBS2')->count, 6;

    # cascade_update
    $bbs->id(100);
    ok my $parent = $session->get( 'MyTest12::BBS2' => 100 );
    is @{$parent->children}, 5;

    # orphan
    my $orphan = shift @{$parent->children};
    is @{$parent->children}, 4;
    is $session->search('MyTest12::BBS2')->filter(
        $attr->p('parent_id') == undef
    )->count, 2;

    # add orphan
    push @{$parent->children}, $orphan;
    is @{$parent->children}, 5;
    is $session->search('MyTest12::BBS2')->filter(
        $attr->p('parent_id') == undef
    )->count, 1;

    # cascade delete
    $session->delete($parent);
    is $session->search('MyTest12::BBS2')->count, 0;

    done_testing;
};

subtest 'many_to_one_cascade_save' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );
    my $attr = $mapper->attribute('MyTest12::BBS2');

    # save
    my $child1 = MyTest12::BBS2->new( comment => 'many_to_one child' );
    my $parent = MyTest12::BBS2->new( comment => 'many_to_one parent' );
    $child1->parent($parent);
    $session->add($child1);

    is $session->search('MyTest12::BBS2')->count, 2;

t/12_session/014_cascade.t  view on Meta::CPAN

use FindBin;
use lib File::Spec->catfile($FindBin::Bin, 'lib');
use MyTest11;


MyTest11->setup_default_data;
MyTest11->mapping_with_foreign_key;

my $mapper = MyTest11->mapper;

subtest 'cascade_detach' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );
    ok my $artist = $session->get( 'MyTest11::Artist' => 1 );
    for my $cd ( @{$artist->cds} ) {
        ok $cd;
        ok $cd->linernote;
        for my $track ( @{$cd->tracks} ) {
            ok $track;
        }
    }
    $session->detach($artist);

t/12_session/014_cascade.t  view on Meta::CPAN

        ok $cd->__mapper__->is_detached;
        ok $cd->linernote->__mapper__->is_detached;
        for my $track ( @{$cd->tracks} ) {
            ok $track->__mapper__->is_detached;
        }
    }

    done_testing;
};

subtest 'cascade_delete' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );

    my $cd     = $mapper->metadata->t('cd');
    my $artist = $session->get( 'MyTest11::Artist' => 1 );
    $session->delete($artist);
    $session->flush;

    # check
    ok !$session->get( 'MyTest11::Artist' => 1 );
    is $session->search('MyTest11::Cd')->count, 0;

    is $session->search('MyTest11::Track')->count, 0;
    is $session->search('MyTest11::Linernote')->count, 0;

    done_testing;
};


subtest 'cascade_update' => sub {
    my $session = $mapper->begin_session;

    my $cd2 = $session->get( 'MyTest11::Cd' => 2 );
    my @cd2_tracks = @{$cd2->tracks};
    $cd2->id(100);
    $session->flush;
    is $cd2->__mapper__->status, 'expired';
    is $cd2->id, 100; # not reflesh

    # check

t/12_session/014_cascade.t  view on Meta::CPAN

    ok my @cd100_tracks = @{$cd100->tracks};
    ok @cd2_tracks == @cd100_tracks;
    for my $i ( 0 .. $#cd2_tracks ) {
        is $cd2_tracks[$i]->title, $cd100_tracks[$i]->title;
        is $cd2_tracks[$i]->track_no, $cd100_tracks[$i]->track_no;
    }

    done_testing;
};

subtest 'cascade_save' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );
    ok my $jimi = MyTest11::Artist->new( name => 'Jimi Hendrix' );
    is_deeply $jimi->cds, [];
    $session->add($jimi);
    ok my $first_album = MyTest11::Cd->new( title => 'Are You Experienced' );
    push @{$jimi->cds}, $first_album;

    my @bulk_tracks = (
        'Purple Haze',
        'Manic Depression',

t/12_session/016_belongs_to_cascade.t  view on Meta::CPAN


ok $mapper->maps(
    $mapper->metadata->t('child') => 'MyTest016::Child',
    constructor => +{ auto => 1 },
    accessors   => +{ auto => 1 },
    attributes  => +{
        properties => +{
            parent => +{
                isa => $mapper->relation(
                    belongs_to => 'MyTest016::Parent',
                    { cascade => 'save_update,delete' }
                )
            }
        }
    },
);

{
    my $session = $mapper->begin_session;
    my @child = map{ MyTest016::Child->new( name => 'child' . $_ ) } 1 .. 5;
    my $parent = MyTest016::Parent->new( id => 1, name => 'parent1' );

t/12_session/016_belongs_to_cascade.t  view on Meta::CPAN


ok $mapper->maps(
    $mapper->metadata->t('parent') => 'MyTest016::Parent2',
    constructor => +{ auto => 1 },
    accessors   => +{ auto => 1 },
    attributes  => +{
        properties => +{
            children => +{
                isa => $mapper->relation(
                    has_many => 'MyTest016::Child2',
                    { cascade => 'all' },
                ),
            }
        }
    }
);

ok $mapper->maps(
    $mapper->metadata->t('child') => 'MyTest016::Child2',
    constructor => +{ auto => 1 },
    accessors   => +{ auto => 1 },
    attributes  => +{
        properties => +{
            parent => +{
                isa => $mapper->relation(
                    belongs_to => 'MyTest016::Parent2',
                    { cascade => 'save_update,delete' }
                )
            }
        }
    },
);

{
    my $session = $mapper->begin_session;
    my @child = map{ MyTest016::Child2->new( name => 'child' . $_ ) } 11 .. 15;
    my $parent = MyTest016::Parent2->new( id => 3, name => 'parent3' );

t/12_session/021_validation.t  view on Meta::CPAN

    1;
};

my $parent_mapper = $mapper->maps(
    $mapper->metadata->t('parent') => 'MyTest21::Parent',
    attributes => {
        properties => {
            children => {
                isa => $mapper->relation(
                    has_many => 'MyTest21::Child',
                    { cascade => 'save_update' }
                ),
                validation => 1,
            },
            has_one => {
                isa => $mapper->relation(
                    has_one => 'MyTest21::HasOne',
                    { cascade => 'save_update' }
                ),
                validation => 1,
            },
            id => { validation => 1 },
            name => { validation => 1 },
        }
    }
);

ok $parent_mapper->attributes->property('children')->is_cascade_save_update, 'only cascade save_update';
ok $parent_mapper->attributes->property('children')->validation, 'set validation option';

$mapper->maps(
    $mapper->metadata->t('child') => 'MyTest21::Child',
    attributes => {
        properties => {
            parent => {
                isa => $mapper->relation(
                    belongs_to => 'MyTest21::Parent',
                    { cascade => 'save_update' }
                ),
                validation => 1,
            },
        }
    }
);


$mapper->maps(
    $mapper->metadata->t('has_one') => 'MyTest21::HasOne',

t/12_session/100_many_to_many.t  view on Meta::CPAN


ok $mapper->maps(
    $left => 'MyTest14::Parent',
    constructor => { auto => 1 },
    accessors   => { auto => 1 },
    attributes  => {
        properties => {
            children => {
                isa => $mapper->relation(
                    'many_to_many' => $association => 'MyTest14::Child',
                    { cascade => 'all'}
                ),
                validation => 1,
            }
        }
    }
);

ok $mapper->maps(
    $right => 'MyTest14::Child',
    constructor => { auto => 1 },
    accessors   => { auto => 1 },
    attributes  => {
        properties => {
            parents => {
                isa => $mapper->relation(
                    'many_to_many' => $association => 'MyTest14::Parent',
                    { cascade => 'all' }
                )
            }
        }
    }
);

subtest 'cascade_save' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );
    ok my $parent = MyTest14::Parent->new( id => 1 );

    dies_ok{ $parent->children('hoge') } 'validation fail';
    dies_ok{ $parent->children([qw(a b c)]) } 'validation fail';

    my @children = map { MyTest14::Child->new( id => $_  ) } ( 1 .. 5 );
    ok $parent->children(\@children);

    $session->add($parent);

t/12_session/100_many_to_many.t  view on Meta::CPAN

    $session->flush;
    $session->commit;

    # check
    my $check_parent = $session->get( 'MyTest14::Parent' => 1 );
    is @{$check_parent->children}, 5;

    done_testing;
};

subtest 'cascade_update' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );
    ok my $parent = $session->get( 'MyTest14::Parent' => 1 );
    $parent->id(10);
    $session->commit;

    # check
    ok my $check_parent = $session->get( 'MyTest14::Parent' => 10 );
    is @{$check_parent->children}, 5;

    done_testing;

t/12_session/100_many_to_many.t  view on Meta::CPAN

    shift @{$p->children};
    $session->commit;

    # check
    ok my $check = $session->get( 'MyTest14::Parent' => 10 );
    is scalar(@{$check->children}), 5;

    done_testing;
};

subtest 'cascade_delete' => sub {
    my $session = $mapper->begin_session( autocommit => 0 );
    ok my $p = $session->get( 'MyTest14::Parent' => 10 );
    my @child_ids = map { $_->id } @{$p->children};
    $session->delete($p);
    $session->commit;

    # check
    ok !$session->get( 'MyTest14::Parent' => 10 );
    is $association->select->where( $association->c('left_id') == 10 )->count, 0;
    for my $cid ( @child_ids ) {

t/12_session/lib/MyTest11.pm  view on Meta::CPAN

sub mapping_with_foreign_key {
    my $artist_mapper = $mapper->maps(
        $artist => 'MyTest11::Artist',
        constructor => { auto => 1 },
        accessors   => { auto => 1 },
        attributes  => {
            properties => {
                cds => +{
                    isa => $mapper->relation(
                        has_many => 'MyTest11::Cd',
                        { cascade  => 'all' },
                    ),
                }
            }
        }
    );

    my $cd_mapper = $mapper->maps(
        $cd => 'MyTest11::Cd',
        constructor => { auto => 1 },
        accessors   => { auto => 1 },
        attributes  => {
            properties => {
                artist => +{
                    isa => $mapper->relation( belongs_to => 'MyTest11::Artist' )
                },
                tracks => +{
                    isa => $mapper->relation(
                        has_many => 'MyTest11::Track',
                        { cascade  => 'all' },
                    ),
                },
                linernote => +{
                    isa =>
                        $mapper->relation(
                            has_one => 'MyTest11::Linernote',
                            { cascade  => 'all' },
                        ),
                }
            }
        }
    );

    my $track_mapper = $mapper->maps(
        $track => 'MyTest11::Track',
        constructor => { auto => 1 },
        accessors   => { auto => 1 },

t/21_example/shopping_cart.t  view on Meta::CPAN

);

$mapper->maps(
    $shopping_cart => 'MapperExample::ShoppingCart',
    attributes => {
        properties => {
            items => {
                isa => $mapper->relation(
                    many_to_many => $shopping_cart_item
                        => 'MapperExample::Product',
                   { cascade => 'save_update,delete' },
                ),
            }
        }
    }
);

# メタデータからproductへインサート
$product->insert( prodkey => 'ABC-1', title => 'title1', price => 100 )->execute;
$product->insert( prodkey => 'ABC-2', title => 'title2', price => 200 )->execute;
$product->insert( prodkey => 'ABC-3', title => 'title3', price => 300 )->execute;



( run in 0.889 second using v1.01-cache-2.11-cpan-49f99fa48dc )