DBIx-QuickORM

 view release on metacpan or  search on metacpan

lib/DBIx/QuickORM/Type/JSON.pm  view on Meta::CPAN


DBIx::QuickORM::Type::JSON - JSON inflate/deflate type.

=head1 DESCRIPTION

A L<DBIx::QuickORM::Role::Type> implementation that stores Perl data
structures as JSON. Inflation decodes the stored JSON to a Perl reference;
deflation encodes a reference back to JSON. Comparison is done on a
canonical encoding so structurally identical values compare equal.

The affinity is C<string>. C<qorm_sql_type> prefers a native C<jsonb>/
C<json> column type and falls back to C<longtext>/C<text>.

When registered for autofill (C<qorm_register_type>) this type claims the
C<json>/C<jsonb> SQL types and any string column whose name contains
"json".

=cut

# {{{ Shared JSON encoders

my $JSON  = Cpanel::JSON::XS->new->utf8(1)->convert_blessed(1)->allow_nonref(1);
my $CJSON = Cpanel::JSON::XS->new->utf8(1)->convert_blessed(1)->allow_nonref(1)->canonical(1);

sub JSON  { shift; $JSON }
sub CJSON { shift; $CJSON }

# }}} Shared JSON encoders

sub qorm_affinity { 'string' }

sub qorm_inflate {
    my $params = parse_conflate_args(@_);
    my $val    = $params->{value};
    return undef unless defined $val;
    my $class  = $params->{class} // __PACKAGE__;

    return $val if ref($val);
    return $class->JSON->decode($val);
}

sub qorm_deflate {
    my $params = parse_conflate_args(@_);
    my $val    = $params->{value};
    return undef unless defined $val;
    my $class = $params->{class} // __PACKAGE__;

    if (blessed($val) && !$val->can('TO_JSON')) {
        my $r = reftype($val) // '';
        if    ($r eq 'HASH')  { $val = {%$val} }
        elsif ($r eq 'ARRAY') { $val = [@$val] }
    }

    return $class->JSON->encode($val);
}

sub qorm_compare {
    my $class = shift;
    my ($a, $b) = @_;

    # First decode the json if it is not already decoded
    $a = $class->qorm_inflate($a);
    $b = $class->qorm_inflate($b);

    # Now encode it in canonical form so that identical structures produce identical strings.
    # Another option would be to use Test2::Compare...
    $a = $class->CJSON->encode($a);
    $b = $class->CJSON->encode($b);

    # Equality contract: true when the two values are the same.
    return $a eq $b;
}

sub qorm_sql_type {
    my $self = shift;
    my ($dialect) = @_;

    if (my $stype = $dialect->supports_type('jsonb') // $dialect->supports_type('json')) {
        return $stype;
    }

    my $fallback = $dialect->supports_type('longtext') // $dialect->supports_type('text');
    return $fallback if $fallback;
    die "Could not find usable type for json, no json type, no longtext, and no text";
}

sub qorm_register_type {
    my $self = shift;
    my ($types, $affinities) = @_;

    my $class = ref($self) || $self;

    $types->{json}  //= $class;
    $types->{jsonb} //= $class;

    push @{$affinities->{string}} => sub {
        my %params = @_;
        return $class if $params{name}    =~ m/json/i;
        return $class if $params{db_name} =~ m/json/i;
        return;
    };
}

1;

__END__

=head1 SOURCE

The source code repository for DBIx::QuickORM can be found at
L<https://github.com/exodist/DBIx-QuickORM>.

=head1 MAINTAINERS

=over 4

=item Chad Granum E<lt>exodist7@gmail.comE<gt>

=back

=head1 AUTHORS



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