DBIx-Class-Schema-GraphQL

 view release on metacpan or  search on metacpan

lib/DBIx/Class/Schema/GraphQL.pm  view on Meta::CPAN

        push @and_parts, { -or  => [ map { _filter_to_dbic($_, $source) } @$or  ] };
    }

    # Column conditions
    for my $key (keys %$filter) {
        next if $key eq 'AND' || $key eq 'OR';
        next unless defined $filter->{$key};

        if ($key =~ /^(.+)_(not|like|gt|gte|lt|lte)$/) {
            my ($col, $op) = ($1, $2);
            my $dbic_op = { not => '!='   , like => 'like',
                            gt  => '>'    , gte  => '>=',
                            lt  => '<'    , lte  => '<='  }->{$op};
            push @and_parts, { $col => { $dbic_op => $filter->{$key} } };
        }
        else {
            # exact match
            push @and_parts, { $key => $filter->{$key} };
        }
    }

    return @and_parts == 1 ? $and_parts[0]
         : @and_parts >  1 ? { -and => \@and_parts }
         :                   {};
}

# _apply_pagination($rs, $args, $source)
#
# Applies ordering, then either offset (page) or cursor (cursor) pagination.
# Returns ($paged_rs, $next_cursor, $has_next_page).
#
# Precedence: cursor pagination wins if both are supplied.
sub _apply_pagination {
    my ($rs, $args, $source) = @_;

    my @pk_cols = $source->primary_columns;

    # Ordering
    my $order_by = $args->{orderBy};
    if ($order_by && $order_by->{field}) {
        my $field = $order_by->{field};
        if ( $source->has_column($field) ) {
            my $dir = lc($order_by->{direction} // 'ASC');
            $rs = $rs->search(undef, { order_by => { "-$dir" => $field } });
        }
        else {
            die "Invalid field '$field' provided for orderBy.\n";
        }
    }
    else {
        # Default: stable order by PK
        $rs = $rs->search(undef, { order_by => [ map { { -asc => $_ } } @pk_cols ] });
    }

    # Cursor pagination
    if (my $cursor_args = $args->{cursor}) {
        my $first = $cursor_args->{first} // 10;
        my $after = $cursor_args->{after};

        if ($after) {
            my @decoded = _decode_cursor($after);
            if (@pk_cols == 1) {
                # Simple: WHERE pk > cursor_val (works for ordered-by-PK default)
                $rs = $rs->search({ $pk_cols[0] => { '>' => $decoded[0] } });
            }
            else {
                # Composite PK: fetch everything after decoded position
                # by re-applying a compound condition
                my %after_cond = map { $pk_cols[$_] => { '>' => $decoded[$_] } }
                                     0 .. $#pk_cols;
                $rs = $rs->search(\%after_cond);
            }
        }

        # Fetch one extra to detect hasNextPage
        my @rows    = $rs->search(undef, { rows => $first + 1 })->all;
        my $has_next = @rows > $first;
        @rows = @rows[0 .. $first - 1] if $has_next;

        my $next_cursor;
        if ($has_next && @rows) {
            my $last_row = $rows[-1];
            $next_cursor = _encode_cursor(map { $last_row->get_column($_) } @pk_cols);
        }

        return (
            [ map { { $_->get_columns } } @rows ],
            $next_cursor,
            $has_next ? 1 : 0,
        );
    }

    # Offset pagination
    if (my $page_args = $args->{page}) {
        my $skip = $page_args->{skip} // 0;
        my $take = $page_args->{take} // 10;
        my @rows = $rs->search(undef, { rows => $take, offset => $skip })->all;
        return (
            [ map { { $_->get_columns } } @rows ],
            undef,   # no cursor for offset pagination
            0,
        );
    }

    # No pagination: return everything
    my @rows = $rs->all;
    return (
        [ map { { $_->get_columns } } @rows ],
        undef,
        0,
    );
}

sub _build_mutation_fields {
    my ($source, $moniker, $gql_type) = @_;
    my %fields;

    my @pk_cols   = $source->primary_columns;
    my %is_pk     = map { $_ => 1 } @pk_cols;
    my @all_cols  = $source->columns;
    my @data_cols = grep { !$is_pk{$_} } @all_cols;

    # createX
    my %create_args;
    for my $col (@all_cols) {
        my $scalar = _scalar_for_column($source, $col);
        $create_args{$col} = {
            type => _col_is_required($source, $col)
                        ? GraphQL::Type::NonNull->new(of => $scalar)



( run in 1.021 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )