package MySQL::Workbench::Parser::Table;
# ABSTRACT: A table of the ER model
use strict;
use warnings;
use List::MoreUtils qw(all);
use Moo;
use Scalar::Util qw(blessed);
use YAML::Tiny;
use MySQL::Workbench::Parser::Column;
use MySQL::Workbench::Parser::Index;
our $VERSION = '1.10';
has node => (
is => 'ro',
required => 1,
isa => sub {
blessed $_[0] && $_[0]->isa( 'XML::LibXML::Node' );
},
);
has parser => (
is => 'ro',
required => 1,
isa => sub {
blessed $_[0] && $_[0]->isa( 'MySQL::Workbench::Parser' );
},
);
has columns => (
is => 'rwp',
isa => sub {
ref $_[0] && ref $_[0] eq 'ARRAY' &&
all{ blessed $_ && $_->isa( 'MySQL::Workbench::Parser::Column' ) }@{$_[0]}
},
lazy => 1,
default => sub { [] },
);
has indexes => (
is => 'rwp',
isa => sub {
ref $_[0] && ref $_[0] eq 'ARRAY' &&
all{ blessed $_ && $_->isa( 'MySQL::Workbench::Parser::Index' ) }@{$_[0]}
},
lazy => 1,
default => sub { [] },
);
has foreign_keys => (
is => 'rwp',
isa => sub {
ref $_[0] && ref $_[0] eq 'HASH'
},
default => sub { {} },
);
has primary_key => (
is => 'rwp',
isa => sub {
ref $_[0] && ref $_[0] eq 'ARRAY'
},
default => sub { [] },
);
has comment => (is => 'rwp');
has name => ( is => 'rwp' );
has column_mapping => (
is => 'rwp',
lazy => 1,
isa => sub {
ref $_[0] && ref $_[0] eq 'HASH'
},
default => sub {
my $self = shift;
my %map = map{
$_->id => $_->name
}@{ $self->columns || [] };
\%map;
},
);
sub BUILD {
my $self = shift;
$self->_parse;
}
sub as_hash {
my $self = shift;
my @columns;
for my $column ( @{$self->columns} ) {
push @columns, $column->as_hash;
}
my @indexes;
for my $index ( @{ $self->indexes } ) {
push @indexes, $index->as_hash;
}
my %info = (
name => $self->name,
columns => \@columns,
indexes => \@indexes,
foreign_keys => $self->foreign_keys,
primary_key => $self->primary_key,
);
$info{comment} = $self->comment if $self->comment;
return \%info;
}
sub _parse {
my $self = shift;
my $node = $self->node;
my @columns;
my @column_nodes = $node->findnodes( './/value[@struct-name="db.mysql.Column"]' );
for my $column_node ( @column_nodes ) {
my $column_obj = MySQL::Workbench::Parser::Column->new(
node => $column_node,
table => $self,
);
push @columns, $column_obj;
}
$self->_set_columns( \@columns );
my $comment = $node->findvalue( './value[@key="comment"]' );
$self->_set_comment( $comment ) if $comment;
my $name = $node->findvalue( './value[@key="name"]' );
$self->_set_name( $name );
my %foreign_keys;
my @foreign_key_nodes = $node->findnodes( './value[@key="foreignKeys"]/value[@struct-name="db.mysql.ForeignKey"]' );
for my $foreign_key_node ( @foreign_key_nodes ) {
my $foreign_table_id = $foreign_key_node->findvalue( 'link[@key="referencedTable"]' );
my $foreign_column_id = $foreign_key_node->findvalue( 'value[@key="referencedColumns"]/link' );
my $foreign_data = $self->_foreign_data(
table_id => $foreign_table_id,
column_id => $foreign_column_id,
);
my $table = $foreign_data->{table};
my $column = $foreign_data->{column};
my $me_column_id = $foreign_key_node->findvalue( './/value[@key="columns"]/link' );
my $me_column = $node->findvalue( './/value[@id="' . $me_column_id . '"]/value[@key="name"]' );
my %actions;
my $delete_action = $foreign_key_node->findvalue( './/value[@key="deleteRule"]' );
$actions{on_delete} = lc $delete_action;
my $update_action = $foreign_key_node->findvalue( './/value[@key="updateRule"]' );
$actions{on_update} = lc $update_action;
push @{ $foreign_keys{$table} }, { %actions, me => $me_column, foreign => $column };
}
my @indexes;
my @index_column_nodes = $node->findnodes( './/value[@struct-name="db.mysql.Index"]' );
for my $index_column_node ( @index_column_nodes ) {
my $type = $index_column_node->findvalue( './/value[@key="indexType"]' );
my $index_obj = MySQL::Workbench::Parser::Index->new(
node => $index_column_node,
table => $self,
);
push @indexes, $index_obj;
next if $type ne 'PRIMARY';
my @column_nodes = $index_column_node->findnodes( './/link[@key="referencedColumn"]' );
my @column_names = map{
my $id = $_->textContent;
$node->findvalue( './/value[@id="' . $id . '"]/value[@key="name"]' );
}@column_nodes;
$self->_set_primary_key( \@column_names );
}
$self->_set_foreign_keys( \%foreign_keys );
$self->_set_indexes( \@indexes );
}
sub get_datatype {
my $self = shift;
return $self->parser->get_datatype( @_ );
}
sub _foreign_data {
my $self = shift;
my %ids = @_;
my ($foreign_table_node) = $self->node->parentNode->findnodes(
'value[@struct-name="db.mysql.Table" and @id="' . $ids{table_id} . '"]'
);
my $foreign_table_name = $foreign_table_node->findvalue( 'value[@key="name"]' );
my $foreign_column_name = $foreign_table_node->findvalue(
'.//value[@id="' . $ids{column_id} . '"]/value[@key="name"]'
);
return { table => $foreign_table_name, column => $foreign_column_name };
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
MySQL::Workbench::Parser::Table - A table of the ER model
=head1 VERSION
version 1.11
=for Pod::Coverage BUILD
=head1 METHODS
=head2 as_hash
return info about a table as a hash
my %info = $table->as_hash;
returns
(
name => 'table_name',
primary_key => [ 'id' ],
foreign_keys => {
second_table => [
{
foreign => 'id',
me => 'second_id',
},
],
},
columns => [
{
name => 'id',
datatype => 'INT',
length => '',
precision => '0',
not_null => '1',
autoincrement => '1',
default_value => '',
}
],
)
=head2 get_datatype
get datatype for a workbench column datatype
my $datatype = $table->get_datatype( 'com.mysql.rdbms.mysql.datatype.mediumtext' );
returns the MySQL name of the datatype
MEDIUMTEXT
=head1 ATTRIBUTES
=over 4
=item * comment
=item * columns
An array reference of L<MySQL::Workbench::Parser::Column> objects
=item * foreign_keys
An array reference of all relationships to other tables
=item * name
The name of the table
=item * node
=item * parser
=item * primary_key
=item * indexes
=item * column_mapping
=back
=head1 MISC
=head2 BUILD
=head1 AUTHOR
Renee Baecker <reneeb@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2018 by Renee Baecker.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
=cut