diff --git a/docs/garbage-collector.md b/docs/garbage-collector.md new file mode 100644 index 000000000..14c92a966 --- /dev/null +++ b/docs/garbage-collector.md @@ -0,0 +1,4 @@ + + +* post_meta of deleted fields +* post_meta of deleted instaces of a multiple compound Field. post_meta of a field that is child of a compound field, but which the ID does not appear in any array of a compound field meta_value \ No newline at end of file diff --git a/src/classes/entities/class-tainacan-item-metadata-entity.php b/src/classes/entities/class-tainacan-item-metadata-entity.php index e88244859..7d4294edc 100644 --- a/src/classes/entities/class-tainacan-item-metadata-entity.php +++ b/src/classes/entities/class-tainacan-item-metadata-entity.php @@ -16,10 +16,26 @@ class Item_Metadata_Entity extends Entity { */ protected $repository = 'Item_Metadata'; - function __construct(Item $item, Field $field) { + /** + * + * @param Item $item Item Entity + * @param Field $field Field Entity + * @param int $meta_id ID for a specific meta row + */ + function __construct(Item $item, Field $field, $meta_id = null, $parent_meta_id = null) { $this->set_item($item); $this->set_field($field); + + if (!is_null($meta_id) && is_int($meta_id)) { + $this->set_meta_id($meta_id); + } + + if (!is_null($parent_meta_id) && is_int($parent_meta_id)) { + $this->set_parent_meta_id($parent_meta_id); + } + + } public function __toString(){ @@ -63,6 +79,39 @@ class Item_Metadata_Entity extends Entity { function set_field(Field $field) { $this->field = $field; } + + /** + * Set the specific meta ID for this metadata. + * + * When this value is set, get_value() will use it to fetch the value from + * the post_meta table, instead of considering the item and field IDs + * + * @param int $meta_id the ID of a specifica post_meta row + */ + function set_meta_id($meta_id) { + if (is_int($meta_id)) { + $this->meta_id = $meta_id; + return true; + // TODO: Should we check here to see if this meta_id is really from this field and item? + } + return false; + } + + /** + * Set parent_meta_id. Used when a item_metadata is inside a compound Field + * + * When you have a multiple compound field, this indicates of which instace of the value this item_metadata is attached to + * + * @param [type] $parent_meta_id [description] + */ + function set_parent_meta_id($parent_meta_id) { + if (is_int($parent_meta_id)) { + $this->parent_meta_id = $parent_meta_id; + return true; + // TODO: Should we check here to see if this meta_id is really from this field and item? + } + return false; + } /** * Return the item @@ -81,6 +130,24 @@ class Item_Metadata_Entity extends Entity { function get_field() { return $this->field; } + + /** + * Return the meta_id + * + * @return Field + */ + function get_meta_id() { + return isset($this->meta_id) ? $this->meta_id : null; + } + + /** + * Return the meta_id + * + * @return Field + */ + function get_parent_meta_id() { + return isset($this->parent_meta_id) ? $this->parent_meta_id : 0; + } /** * Return the field value @@ -157,10 +224,6 @@ class Item_Metadata_Entity extends Entity { foreach($value as $val) { if (!empty($val)) $one_filled = true; - - // TODO: call fieldtype validation - // if (invalid) $valid = false; - } if ($this->is_required() && !$one_filled) { diff --git a/src/classes/entities/class-tainacan-item.php b/src/classes/entities/class-tainacan-item.php index d47c60853..32d8b45c6 100644 --- a/src/classes/entities/class-tainacan-item.php +++ b/src/classes/entities/class-tainacan-item.php @@ -309,6 +309,11 @@ class Item extends Entity { if ( $pos !== false ) { continue; } + + // skip validation for Compound Fields + if ( $itemMetadata->get_field()->get_field_type() == 'Tainacan\Field_Types\Compound' ) { + continue; + } if ( ! $itemMetadata->validate() ) { $errors = $itemMetadata->get_errors(); diff --git a/src/classes/field-types/compound/class-tainacan-compound.php b/src/classes/field-types/compound/class-tainacan-compound.php new file mode 100644 index 000000000..4f3735bf0 --- /dev/null +++ b/src/classes/field-types/compound/class-tainacan-compound.php @@ -0,0 +1,41 @@ +set_primitive_type('compound'); + $this->set_component('tainacan-compound'); + } + + /** + * @param $itemMetadata \Tainacan\Entities\Item_Metadata_Entity The instace of the entity itemMetadata + * @return string + */ + + public function render( $itemMetadata ){ + return 'get_value() ).'\' + name="'.$itemMetadata->get_field()->get_name().'">'; + } + + /** + * generate the fields for this field type + */ + public function form(){ + + } + + +} \ No newline at end of file diff --git a/src/classes/repositories/class-tainacan-fields.php b/src/classes/repositories/class-tainacan-fields.php index 38b227339..978b91e82 100644 --- a/src/classes/repositories/class-tainacan-fields.php +++ b/src/classes/repositories/class-tainacan-fields.php @@ -68,7 +68,7 @@ class Fields extends Repository { //'validation' => v::numeric(), ], 'parent' => [ - 'map' => 'parent', + 'map' => 'post_parent', 'title' => __('Parent', 'tainacan'), 'type' => 'integer', 'description'=> __('Parent field', 'tainacan'), @@ -252,7 +252,10 @@ class Fields extends Repository { * @param $class_name string | object The class name or the instance */ public function register_field_type( $class_name ){ - if( is_object( $class_name ) ){ + + // TODO: we shoud not allow registration of field types of retricted core field types (e.g. compound, term) by plugins + + if( is_object( $class_name ) ){ $class_name = get_class( $class_name ); } diff --git a/src/classes/repositories/class-tainacan-item-metadata.php b/src/classes/repositories/class-tainacan-item-metadata.php index 28dc1bdf7..0b48db004 100644 --- a/src/classes/repositories/class-tainacan-item-metadata.php +++ b/src/classes/repositories/class-tainacan-item-metadata.php @@ -35,9 +35,30 @@ class Item_Metadata extends Repository { $this->save_core_field_value($item_metadata); } elseif ($field_type->get_primitive_type() == 'term') { $this->save_terms_field_value($item_metadata); + } elseif ($field_type->get_primitive_type() == 'compound') { + // do nothing. Compound values are updated when its child fields are updated + return $item_metadata; } else { if ($unique) { - update_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), wp_slash( $item_metadata->get_value() ) ); + + if (is_int($item_metadata->get_meta_id())) { + update_metadata_by_mid( 'post', $item_metadata->get_meta_id(), wp_slash( $item_metadata->get_value() ) ); + } else { + // update_post_meta returns meta ID on addition and true on update + $added_meta_id = update_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), wp_slash( $item_metadata->get_value() ) ); + } + + // handle fields inside Compound fields when adding a new meta value + + //var_dump($item_metadata->get_field()->get_parent()); + //var_dump($item_metadata->get_parent_meta_id()); + //var_dump($item_metadata->get_value()); + //die; + + if ( ( $item_metadata->get_field()->get_parent() > 0 || $item_metadata->get_parent_meta_id() > 0 ) && is_int($added_meta_id)) { + $added_compound = $this->add_compound_value($item_metadata, $added_meta_id); + } + } else { delete_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id()); @@ -56,7 +77,18 @@ class Item_Metadata extends Repository { do_action('tainacan-insert', $item_metadata); do_action('tainacan-insert-Item_Metadata_Entity', $item_metadata); - return new Entities\Item_Metadata_Entity($item_metadata->get_item(), $item_metadata->get_field()); + $new_entity = new Entities\Item_Metadata_Entity($item_metadata->get_item(), $item_metadata->get_field()); + + if (isset($added_compound) && is_int($added_compound)) { + $new_entity->set_parent_meta_id($added_compound); + } + + if (isset($added_meta_id) && is_int($added_meta_id)) { + $new_entity->set_meta_id($added_meta_id); + } + + return $new_entity; + } /** @@ -94,6 +126,34 @@ class Item_Metadata extends Repository { } } + + /** + * + * @return null|ind the meta id of the created compound metadata + */ + public function add_compound_value(Entities\Item_Metadata_Entity $item_metadata, $meta_id) { + + $current_value = get_metadata_by_mid( 'post', $item_metadata->get_parent_meta_id() ); + + if (is_object($current_value)) + $current_value = $current_value->meta_value; + + //var_dump($current_value); die; + if ( !is_array($current_value) ) + $current_value = []; + + if ( !in_array( $meta_id, $current_value ) ) { + $current_value[] = $meta_id; + } + + if ( $item_metadata->get_parent_meta_id() > 0 ) { + update_metadata_by_mid( 'post', $item_metadata->get_parent_meta_id(), $current_value ); + } elseif ( $item_metadata->get_field()->get_parent() > 0 ) { + return add_post_meta( $item_metadata->get_item()->get_id(), $item_metadata->get_field()->get_parent(), $current_value ); + } + + + } /** * Fetch Item Field objects related to an Item @@ -164,12 +224,73 @@ class Item_Metadata extends Repository { $terms = reset($terms); return $terms; + + } elseif ($field_type->get_primitive_type() == 'compound') { + + global $wpdb; + $rows = $wpdb->get_results( + $wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $item_metadata->get_item()->get_id(), $item_metadata->field->get_id()), + ARRAY_A ); + + $return_value = []; + + if (is_array($rows)) { + + foreach ($rows as $row) { + $value = $this->extract_compound_value(maybe_unserialize($row['meta_value']), $item_metadata->get_item(), $row['meta_id']); + if ( $unique ) { + $return_value = $value; + break; + } else { + $return_value[] = $value; + } + } + + } + + return $return_value; } else { - return get_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), $unique); + if (is_int($item_metadata->get_meta_id())) { + $value = get_metadata_by_mid( 'post', $item_metadata->get_meta_id() ); + if ( is_object($value) && isset( $value->meta_value) ) { + return $value->meta_value; + } + } else { + return get_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), $unique); + } + } } + + /** + * Transforms the array saved as meta_value with the IDs of post_meta saved as a value for compound fields + * and converts it into an array of Item Metadatada Entitites + * + * @param array $ids The array of post_meta ids + * @param Entities\Item $item The item this post_meta is related to + * @param int $compund_meta_id the meta_id of the parent compound metadata + * @return array An array of Item_Metadata_Entity objects + */ + private function extract_compound_value(array $ids, Entities\Item $item, $compund_meta_id) { + + $return_value = []; + + if (is_array($ids)) { + foreach ($ids as $id) { + $post_meta_object = get_metadata_by_mid( 'post', $id ); + if ( is_object($post_meta_object) ) { + $field = new Entities\Field($post_meta_object->meta_key); + $return_value[] = new Entities\Item_Metadata_Entity( $item, $field, $id, $compund_meta_id ); + } + + } + } + + return $return_value; + + } public function register_post_type() { } diff --git a/src/classes/tainacan-creator.php b/src/classes/tainacan-creator.php index a49f247dc..e517421ba 100644 --- a/src/classes/tainacan-creator.php +++ b/src/classes/tainacan-creator.php @@ -88,6 +88,7 @@ $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Numeric'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Selectbox'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Relationship'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Category'); +$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Compound'); $Tainacan_Filters = \Tainacan\Repositories\Filters::getInstance(); diff --git a/tests/test-compound-field-types.php b/tests/test-compound-field-types.php new file mode 100644 index 000000000..c9f07d1f8 --- /dev/null +++ b/tests/test-compound-field-types.php @@ -0,0 +1,128 @@ +tainacan_entity_factory->create_entity( + 'collection', + array( + 'name' => 'test', + ), + true + ); + + $field = $this->tainacan_entity_factory->create_entity( + 'field', + array( + 'name' => 'meta', + 'description' => 'description', + 'collection' => $collection, + 'field_type' => 'Tainacan\Field_Types\Compound', + 'status' => 'publish', + ), + true + ); + + $field_child1 = $this->tainacan_entity_factory->create_entity( + 'field', + array( + 'name' => 'meta2', + 'description' => 'description', + 'collection' => $collection, + 'field_type' => 'Tainacan\Field_Types\Text', + 'status' => 'publish', + 'parent' => $field->get_id(), + ), + true + ); + + $field_child2 = $this->tainacan_entity_factory->create_entity( + 'field', + array( + 'name' => 'meta3', + 'description' => 'description', + 'collection' => $collection, + 'field_type' => 'Tainacan\Field_Types\Text', + 'status' => 'publish', + 'parent' => $field->get_id(), + ), + true + ); + + + $i = $this->tainacan_entity_factory->create_entity( + 'item', + array( + 'title' => 'item test', + 'description' => 'adasdasdsa', + 'collection' => $collection, + 'status' => 'publish', + ), + true + ); + + $item_metadata1 = new \Tainacan\Entities\Item_Metadata_Entity($i, $field_child1); + $item_metadata1->set_value('Red'); + + $item_metadata1->validate(); + + $item_metadata1 = $Tainacan_Item_Metadata->insert($item_metadata1); + + $item_metadata = new \Tainacan\Entities\Item_Metadata_Entity($i, $field_child2, null, $item_metadata1->get_parent_meta_id()); + $item_metadata->set_value('Blue'); + + $item_metadata->validate(); + + $item_metadata = $Tainacan_Item_Metadata->insert($item_metadata); + + $compoundItem = new \Tainacan\Entities\Item_Metadata_Entity($i, $field); + + global $wpdb; + + var_dump($wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = {$i->get_id()}")); + //var_dump($wpdb->get_results("SELECT * FROM $wpdb->posts WHERE parent = {$field->get_id()}")); + + $compoundValue = $compoundItem->get_value(); + + $this->assertTrue( is_array($compoundValue), 'value of a compound should return array' ); + $this->assertEquals( 2, sizeof($compoundValue), 'value should have 2 item metadata' ); + + $this->assertTrue( $compoundValue[0] instanceof \Tainacan\Entities\Item_Metadata_Entity , 'First element of value should be an item metadata entity' ); + $this->assertTrue( $compoundValue[1] instanceof \Tainacan\Entities\Item_Metadata_Entity , 'Second element of value should be an item metadata entity' ); + + //var_dump($compoundValue); + $metas = []; + foreach ($compoundValue as $val) { + $metas[$val->get_field()->get_id()] = $val->get_value(); + } + + $this->assertEquals( 'Red', $metas[$field_child1->get_id()] , 'First element of value should have "Red" value' ); + $this->assertEquals( 'Blue', $metas[$field_child2->get_id()] , 'Second element of value should have "Blue" value' ); + + //var_dump($compoundValue[0]->get_field()->get_id()); + //var_dump($field_child2->get_id()); + //var_dump($field_child1->get_id()); + //var_dump($field->get_id()); + + } + +} \ No newline at end of file