DBIx-Class-Tree-NestedSet

 view release on metacpan or  search on metacpan

lib/DBIx/Class/Tree/NestedSet.pm  view on Meta::CPAN

package DBIx::Class::Tree::NestedSet;

use strict;
use warnings;

use Carp qw/croak/;
use base 'DBIx::Class';

our $VERSION = '0.10';
$VERSION = eval $VERSION;

__PACKAGE__->mk_classdata( _tree_columns => {} );

# specify the tree columns and define the relationships
#
sub tree_columns {
    my ($class, $args) = @_;

    if (defined $args) {

        my ($root, $left, $right, $level) = map {
            my $col = $args->{"${_}_column"};
            croak("required param $_ not specified") if !defined $col;
            $col;
        } qw/root left right level/;

        my $table        = $class->table;
        my %join_cond    = ( "foreign.$root" => "self.$root" );

        $class->belongs_to(
            'root' => $class,
            \%join_cond,{
                where    => \"me.$left = 1",                              #"
            },
        );

        $class->belongs_to(
            'parent' => $class,
            \%join_cond,{
                where    => \"child.$left > me.$left AND child.$right < me.$right AND me.$level = child.$level - 1",       #"
                from     => "$table me, $table child",
            },
        );

        $class->has_many(
            'nodes' => $class,
            \%join_cond,{
                order_by        => "me.$left",
                cascade_delete  => 0,
            },
        );

        $class->has_many(
            'descendants' => $class,
            \%join_cond, {
                where           => \"me.$left > parent.$left AND me.$right < parent.$right",     #"
                order_by        =>  "me.$left",
                from            =>  "$table me, $table parent",
                cascade_delete  => 0,
            },
        );

        $class->has_many(
            'children' => $class,
            \%join_cond, {
                where           => \"me.$left > parent.$left AND me.$right < parent.$right AND me.$level = parent.$level + 1",     #"
                order_by        =>  "me.$left",
                from            =>  "$table me, $table parent",
                cascade_delete  => 0,
            },
        );

        $class->has_many(
            'ancestors' => $class,
            \%join_cond, {
                where           => \"child.$left > me.$left AND child.$right < me.$right",       #"
                order_by        =>  "me.$right",
                from            =>  "$table me, $table child",
                cascade_delete  => 0,
            },
        );

        $class->_tree_columns($args);
    }

    return $class->_tree_columns;
}

# Insert a new node.
#
# If the 'right' column is not defined it assumes that we are inserting a root
# node.
#
sub insert {
    my ($self, @args) = @_;

    my ($root, $left, $right, $level) = $self->_get_columns;

    if (!$self->$right) {
        $self->set_columns({
            $left  => 1,
            $right => 2,
            $level => 0,
        });
    }

    my $row;
    my $get_row = $self->next::can;
    $self->result_source->schema->txn_do(sub {
        $row = $get_row->($self, @args);

        # If the root column is not defined, it uses the primary key so long as it is a
        # single column primary key
        if (!defined $row->$root) {
            my @primary_columns = $row->result_source->primary_columns;
            if (scalar @primary_columns > 1) {
                croak('Only single column primary keys are supported for default root selection in nested set tree classes');
            }

            $row->update({
                $root => \"$primary_columns[0]",            #"
            });

            $row->discard_changes;
        }
    });

    return $row;
}

# Delete the current node, and all sub-nodes.
#
sub delete {
    my ($self) = shift;

    my ($root, $left, $right, $level) = $self->_get_columns;

    my $p_lft = $self->$left;
    my $p_rgt = $self->$right;



( run in 2.457 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )