| 382 | | my $cprops = $class->properties; |
| 383 | | my $fields = $cprops->{meta_columns} ||= {}; |
| 384 | | my $meta_col = $cprops->{meta_column}; |
| 385 | | foreach my $name (@{ $props->{columns} }) { |
| 386 | | $fields->{$name} = (); |
| 387 | | # Skip adding this method if the class overloads it. |
| 388 | | # this lets the SUPER::columnname magic do it's thing |
| 389 | | unless ($class->can($name)) { |
| 390 | | no strict 'refs'; ## no critic |
| 391 | | *{"${class}::$name"} = sub { shift->$meta_col($name, @_) }; |
| 392 | | } |
| 393 | | } |
| | 406 | |
| | 407 | require MT::Meta; |
| | 408 | my $pkg = ref $class || $class; |
| | 409 | if (!$pkg->SUPER::properties->{meta_installed}) { |
| | 410 | $pkg->add_trigger( post_save => \&post_save_save_metadata ); |
| | 411 | $pkg->add_trigger( post_load => \&post_load_initialize_metadata ); |
| | 412 | } |
| | 413 | |
| | 414 | my $props = $class->properties; |
| | 415 | |
| | 416 | if (!$params->{columns} && !$params->{fields} && !$params->{column_defs}) { |
| | 417 | return $class->error('No meta fields specified to install_meta'); |
| | 418 | } |
| | 419 | |
| | 420 | $params->{fields} ||= []; |
| | 421 | if (my $cols = delete $params->{columns}) { |
| | 422 | foreach my $col (@$cols) { |
| | 423 | push @{ $params->{fields} }, { |
| | 424 | name => $col, |
| | 425 | type => 'vblob', |
| | 426 | }; |
| | 427 | # $props->{fields}{$col} = 'vblob'; |
| | 428 | } |
| | 429 | } |
| | 430 | if (my $cols = delete $params->{column_defs}) { |
| | 431 | foreach my $col ( keys %$cols ) { |
| | 432 | my $type = $cols->{$col}; |
| | 433 | $type =~ s/\s.*//; # take first keyword, ignoring anything after |
| | 434 | $type .= '_indexed' |
| | 435 | if $cols->{$col} =~ m/\bindexed\b/; |
| | 436 | $type = MT::Meta->normalize_type($type); |
| | 437 | |
| | 438 | push @{ $params->{fields} }, { |
| | 439 | name => $col, |
| | 440 | type => $type, |
| | 441 | }; |
| | 442 | # $props->{fields}{$col} = $type; |
| | 443 | } |
| | 444 | } |
| | 445 | |
| | 446 | $params->{datasource} ||= $class->datasource . '_meta'; |
| | 447 | |
| | 448 | if ($props->{meta_installed} && !@{ $params->{fields} }) { |
| | 449 | return 1; |
| | 450 | } |
| | 451 | |
| | 452 | if (my $fields = MT::Meta->install($pkg, $params)) { |
| | 453 | # we may have inherited meta fields so lets update with |
| | 454 | # the fields returned by MT::Meta |
| | 455 | $props->{fields} = $fields; |
| | 456 | } |
| | 457 | |
| | 458 | return $props->{meta_installed} = 1; |
| | 459 | } |
| | 460 | |
| | 461 | sub meta_args { |
| | 462 | my $class = shift; |
| | 463 | my $id_field = $class->datasource . '_id'; |
| | 464 | return { |
| | 465 | key => $class->datasource, |
| | 466 | column_defs => { |
| | 467 | $id_field => 'integer not null', |
| | 468 | type => 'string(255) not null', |
| | 469 | vchar => 'string(255)', |
| | 470 | vchar_indexed => 'string(255)', |
| | 471 | vdatetime => 'datetime', |
| | 472 | vdatetime_indexed => 'datetime', |
| | 473 | vinteger => 'integer', |
| | 474 | vinteger_indexed => 'integer', |
| | 475 | vfloat => 'float', |
| | 476 | vfloat_indexed => 'float', |
| | 477 | vblob => 'blob', |
| | 478 | vclob => 'text', |
| | 479 | }, |
| | 480 | columns => [ $id_field, qw( |
| | 481 | type |
| | 482 | vchar |
| | 483 | vchar_indexed |
| | 484 | vdatetime |
| | 485 | vdatetime_indexed |
| | 486 | vinteger |
| | 487 | vinteger_indexed |
| | 488 | vfloat |
| | 489 | vfloat_indexed |
| | 490 | vblob |
| | 491 | vclob |
| | 492 | ) ], |
| | 493 | indexes => { |
| | 494 | $id_field => 1, |
| | 495 | id_type => { columns => [ $id_field, 'type' ] }, |
| | 496 | id_type_vchar => { columns => [ $id_field, 'type', 'vchar_indexed' ] }, |
| | 497 | id_type_vdatetime => { columns => [ $id_field, 'type', |
| | 498 | 'vdatetime_indexed' ] }, |
| | 499 | id_type_vinteger => { columns => [ $id_field, 'type', |
| | 500 | 'vinteger_indexed' ] }, |
| | 501 | id_type_vfloat => { columns => [ $id_field, 'type', |
| | 502 | 'vfloat_indexed' ] }, |
| | 503 | }, |
| | 504 | primary_key => [ $class->datasource . '_id', 'type' ], |
| | 505 | }; |
| 397 | | my $props = $_[0]->properties; |
| 398 | | return $props->{meta} && (@_ > 1 ? exists $props->{meta_columns}{$_[1]} : 1); |
| 399 | | } |
| 400 | | |
| 401 | | sub pre_save_serialize_metadata { |
| 402 | | my ($obj) = shift; |
| 403 | | my $meta_col = $obj->properties->{meta_column}; |
| 404 | | if ($obj->{changed_cols}{$meta_col}) { |
| 405 | | require MT::Serialize; |
| 406 | | my $meta = $obj->$meta_col; |
| 407 | | $obj->$meta_col(MT::Serialize->serialize(\$meta)); |
| 408 | | } |
| 409 | | } |
| 410 | | |
| 411 | | sub __thaw_meta { |
| 412 | | my ($meta) = @_; |
| 413 | | $$meta = '' unless defined $$meta; |
| 414 | | require MT::Serialize; |
| 415 | | my $out = MT::Serialize->unserialize($$meta); |
| 416 | | if (ref $out eq 'REF') { |
| 417 | | return $$out; |
| 418 | | } else { |
| 419 | | return {}; |
| 420 | | } |
| 421 | | } |
| 422 | | |
| 423 | | # $obj->meta returns a hashref of metadata information |
| 424 | | # $obj->meta($scalar) allows assignment of a serialized value |
| 425 | | # $obj->meta('name', 'value') assigns an individual metadata element |
| 426 | | # $obj->meta('name') returns an individual metadata value |
| 427 | | # $obj->save will automatically serialize the metadata back to the database |
| 428 | | sub __meta_column { |
| 429 | | my $obj = shift; |
| 430 | | my $meta_col = $obj->properties->{meta_column} or return; |
| 431 | | |
| 432 | | if (@_) { |
| 433 | | my $var = shift; |
| 434 | | if ((defined $var) && ($var =~ m/^SERG\0\0\0\0/)) { |
| 435 | | return $obj->column($meta_col, $var); |
| 436 | | } |
| 437 | | my $meta = $obj->column($meta_col); |
| 438 | | if (!defined($meta)) { |
| 439 | | $obj->{column_values}{$meta_col} = $meta = {}; |
| 440 | | } elsif (!ref $meta) { |
| 441 | | $obj->{column_values}{$meta_col} = $meta = __thaw_meta(\$meta); |
| 442 | | } |
| | 509 | my $obj = shift; |
| | 510 | return $obj->is_meta_column(@_) if @_; |
| | 511 | return $obj->properties->{meta_installed} ? 1 : 0; |
| | 512 | } |
| | 513 | |
| | 514 | sub post_load_initialize_metadata { |
| | 515 | my $obj = shift; |
| | 516 | if (defined $obj && $obj->properties->{meta_installed}) { |
| | 517 | $obj->init_meta(); |
| | 518 | $obj->{__meta}->set_primary_keys($obj); |
| | 519 | } |
| | 520 | } |
| | 521 | |
| | 522 | sub is_meta_column { |
| | 523 | my $obj = shift; |
| | 524 | my ($field) = @_; |
| | 525 | |
| | 526 | my $props = $obj->properties; |
| | 527 | return unless $props->{meta_installed}; |
| | 528 | |
| | 529 | my $meta = $obj->meta_pkg; |
| | 530 | return 1 if $props->{fields}{$field}; |
| | 531 | |
| | 532 | return; |
| | 533 | } |
| | 534 | |
| | 535 | sub meta_pkg { |
| | 536 | my $class = shift; |
| | 537 | my $props = $class->properties; |
| | 538 | return unless $props->{meta}; # this only works for meta-enabled classes |
| | 539 | |
| | 540 | return $props->{meta_pkg} if $props->{meta_pkg}; |
| | 541 | |
| | 542 | my $meta = ref $class || $class; |
| | 543 | $meta .= '::Meta'; |
| | 544 | return $props->{meta_pkg} = $meta; |
| | 545 | } |
| | 546 | |
| | 547 | sub has_column { |
| | 548 | my $obj = shift; |
| | 549 | return 1 if $obj->SUPER::has_column(@_); |
| | 550 | return 1 if $obj->is_meta_column(@_); |
| | 551 | return; |
| | 552 | } |
| | 553 | |
| | 554 | sub post_save_save_metadata { |
| | 555 | my $obj = shift; |
| | 556 | if (defined $obj && exists $obj->{__meta}) { |
| | 557 | $obj->{__meta}->set_primary_keys($obj); |
| | 558 | $obj->{__meta}->save; |
| | 559 | } |
| | 560 | } |
| | 561 | |
| | 562 | sub meta { |
| | 563 | my $obj = shift; |
| | 564 | my ($name, $value) = @_; |
| | 565 | |
| | 566 | return !$obj->{__meta} ? undef |
| | 567 | : 2 == scalar @_ ? $obj->{__meta}->set($name, $value) |
| | 568 | : 1 == scalar @_ ? $obj->{__meta}->get($name) |
| | 569 | : $obj->{__meta}->get_hash |
| | 570 | ; |
| | 571 | } |
| | 572 | |
| | 573 | sub meta_obj { |
| | 574 | my $obj = shift; |
| | 575 | return $obj->{__meta}; |
| | 576 | } |
| | 577 | |
| | 578 | sub column_func { |
| | 579 | my $obj = shift; |
| | 580 | my ($col) = @_; |
| | 581 | return if !$col; |
| | 582 | |
| | 583 | return $obj->SUPER::column_func(@_) |
| | 584 | if !$obj->is_meta_column($col); |
| | 585 | |
| | 586 | return sub { |
| | 587 | my $obj = shift; |
| | 1162 | |
| | 1163 | package MT::Object::Meta; |
| | 1164 | |
| | 1165 | use base qw( Data::ObjectDriver::BaseObject ); |
| | 1166 | |
| | 1167 | sub driver { $MT::Object::DRIVER ||= MT::ObjectDriverFactory->new } |
| | 1168 | |
| | 1169 | sub install_properties { |
| | 1170 | my $class = shift; |
| | 1171 | my ($props) = @_; |
| | 1172 | $props->{column_defs}->{$_} ||= 'string' |
| | 1173 | for @{ $props->{columns} }; |
| | 1174 | $class->SUPER::install_properties(@_); |
| | 1175 | } |
| | 1176 | |
| | 1177 | sub meta_pkg { undef } |
| | 1178 | |
| | 1179 | *table_name = \&MT::Object::table_name; |
| | 1180 | *column_defs = \&MT::Object::column_defs; |
| | 1181 | *column_def = \&MT::Object::column_def; |
| | 1182 | *index_defs = \&MT::Object::index_defs; |
| | 1183 | *__parse_defs = \&MT::Object::__parse_defs; |
| | 1184 | *__parse_def = \&MT::Object::__parse_def; |
| | 1185 | *count = \&MT::Object::count; |
| | 1186 | *columns_of_type = \&MT::Object::columns_of_type; |
| | 1187 | |
| | 1188 | # TODO: copy this too |
| | 1189 | sub blob_requires_zip {} |