diff --git a/uc_attribute/tests/uc_attribute.test b/uc_attribute/tests/uc_attribute.test index b3786d9b..ed2065b8 100644 --- a/uc_attribute/tests/uc_attribute.test +++ b/uc_attribute/tests/uc_attribute.test @@ -14,14 +14,6 @@ require_once backdrop_get_path('module', 'uc_store') . '/tests/test_helper.inc'; */ class UbercartAttributeTestCase extends UbercartTestHelper { - public static function getInfo() { - return array( - 'name' => 'Attribute API', - 'description' => 'Test the attribute API.', - 'group' => 'Ubercart', - ); - } - /** * Overrides BackdropWebTestCase::setUp(). */ @@ -60,7 +52,7 @@ class UbercartAttributeTestCase extends UbercartTestHelper { $this->assertTrue(uc_attribute_subject_exists($attribute->aid, 'product', $product->nid)); // Test retrieval. - $loaded_attribute = uc_attribute_load($attribute->aid, $product->nid, 'product'); + $loaded_attribute = uc_attribute_product_load($attribute->aid, $product->nid); // Check the attribute integrity. foreach (self::attributeFieldsToTest('product') as $field) { @@ -88,7 +80,7 @@ class UbercartAttributeTestCase extends UbercartTestHelper { $this->assertTrue(uc_attribute_subject_exists($attribute->aid, 'class', $product_class->pcid)); // Test retrieval. - $loaded_attribute = uc_attribute_load($attribute->aid, $product_class->pcid, 'class'); + $loaded_attribute = uc_attribute_class_load($attribute->aid, $product_class->pcid); // Check the attribute integrity. foreach (self::attributeFieldsToTest('class') as $field) { @@ -256,15 +248,17 @@ class UbercartAttributeTestCase extends UbercartTestHelper { $this->assertFalse(uc_attribute_load($attribute->aid), t('Attribute was deleted properly.')); // Sanity check! - $this->assertFalse(db_query("SELECT aid FROM {uc_attributes} WHERE aid = :aid", array(':aid' => $attribute->aid))->fetchField(), t('Attribute was seriously deleted properly!')); + $attribute_from_config = config_get('uc_attribute.attributes', "attributes.{$attribute->aid}"); + $this->assertFalse(!empty($attribute_from_config), t('Attribute was seriously deleted properly!')); // Test that options were deleted properly. foreach ($options as $option) { - $this->assertFalse(db_query("SELECT oid FROM {uc_attribute_options} WHERE oid = :oid", array(':oid' => $option->oid))->fetchField(), t('Make sure options are deleted properly.')); + $option_from_config = config_get('uc_attribute.attributes', "attributes.{$attribute->aid}.options.{$option->oid}"); + $this->assertFalse(!empty($option_from_config), t('Make sure options are deleted properly.')); } // Test the deletion applied to products too. - $loaded_product_attributes = uc_attribute_load_multiple(array(), 'product', $product->nid); + $loaded_product_attributes = uc_attribute_product_load_multiple(array(), $product->nid); // We'll get all in $loaded_attributes above, without the original. (Which // has been deleted.) @@ -342,7 +336,14 @@ class UbercartAttributeTestCase extends UbercartTestHelper { $types = _uc_attribute_display_types(); $this->assertRaw('' . $types[$edit['display']] . '', t('Verify display field.')); - $aid = db_query("SELECT aid FROM {uc_attributes} WHERE name = :name", array(':name' => $edit['name']))->fetchField(); + $config = config('uc_attribute.attributes'); + $attributes = $config->get('attributes'); + foreach ($attributes as $attribute_aid => $attribute) { + if ($attribute['name'] = $edit['name']) { + $aid = $attribute_aid; + } + } + $this->assertTrue($aid, t('Attribute was created.')); $attribute = uc_attribute_load($aid); @@ -473,7 +474,7 @@ class UbercartAttributeTestCase extends UbercartTestHelper { $this->backdropPost('admin/store/products/attributes/' . $attribute->aid . '/options/add', $edit, t('Submit')); - $option = db_query("SELECT * FROM {uc_attribute_options} WHERE aid = :aid", array(':aid' => $attribute->aid))->fetchObject(); + $option = config_get('uc_attribute.attributes', "attributes.{$attribute->aid}.options"); $fields_ok = TRUE; foreach ($edit as $field => $value) { @@ -565,7 +566,7 @@ class UbercartAttributeTestCase extends UbercartTestHelper { $this->showVar($edit); $this->backdropPost('admin/store/products/classes/' . $class->pcid . '/attributes', $edit, t('Save changes')); - $attribute = uc_attribute_load($attribute->aid, $class->pcid, 'class'); + $attribute = uc_attribute_class_load($attribute->aid, $class->pcid); $fields_ok = TRUE; foreach ($a as $field => $value) { @@ -861,12 +862,14 @@ class UbercartAttributeTestCase extends UbercartTestHelper { * @param $save */ public static function createAttributeOption($data = array(), $save = TRUE) { - $max_aid = db_select('uc_attributes', 'a') - ->fields('a', array('aid')) - ->orderBy('aid', 'DESC') - ->range(0, 1) - ->execute() - ->fetchField(); + $config = config('uc_attribute.attributes'); + $attributes = $config->get('attributes'); + $attribute_keys = is_array($attributes) ? array_keys($attributes) : array(); + $max_aid = 1; + if (!empty($attribute_keys)) { + $max_aid = max($attribute_keys); + } + $option = $data + array( 'aid' => $max_aid, 'name' => BackdropWebTestCase::randomName(8), diff --git a/uc_attribute/uc_attribute.admin.inc b/uc_attribute/uc_attribute.admin.inc index fe56be49..3577299b 100644 --- a/uc_attribute/uc_attribute.admin.inc +++ b/uc_attribute/uc_attribute.admin.inc @@ -32,16 +32,28 @@ function uc_attribute_admin() { $display_types = _uc_attribute_display_types(); - $query = db_select('uc_attributes', 'a')->extend('PagerDefault')->extend('TableSort') - ->fields('a', array('aid', 'name', 'label', 'required', 'ordering', 'display')) - ->orderByHeader($header) - ->limit(30); + $config = config('uc_attribute.attributes'); + $attributes = $config->get('attributes'); + if (!$attributes) { + $attributes = array(); + } + + // $order = tablesort_get_order($header); // array('name' => 'Name', 'sql' => 'a.name'); + // $sort = tablesort_get_sort($header); // 'asc' + // @see https://drupal.stackexchange.com/questions/14889/can-tablesort-be-used-without-a-query + //$sorted_attributes = my_array_sort($attributes, $order['sql'], $sort); + // backdrop_sort($attributes, array('attr_ordering' => SORT_NUMERIC, 'ordering' => SORT_NUMERIC)); + + $total = $attributes ? count($attributes) : 0; + $number_per_page = 30; + $page = pager_default_initialize($total, $number_per_page); + $offset = $number_per_page * $page; + $current_page_items = array_slice($attributes, $offset, $number_per_page); $rows = array(); - $result = $query->execute(); - foreach ($result as $attr) { - $attr->options = db_query('SELECT COUNT(*) FROM {uc_attribute_options} WHERE aid = :aid', array(':aid' => $attr->aid))->fetchField(); + foreach ($current_page_items as $attr) { + $attr = (object) $attr; if (empty($attr->label)) { $attr->label = $attr->name; } @@ -67,7 +79,7 @@ function uc_attribute_admin() { check_plain($attr->label), $attr->required == 1 ? t('Yes') : t('No'), $attr->ordering, - $attr->options, + isset($attr->options) ? $attr->options : '', $display_types[$attr->display], backdrop_render($operations), ); @@ -171,19 +183,28 @@ function uc_attribute_form($form, &$form_state, $attribute = NULL) { * @see uc_attribute_form() */ function uc_attribute_form_submit($form, &$form_state) { + $attribute = (object) array( + 'aid' => !empty($form_state['values']['aid']) ? $form_state['values']['aid'] : '', + 'name' => $form_state['values']['name'], + 'label' => $form_state['values']['label'], + 'description' => $form_state['values']['description'], + 'required' => $form_state['values']['required'], + 'display' => $form_state['values']['display'], + 'ordering' => $form_state['values']['ordering'], + ); + $attribute = uc_attribute_attribute_save($attribute); + if (!empty($form_state['values']['aid'])) { - backdrop_write_record('uc_attributes', $form_state['values'], 'aid'); $form_state['redirect'] = 'admin/store/products/attributes'; } else { - backdrop_write_record('uc_attributes', $form_state['values']); if ($form_state['values']['display'] == 0) { // No options needed/allowed for Textfield display type. $form_state['redirect'] = 'admin/store/products/attributes'; } else { // All other display types we redirect to add options. - $form_state['redirect'] = 'admin/store/products/attributes/' . $form_state['values']['aid'] . '/options'; + $form_state['redirect'] = 'admin/store/products/attributes/' . $attribute->aid . '/options'; } } } @@ -219,45 +240,14 @@ function uc_attribute_delete_confirm($form, &$form_state, $attribute) { * * @see uc_attribute_delete_confirm() */ -function uc_attribute_delete_confirm_submit($form, &$form_state) { +function uc_attribute_delete_confirm_submit($form, &$form_state) { // Is this only for the global attribute? if ($form_state['values']['confirm']) { - $attribute = uc_attribute_load($form_state['values']['aid']); - $options = array_keys($attribute->options); + $deleted = uc_attribute_global_attribute_cascade_delete($form_state['values']['aid']); - if ($options) { - db_delete('uc_class_attribute_options') - ->condition('oid', $options, 'IN') - ->execute(); - - db_delete('uc_product_options') - ->condition('oid', $options, 'IN') - ->execute(); + if ($deleted) { + backdrop_set_message(t('Product attribute deleted.')); } - if ($nodes = db_query("SELECT nid FROM {uc_product_attributes} WHERE aid = :aid", array(':aid' => $attribute->aid))->fetchCol()) { - db_delete('uc_product_adjustments') - ->condition('nid', $nodes, 'IN') - ->execute(); - } - - db_delete('uc_class_attributes') - ->condition('aid', $form_state['values']['aid']) - ->execute(); - - db_delete('uc_product_attributes') - ->condition('aid', $form_state['values']['aid']) - ->execute(); - - db_delete('uc_attribute_options') - ->condition('aid', $form_state['values']['aid']) - ->execute(); - - db_delete('uc_attributes') - ->condition('aid', $form_state['values']['aid']) - ->execute(); - - backdrop_set_message(t('Product attribute deleted.')); - $form_state['redirect'] = 'admin/store/products/attributes'; } } @@ -331,14 +321,12 @@ function uc_attribute_options_form($form, &$form_state, $attribute) { * @see uc_object_options_form_submit() */ function uc_attribute_options_form_submit($form, &$form_state) { - foreach ($form_state['values']['options'] as $oid => $option) { - db_update('uc_attribute_options') - ->fields(array( - 'ordering' => $option['ordering'], - )) - ->condition('oid', $oid) - ->execute(); + $config = config('uc_attribute.attributes'); + $values = $form_state['values']; + foreach ($values['options'] as $oid => $option) { + $config->set('attributes.' . $values['aid'] . '.options.' . $oid . '.' . 'ordering', $option['ordering']); } + $config->save(); backdrop_set_message(t('The changes have been saved.')); } @@ -523,14 +511,22 @@ function uc_attribute_option_form_validate($form, &$form_state) { * @see uc_attribute_option_form_validate() */ function uc_attribute_option_form_submit($form, &$form_state) { + $option = (object) array( + 'aid' => $form_state['values']['aid'], + 'oid' => isset($form_state['values']['oid']) ? $form_state['values']['oid'] : '', + 'name' => $form_state['values']['name'], + 'cost' => $form_state['values']['cost'], + 'price' => $form_state['values']['price'], + 'weight' => $form_state['values']['weight'], + 'ordering' => $form_state['values']['ordering'], + ); + uc_attribute_attribute_option_save($option); if (!isset($form_state['values']['oid'])) { - backdrop_write_record('uc_attribute_options', $form_state['values']); backdrop_set_message(t('Created new option %option.', array('%option' => $form_state['values']['name']))); watchdog('uc_attribute', 'Created new option %option.', array('%option' => $form_state['values']['name']), WATCHDOG_NOTICE, 'admin/store/products/attributes/' . $form_state['values']['aid'] . '/options/add'); $form_state['redirect'] = 'admin/store/products/attributes/' . $form_state['values']['aid'] . '/options/add'; } else { - backdrop_write_record('uc_attribute_options', $form_state['values'], array('aid', 'oid')); backdrop_set_message(t('Updated option %option.', array('%option' => $form_state['values']['name']))); watchdog('uc_attribute', 'Updated option %option.', array('%option' => $form_state['values']['name']), WATCHDOG_NOTICE, 'admin/store/products/attributes/' . $form_state['values']['aid'] . '/options/' . $form_state['values']['oid']); $form_state['redirect'] = 'admin/store/products/attributes/' . $form_state['values']['aid'] . '/options'; @@ -571,31 +567,7 @@ function uc_attribute_option_delete_confirm($form, &$form_state, $attribute, $op */ function uc_attribute_option_delete_confirm_submit($form, &$form_state) { if ($form_state['values']['confirm']) { - $match = 'i:' . $form_state['values']['aid'] . ';s:' . strlen($form_state['values']['oid']) . ':"' . $form_state['values']['oid'] . '";'; - - db_delete('uc_product_adjustments') - ->condition('combination', '%' . db_like($match) . '%', 'LIKE') - ->execute(); - - $select = db_select('uc_attribute_options', 'ao') - ->where('{uc_class_attribute_options}.oid = ao.oid') - ->condition('ao.oid', $form_state['values']['oid']); - $select->addExpression('1'); - db_delete('uc_class_attribute_options') - ->condition('', $select, 'EXISTS') - ->execute(); - - $select = db_select('uc_attribute_options', 'ao') - ->where('{uc_product_options}.oid = ao.oid') - ->condition('ao.oid', $form_state['values']['oid']); - $select->addExpression('1'); - db_delete('uc_product_options') - ->condition('', $select, 'EXISTS') - ->execute(); - - db_delete('uc_attribute_options') - ->condition('oid', $form_state['values']['oid']) - ->execute(); + uc_attribute_global_option_cascade_delete($form_state['values']['aid'], $form_state['values']['oid']); } $form_state['redirect'] = 'admin/store/products/attributes/' . $form_state['values']['aid'] . '/options'; @@ -618,7 +590,7 @@ function uc_object_attributes_form($form, &$form_state, $object, $type, $view = backdrop_goto('admin/store/products/classes/' . $id); } backdrop_set_title($class->name); - $attributes = uc_class_get_attributes($id); + $attributes = uc_class_get_attributes_config($id); break; case 'product': @@ -629,12 +601,12 @@ function uc_object_attributes_form($form, &$form_state, $object, $type, $view = backdrop_goto('node/' . $id); } backdrop_set_title($product->title); - $attributes = uc_product_get_attributes($id); + $attributes = (array) uc_product_get_attributes($id); } $used_aids = array(); - foreach ($attributes as $attribute) { - $used_aids[] = $attribute->aid; + foreach ($attributes as $aid => $attribute) { + $used_aids[] = $aid; } if ($view == 'overview') { @@ -644,7 +616,8 @@ function uc_object_attributes_form($form, &$form_state, $object, $type, $view = if (count($attributes) > 0) { foreach ($attributes as $attribute) { - $option = isset($attribute->options[$attribute->default_option]) ? $attribute->options[$attribute->default_option] : NULL; + $attribute = (object) $attribute; + $option = isset($attribute->options[$attribute->default_option]) ? (object) $attribute->options[$attribute->default_option] : NULL; $form['attributes'][$attribute->aid] = array( 'remove' => array( @@ -702,10 +675,11 @@ function uc_object_attributes_form($form, &$form_state, $object, $type, $view = elseif ($view == 'add') { // Get list of attributes not already assigned to this node or class. $unused_attributes = array(); - $result = db_query("SELECT a.aid, a.name, a.label FROM {uc_attributes} a LEFT JOIN {uc_attribute_options} ao ON a.aid = ao.aid GROUP BY a.aid, a.name, a.label ORDER BY a.name"); - foreach ($result as $attribute) { - if (!in_array($attribute->aid, $used_aids)) { - $unused_attributes[$attribute->aid] = $attribute->name; + $config = config('uc_attribute.attributes'); + $attributes_global = $config->get('attributes'); + foreach ($attributes_global as $attribute_global) { + if (!in_array($attribute_global['aid'], $used_aids)) { + $unused_attributes[$attribute_global['aid']] = $attribute_global['name']; } } @@ -810,16 +784,16 @@ function theme_uc_object_attributes_form($variables) { */ function uc_object_attributes_form_submit($form, &$form_state) { if ($form_state['values']['type'] == 'product') { - $attr_table = 'uc_product_attributes'; $opt_table = 'uc_product_options'; $id = 'nid'; } elseif ($form_state['values']['type'] == 'class') { - $attr_table = 'uc_class_attributes'; - $opt_table = 'uc_class_attribute_options'; $id = 'pcid'; } + $config = config('uc_attribute.attributes'); + + // Attributes list: for the product node or class. if ($form_state['values']['view'] == 'overview' && is_array($form_state['values']['attributes'])) { $changed = FALSE; @@ -830,32 +804,51 @@ function uc_object_attributes_form_submit($form, &$form_state) { else { $attribute['aid'] = $aid; $attribute[$id] = $form_state['values']['id']; - backdrop_write_record($attr_table, $attribute, array('aid', $id)); + if ($form_state['values']['type'] == 'product') { + backdrop_write_record('uc_product_attributes', $attribute, array('aid', $id)); + } + elseif ($form_state['values']['type'] == 'class') { + unset($attribute['remove']); + $config->set("classes.{$form_state['values']['id']}.{$aid}", $attribute); + $config->save(); + } $changed = TRUE; } } + // Removing attributes. if (isset($remove_aids)) { $id_value = $form_state['values']['id']; - $select = db_select('uc_attribute_options', 'ao') - ->fields('ao', array('oid')) - ->condition('ao.aid', $remove_aids, 'IN'); - db_delete($opt_table) - ->condition('oid', $select, 'IN') - ->condition($id, $id_value) - ->execute(); - - db_delete($attr_table) - ->condition($id, $id_value) - ->condition('aid', $remove_aids, 'IN') - ->execute(); + // @todo replace with standard separate functions for product and class. + // Class will need to cascade to products and delete related ones too. if ($form_state['values']['type'] == 'product') { + $remove_oids = array(); + foreach ($remove_aids as $aid) { + $options = $config->get("attributes.{$aid}.options"); + $remove_oids += array_keys($options); + } + db_delete('uc_product_options') + ->condition('oid', $remove_oids, 'IN') + ->condition('nid', $id_value) + ->execute(); + + db_delete('uc_product_attributes') + ->condition('nid', $id_value) + ->condition('aid', $remove_aids, 'IN') + ->execute(); + db_delete('uc_product_adjustments') ->condition('nid', $id_value) ->execute(); } + elseif ($form_state['values']['type'] == 'class') { + foreach ($remove_aids as $aid) { + $config->clear("classes.{$id_value}.{$aid}"); + } + $config->save(); + } backdrop_set_message(format_plural(count($remove_aids), '1 attribute has been removed.', '@count attributes have been removed.')); } @@ -864,36 +857,55 @@ function uc_object_attributes_form_submit($form, &$form_state) { backdrop_set_message(t('The changes have been saved.')); } } + // Adding an attribute. elseif ($form_state['values']['view'] == 'add') { foreach (array_filter($form_state['values']['add_attributes']) as $aid) { // Enable all options for added attributes. - $attribute = uc_attribute_load($aid); + // $attribute = uc_attribute_load($aid); + $attribute = (object) $config->get("attributes.{$aid}"); $oid = 0; if (isset($attribute->options)) { foreach ($attribute->options as $option) { - $option->$id = $form_state['values']['id']; - backdrop_write_record($opt_table, $option); - $option->aid = $aid; + $option[$id] = $form_state['values']['id']; + if ($form_state['values']['type'] == 'product') { + backdrop_write_record('uc_product_options', $option); + } + $option['aid'] = $aid; } // Make the first option (if any) the default. if ($option = reset($attribute->options)) { - $oid = $option->oid; + $oid = $option['oid']; } } - $select = db_select('uc_attributes', 'a') - ->condition('aid', $aid); - $select->addExpression(':id', $id, array(':id' => $form_state['values']['id'])); - $select->addField('a', 'aid'); - $select->addField('a', 'label'); - $select->addField('a', 'ordering'); - $select->addExpression(':oid', 'default_option', array(':oid' => $oid)); - $select->addField('a', 'required'); - $select->addField('a', 'display'); - - db_insert($attr_table) - ->from($select) - ->execute(); + $attribute_global = $config->get("attributes.{$aid}"); + $attribute_global['default_option'] = $oid; + + $attribute->default_option = $oid; + + if ($form_state['values']['type'] == 'product') { + $product_attribute = $attribute; + $product_attribute->nid = $form_state['values']['id']; + $product_attribute->default_option = $oid; + unset($product_attribute->name); + unset($product_attribute->description); + db_insert('uc_product_attributes') + ->fields((array) $product_attribute) + ->execute(); + } + elseif ($form_state['values']['type'] == 'class') { + $class_attribute = $attribute; + $class_attribute->pcid = $form_state['values']['id']; + $class_attribute->default_option = $oid; + unset($class_attribute->name); + unset($class_attribute->description); + unset($class_attribute->remove); + + $config->set("classes.{$class_attribute->pcid}.{$aid}", $class_attribute); + } + } + if ($form_state['values']['type'] == 'class') { + $config->save(); } $num = count(array_filter($form_state['values']['add_attributes'])); if ($num > 0) { @@ -925,24 +937,31 @@ function uc_object_attributes_form_submit($form, &$form_state) { * @ingroup forms */ function uc_object_options_form($form, &$form_state, $object, $type) { + $config = config('uc_attribute.attributes'); + if ($type == 'product') { $product = $object; $id = $product->nid; backdrop_set_title($product->title); $attributes = uc_product_get_attributes($id); - $table = 'uc_product_options'; - $id_type = 'nid'; } elseif ($type == 'class') { $class = $object; $id = $class->pcid; backdrop_set_title($class->name); - $attributes = uc_class_get_attributes($id); - $table = 'uc_class_attribute_options'; - $id_type = 'pcid'; + // $attributes = uc_class_get_attributes($id); + // Sort by null_order, ordering, default_ordering, name. + $attributes = $config->get('classes.' . $id); + if (!$attributes) { + $attributes = array(); + } + // Add defaults based on attribute_options. } foreach ($attributes as $aid => $attribute) { + if ($type == 'class') { + $attribute = uc_attribute_class_load($aid, $id); + } $form['attributes'][$aid]['name'] = array( '#markup' => check_plain($attribute->name), ); @@ -957,40 +976,63 @@ function uc_object_options_form($form, &$form_state, $object, $type) { $form['attributes'][$aid]['options'] = array('#weight' => 2); - $base_attr = uc_attribute_load($attribute->aid); + if ($type == 'product') { + $base_attr = uc_attribute_load($attribute->aid); + } + elseif ($type == 'class') { + $base_attr = $attribute; + } if ($base_attr->options) { $options = array(); - $query = db_select('uc_attribute_options', 'ao') - ->fields('ao', array( - 'aid', - 'oid', - 'name', - )); - $query->leftJoin($table, 'po', "ao.oid = po.oid AND po.$id_type = :id", array(':id' => $id)); - - $query->addField('ao', 'cost', 'default_cost'); - $query->addField('ao', 'price', 'default_price'); - $query->addField('ao', 'weight', 'default_weight'); - $query->addField('ao', 'ordering', 'default_ordering'); - - $query->fields('po', array( - 'cost', - 'price', - 'weight', - 'ordering', - )) - ->addExpression('CASE WHEN po.ordering IS NULL THEN 1 ELSE 0 END', 'null_order'); - - $query->condition('aid', $attribute->aid) - ->orderBy('null_order') - ->orderBy('po.ordering') - ->orderBy('default_ordering') - ->orderBy('ao.name'); - - $result = $query->execute(); - foreach ($result as $option) { + if ($type == 'product') { + $query = db_select('uc_product_options', 'po') + ->fields('po', array( + 'oid', + 'cost', + 'price', + 'weight', + 'ordering', + )); + $query->condition('nid', $product->nid); + $query->fields('po', array( + + )) + ->addExpression('CASE WHEN po.ordering IS NULL THEN 1 ELSE 0 END', 'null_order'); + + $query->orderBy('null_order') + ->orderBy('po.ordering'); + //->orderBy('default_ordering') + //->orderBy('ao.name'); + + $result = $query->execute(); + + $options_refined = array(); + foreach ($result as $option) { + $options_refined[$option->oid] = (object) array( + 'oid' => $option->oid, + 'aid' => $aid, + 'name' => $config->get("attributes.{$aid}.options.{$option->oid}.name"), + 'default_cost' => $config->get("attributes.{$aid}.options.{$option->oid}.cost"), + 'default_price' => $config->get("attributes.{$aid}.options.{$option->oid}.price"), + 'default_weight' => $config->get("attributes.{$aid}.options.{$option->oid}.weight"), + 'default_ordering' => $config->get("attributes.{$aid}.options.{$option->oid}.ordering"), + 'cost' => $option->cost, + 'price' => $option->price, + 'weight' => $option->weight, + 'ordering' => $option->ordering, + ); + } + + } + elseif($type == 'class') { + $options_refined = $attribute->options; + } + foreach ($options_refined as $option) { + if ($type == 'class') { + $option = (object) $option; + } $oid = $option->oid; $options[$oid] = ''; @@ -1220,52 +1262,70 @@ function uc_object_options_form_validate($form, &$form_state) { */ function uc_object_options_form_submit($form, &$form_state) { if ($form_state['values']['type'] == 'product') { - $attr_table = 'uc_product_attributes'; - $opt_table = 'uc_product_options'; $id = 'nid'; } elseif ($form_state['values']['type'] == 'class') { - $attr_table = 'uc_class_attributes'; - $opt_table = 'uc_class_attribute_options'; $id = 'pcid'; + $config = config('uc_attribute.attributes'); } foreach ($form_state['values']['attributes'] as $attribute) { if (isset($attribute['default'])) { - db_update($attr_table) - ->fields(array( - 'default_option' => $attribute['default'], - )) - ->condition($id, $form_state['values']['id']) - ->condition('aid', $attribute['aid']) - ->execute(); + if ($form_state['values']['type'] == 'product') { + db_update('uc_product_attributes') + ->fields(array( + 'default_option' => $attribute['default'], + )) + ->condition('nid', $form_state['values']['id']) + ->condition('aid', $attribute['aid']) + ->execute(); + } + elseif ($form_state['values']['type'] == 'class') { + $config->set('classes.' . $form_state['values']['id'] . '.' . $attribute['aid'] . '.default_option', $attribute['default']); + } } if (isset($attribute['options'])) { - db_delete($opt_table) + if ($form_state['values']['type'] == 'product') { + db_delete('uc_product_options') ->condition($id, $form_state['values']['id']) ->condition('oid', array_keys($attribute['options']), 'IN') ->execute(); + } foreach ($attribute['options'] as $oid => $option) { - if ($option['select']) { - $option[$id] = $form_state['values']['id']; - $option['oid'] = $oid; + if ($form_state['values']['type'] == 'product') { + if ($option['select']) { + $option[$id] = $form_state['values']['id']; + $option['oid'] = $oid; - backdrop_write_record($opt_table, $option); + backdrop_write_record('uc_product_options', $option); + } + else { + $aid = $attribute['aid']; + $match = 'i:' . $aid . ';s:' . strlen($oid) . ':"' . $oid . '";'; + db_delete('uc_product_adjustments') + ->condition('nid', $form_state['values']['id']) + ->condition('combination', '%' . db_like($match) . '%', 'LIKE') + ->execute(); + } } - elseif ($form_state['values']['type'] == 'product') { - $aid = $attribute['aid']; - $match = 'i:' . $aid . ';s:' . strlen($oid) . ':"' . $oid . '";'; - db_delete('uc_product_adjustments') - ->condition('nid', $form_state['values']['id']) - ->condition('combination', '%' . db_like($match) . '%', 'LIKE') - ->execute(); + elseif ($form_state['values']['type'] == 'class') { + if ($option['select']) { + $option[$id] = $form_state['values']['id']; + $option['oid'] = $oid; + + $config->set('classes.' . $form_state['values']['id'] . '.' . $attribute['aid'] . '.options.' . $oid, $option); + } } } } } + if ($form_state['values']['type'] == 'class') { + $config->save(); + } + backdrop_set_message(t('The @type options have been saved.', array('@type' => $form_state['values']['type'] == 'product' ? t('product') : t('product class')))); if ($form_state['values']['type'] == 'product') { diff --git a/uc_attribute/uc_attribute.install b/uc_attribute/uc_attribute.install index 2fbc2903..fab60ddd 100644 --- a/uc_attribute/uc_attribute.install +++ b/uc_attribute/uc_attribute.install @@ -10,255 +10,247 @@ function uc_attribute_schema() { $schema = array(); - $schema['uc_attributes'] = array( - 'description' => 'Attributes: the decisions that need to be made about products.', - 'fields' => array( - 'aid' => array( - 'description' => 'Primary key: attribute ID.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'name' => array( - 'description' => 'Name of the attribute.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'label' => array( - 'description' => 'Label to use when attribute is displayed.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'ordering' => array( - 'description' => 'Determines the list position of attributes.', - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - 'required' => array( - 'description' => 'Flag that, if set, requires a user response for attributes (disables default options).', - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'display' => array( - 'description' => 'Display type of the attribute options: 0 => text fields, 1 => select box (default), 2 => radio buttons.', - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 1, - ), - 'description' => array( - 'description' => 'Description of the attribute.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('aid'), - ); + // $schema['uc_attributes'] = array( + // 'description' => 'Attributes: the decisions that need to be made about products.', + // 'fields' => array( + // 'aid' => array( + // 'description' => 'Primary key: attribute ID.', + // 'type' => 'serial', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // ), + // 'name' => array( + // 'description' => 'Name of the attribute.', + // 'type' => 'varchar', + // 'length' => 255, + // 'not null' => TRUE, + // 'default' => '', + // ), + // 'label' => array( + // 'description' => 'Label to use when attribute is displayed.', + // 'type' => 'varchar', + // 'length' => 255, + // 'not null' => TRUE, + // 'default' => '', + // ), + // 'ordering' => array( + // 'description' => 'Determines the list position of attributes.', + // 'type' => 'int', + // 'size' => 'tiny', + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'required' => array( + // 'description' => 'Flag that, if set, requires a user response for attributes (disables default options).', + // 'type' => 'int', + // 'size' => 'tiny', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'display' => array( + // 'description' => 'Display type of the attribute options: 0 => text fields, 1 => select box (default), 2 => radio buttons.', + // 'type' => 'int', + // 'size' => 'tiny', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 1, + // ), + // 'description' => array( + // 'description' => 'Description of the attribute.', + // 'type' => 'varchar', + // 'length' => 255, + // 'not null' => TRUE, + // 'default' => '', + // ), + // ), + // 'primary key' => array('aid'), + // ); - $schema['uc_attribute_options'] = array( - 'description' => 'The available choices for each attribute.', - 'fields' => array( - 'aid' => array( - 'description' => 'The {uc_attributes}.aid.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'oid' => array( - 'description' => 'Primary key: the option ID.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'name' => array( - 'description' => 'The name of the option.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'cost' => array( - 'description' => "The adjustment to a product's cost with the chosen option.", - 'type' => 'numeric', - 'precision' => 16, - 'scale' => 5, - 'not null' => TRUE, - 'default' => 0, - ), - 'price' => array( - 'description' => "The adjustment to a product's price with the chosen option.", - 'type' => 'numeric', - 'precision' => 16, - 'scale' => 5, - 'not null' => TRUE, - 'default' => 0, - ), - 'weight' => array( - 'description' => "The adjustment to a product's physical weight with the chosen option.", - 'type' => 'float', - 'not null' => TRUE, - 'default' => 0, - ), - 'ordering' => array( - 'description' => 'Affects the list position of the options.', - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('oid'), - 'indexes' => array( - 'aid' => array('aid'), - ), - 'foreign keys' => array( - 'uc_attributes' => array( - 'table' => 'uc_attributes', - 'columns' => array('aid' => 'aid'), - ), - ), - ); + // $schema['uc_attribute_options'] = array( + // 'description' => 'The available choices for each attribute.', + // 'fields' => array( + // 'aid' => array( + // 'description' => 'The {uc_attributes}.aid.', + // 'type' => 'int', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'oid' => array( + // 'description' => 'Primary key: the option ID.', + // 'type' => 'serial', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // ), + // 'name' => array( + // 'description' => 'The name of the option.', + // 'type' => 'varchar', + // 'length' => 255, + // 'not null' => TRUE, + // 'default' => '', + // ), + // 'cost' => array( + // 'description' => "The adjustment to a product's cost with the chosen option.", + // 'type' => 'numeric', + // 'precision' => 16, + // 'scale' => 5, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'price' => array( + // 'description' => "The adjustment to a product's price with the chosen option.", + // 'type' => 'numeric', + // 'precision' => 16, + // 'scale' => 5, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'weight' => array( + // 'description' => "The adjustment to a product's physical weight with the chosen option.", + // 'type' => 'float', + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'ordering' => array( + // 'description' => 'Affects the list position of the options.', + // 'type' => 'int', + // 'size' => 'tiny', + // 'not null' => TRUE, + // 'default' => 0, + // ), + // ), + // 'primary key' => array('oid'), + // 'indexes' => array( + // 'aid' => array('aid'), + // ), + // 'foreign keys' => array( + // 'uc_attributes' => array( + // 'table' => 'uc_attributes', + // 'columns' => array('aid' => 'aid'), + // ), + // ), + // ); - $schema['uc_class_attributes'] = array( - 'description' => 'Attributes copied to a product of a certain class when it is created.', - 'fields' => array( - 'pcid' => array( - 'description' => 'Primary key: the product {node}.type.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'aid' => array( - 'description' => 'The {uc_attributes}.aid.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'label' => array( - 'description' => 'Label to use when attribute is displayed.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'ordering' => array( - 'description' => 'Determines the list position of attributes.', - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - 'default_option' => array( - 'description' => 'The default value of the attribute field on the add to cart form.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'required' => array( - 'description' => "A flag indicating that, if set, requires a user response for attributes (disables default options).", - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'display' => array( - 'description' => 'Display type of the attribute options: 0 => text fields, 1 => select box (default), 2 => radio buttons', - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 1, - ), - ), - 'primary key' => array('pcid', 'aid'), - 'foreign keys' => array( - 'uc_product_classes' => array( - 'table' => 'uc_product_classes', - 'columns' => array('pcid' => 'pcid'), - ), - 'uc_attributes' => array( - 'table' => 'uc_attributes', - 'columns' => array('aid' => 'aid'), - ), - ), - ); + // $schema['uc_class_attributes'] = array( + // 'description' => 'Attributes copied to a product of a certain class when it is created.', + // 'fields' => array( + // 'pcid' => array( + // 'description' => 'Primary key: the product {node}.type.', + // 'type' => 'varchar', + // 'length' => 32, + // 'not null' => TRUE, + // 'default' => '', + // ), + // 'aid' => array( + // 'description' => 'The {uc_attributes}.aid.', + // 'type' => 'int', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'label' => array( + // 'description' => 'Label to use when attribute is displayed.', + // 'type' => 'varchar', + // 'length' => 255, + // 'not null' => TRUE, + // 'default' => '', + // ), + // 'ordering' => array( + // 'description' => 'Determines the list position of attributes.', + // 'type' => 'int', + // 'size' => 'tiny', + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'default_option' => array( + // 'description' => 'The default value of the attribute field on the add to cart form.', + // 'type' => 'int', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'required' => array( + // 'description' => "A flag indicating that, if set, requires a user response for attributes (disables default options).", + // 'type' => 'int', + // 'size' => 'tiny', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'display' => array( + // 'description' => 'Display type of the attribute options: 0 => text fields, 1 => select box (default), 2 => radio buttons', + // 'type' => 'int', + // 'size' => 'tiny', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 1, + // ), + // ), + // 'primary key' => array('pcid', 'aid'), + // 'foreign keys' => array( + // 'uc_attributes' => array( + // 'table' => 'uc_attributes', + // 'columns' => array('aid' => 'aid'), + // ), + // ), + // ); - $schema['uc_class_attribute_options'] = array( - 'description' => 'The available choices for each attribute.', - 'fields' => array( - 'pcid' => array( - 'description' => 'Primary key: the product {node}.type.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'oid' => array( - 'description' => 'The {uc_attribute_options}.oid.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'cost' => array( - 'description' => "The adjustment to a product's cost with the chosen option.", - 'type' => 'numeric', - 'precision' => 16, - 'scale' => 5, - 'not null' => TRUE, - 'default' => 0, - ), - 'price' => array( - 'description' => "The adjustment to a product's price with the chosen option.", - 'type' => 'numeric', - 'precision' => 16, - 'scale' => 5, - 'not null' => TRUE, - 'default' => 0, - ), - 'weight' => array( - 'description' => "The adjustment to a product's physical weight with the chosen option.", - 'type' => 'float', - 'not null' => TRUE, - 'default' => 0, - ), - 'ordering' => array( - 'description' => 'Affects the list position of the options.', - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('pcid', 'oid'), - 'foreign keys' => array( - 'uc_product_classes' => array( - 'table' => 'uc_product_classes', - 'columns' => array('pcid' => 'pcid'), - ), - 'uc_attribute_options' => array( - 'table' => 'uc_attribute_options', - 'columns' => array('oid' => 'oid'), - ), - ), - ); + // $schema['uc_class_attribute_options'] = array( + // 'description' => 'The available choices for each attribute.', + // 'fields' => array( + // 'pcid' => array( + // 'description' => 'Primary key: the product {node}.type.', + // 'type' => 'varchar', + // 'length' => 32, + // 'not null' => TRUE, + // 'default' => '', + // ), + // 'oid' => array( + // 'description' => 'The {uc_attribute_options}.oid.', + // 'type' => 'int', + // 'unsigned' => TRUE, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'cost' => array( + // 'description' => "The adjustment to a product's cost with the chosen option.", + // 'type' => 'numeric', + // 'precision' => 16, + // 'scale' => 5, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'price' => array( + // 'description' => "The adjustment to a product's price with the chosen option.", + // 'type' => 'numeric', + // 'precision' => 16, + // 'scale' => 5, + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'weight' => array( + // 'description' => "The adjustment to a product's physical weight with the chosen option.", + // 'type' => 'float', + // 'not null' => TRUE, + // 'default' => 0, + // ), + // 'ordering' => array( + // 'description' => 'Affects the list position of the options.', + // 'type' => 'int', + // 'size' => 'tiny', + // 'not null' => TRUE, + // 'default' => 0, + // ), + // ), + // 'primary key' => array('pcid', 'oid'), + // 'foreign keys' => array( + // 'uc_attribute_options' => array( + // 'table' => 'uc_attribute_options', + // 'columns' => array('oid' => 'oid'), + // ), + // ), + // ); $schema['uc_product_attributes'] = array( 'description' => 'Attributes copied to a product.', @@ -321,10 +313,10 @@ function uc_attribute_schema() { 'table' => 'uc_products', 'columns' => array('nid' => 'nid'), ), - 'uc_attributes' => array( - 'table' => 'uc_attributes', - 'columns' => array('aid' => 'aid'), - ), + // 'uc_attributes' => array( + // 'table' => 'uc_attributes', + // 'columns' => array('aid' => 'aid'), + // ), ), ); @@ -381,10 +373,10 @@ function uc_attribute_schema() { 'table' => 'uc_products', 'columns' => array('nid' => 'nid'), ), - 'uc_attribute_options' => array( - 'table' => 'uc_attribute_options', - 'columns' => array('oid' => 'oid'), - ), + // 'uc_attribute_options' => array( + // 'table' => 'uc_attribute_options', + // 'columns' => array('oid' => 'oid'), + // ), ), ); @@ -457,8 +449,15 @@ function uc_attribute_update_1000() { } /** - * Implements hook_install(). + * Convert Ubercart attributes to config. */ -function uc_attribute_install() { - // Dynamically generated variable data was detected. +function uc_attribute_update_1001() { + +} + +/** + * Convert Ubercart class attributes to config. + */ +function uc_attribute_update_1002() { + } diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module index 047a16f0..58656752 100644 --- a/uc_attribute/uc_attribute.module +++ b/uc_attribute/uc_attribute.module @@ -92,6 +92,17 @@ function uc_attribute_menu() { 'weight' => 1, 'file' => 'uc_attribute.admin.inc', ); + // $items['admin/store/products/classes/%uc_product_class/attributes/add'] = array( + // 'title' => 'Add an attribute', + // 'description' => 'Add an attribute to this product.', + // 'page callback' => 'backdrop_get_form', + // 'page arguments' => array('uc_object_attributes_form', 1, 'class', 'add'), + // 'access callback' => 'uc_attribute_product_class_access', + // 'access arguments' => array(1), + // 'type' => MENU_LOCAL_ACTION, + // 'weight' => 1, + // 'file' => 'uc_attribute.admin.inc', + // ); $items['admin/store/products/classes/%uc_product_class/options'] = array( 'title' => 'Options', 'description' => 'Administer product class options.', @@ -380,9 +391,20 @@ function uc_attribute_node_insert($node) { // SELECT $node->nid AS nid. $select->addExpression(':nid', 'nid', array(':nid' => $node->nid)); - db_insert('uc_product_attributes') - ->from($select) - ->execute(); + $config = config('uc_attribute.attributes'); + $class_attributes = $config->get("classes.{$node->type}"); + unset($class_attributes['options']); + unset($class_attributes['pcid']); + $class_attributes['nid'] = $node->nid; + foreach ($class_attributes as $attribute) { + db_insert('uc_product_attributes') + ->fields($class_attributes) + ->execute(); + } + + // db_insert('uc_product_attributes') + // ->from($select) + // ->execute(); // Set options from class options. $select = db_select('uc_class_attribute_options', 'co') @@ -396,8 +418,17 @@ function uc_attribute_node_insert($node) { ->condition('pcid', $node->type); $select->addExpression(':nid', 'nid', array(':nid' => $node->nid)); + $class_options = array(); + foreach($class_attributes as $class_attribute) { + $class_attribute['options']['nid'] = $node->nid; + unset($class_attribute['options']['oid']); + unset($class_attribute['options']['pcid']); + $class_options += $class_attribute['options']; + } + db_insert('uc_product_options') - ->from($select) + //->from($select) + ->fields($class_options) ->execute(); } @@ -497,13 +528,16 @@ function uc_attribute_uc_order_product_alter(&$product, $order) { function uc_attribute_uc_product_class($type, $op) { switch ($op) { case 'delete': - db_delete('uc_class_attributes') - ->condition('pcid', $type) - ->execute(); + $config = config('uc_attribute.attributes'); + $config->clear("classes.{$type}"); + $config->save(); + // db_delete('uc_class_attributes') + // ->condition('pcid', $type) + // ->execute(); - db_delete('uc_class_attribute_options') - ->condition('pcid', $type) - ->execute(); + // db_delete('uc_class_attribute_options') + // ->condition('pcid', $type) + // ->execute(); break; } @@ -524,7 +558,7 @@ function uc_attribute_uc_product_alter(&$node) { $combination = array(); foreach ($node->data['attributes'] as $aid => $value) { if (is_numeric($value)) { - $attribute = uc_attribute_load($aid, $node->nid, 'product'); + $attribute = uc_attribute_product_load($aid, $node->nid); if ($attribute && ($attribute->display == 1 || $attribute->display == 2)) { $combination[$aid] = $value; } @@ -586,7 +620,33 @@ function uc_attribute_uc_product_description($product) { } /** - * Loads attribute objects from the database. + * Loads class attribute objects from the database. + * + * @param array $aids + * Attribute IDs to load. + * @param int $pcid + * The ID of the class this attribute belongs to. + * + * @return array + * An array of loaded attributes. + */ +function uc_attribute_class_load_multiple(array $aids, $pcid) { + $config = config('uc_attribute.attributes'); + $class_attributes = array(); + foreach($aids as $aid) { + $attr = $config->get("classes.{$pcid}.{$aid}"); + $attr['name'] = $config->get("attributes.{$aid}.name"); + $class_attributes[$aid] = (object) $attr; + } + + // @todo order by ordering and name. Does the following work? + backdrop_sort($result, array('ordering' => SORT_NUMERIC, 'name' => SORT_STRING)); + + return $class_attributes; +} + +/** + * Loads product attribute objects from the database. * * @todo If we feel it necessary, we could optimize this, by inverting the * logic; that is, we could make uc_attribute load call this function and allow @@ -594,46 +654,28 @@ function uc_attribute_uc_product_description($product) { * * @param array $aids * Attribute IDs to load. - * @param string $type - * The type of attribute. 'product', or 'class'. Any other type will fetch - * a base attribute. - * @param int $id - * The ID of the product/class this attribute belongs to. + * @param int $nid + * The ID of the product this attribute belongs to. * * @return array * An array of loaded attributes. */ -function uc_attribute_load_multiple(array $aids = array(), $type = '', $id = NULL) { - $sql = uc_attribute_type_info($type); - - // Product/class attributes. - if (!empty($type)) { - // Seems like a big query to get attribute IDs, but it's all about the sort. - // (I'm not sure if the default ordering is propagating down correctly here. - // It appears that product/class attributes with no ordering won't let the - // attribute's propagate down, as it does when loading. -cha0s) - $query = db_select($sql['attr_table'], 'uca') - ->fields('uca', array('aid')) - ->condition("uca.{$sql['id']}", $id); - - $query->leftJoin('uc_attributes', 'ua', 'uca.aid = ua.aid'); +function uc_attribute_product_load_multiple(array $aids = array(), $nid = NULL) { + // Seems like a big query to get attribute IDs, but it's all about the sort. + // (I'm not sure if the default ordering is propagating down correctly here. + // It appears that product/class attributes with no ordering won't let the + // attribute's propagate down, as it does when loading. -cha0s) + $query = db_select('uc_product_attributes', 'pa') + ->fields('pa', array('aid')) + ->condition("pa.nid", $nid); - $query->orderBy('uca.ordering') - ->orderBy('ua.name'); - } - // Base attributes. - else { - $query = db_select('uc_attributes', 'ua') - ->fields('ua', array('aid')) - ->orderBy('ordering') - ->orderBy('name'); - } + $query->orderBy('uca.ordering'); // Filter by the attribute IDs requested. if (!empty($aids)) { // Sanity check - filter out non-numeric attribute IDs. $aids = array_filter($aids, 'is_numeric'); - $query->condition('ua.aid', $aids, 'IN'); + $query->condition('pa.aid', $aids, 'IN'); } $aids = $query->execute()->fetchCol(); @@ -641,20 +683,50 @@ function uc_attribute_load_multiple(array $aids = array(), $type = '', $id = NUL // Load the attributes. $attributes = array(); foreach ($aids as $aid) { - $attributes[$aid] = uc_attribute_load($aid, $id, $type); + // Break this up. + $attributes[$aid] = uc_attribute_load($aid, $nid); } return $attributes; } /** - * Loads an attribute from the database. + * Loads attribute objects from the database. + * + * @todo If we feel it necessary, we could optimize this, by inverting the + * logic; that is, we could make uc_attribute load call this function and allow + * this function to minimize the number of queries necessary. -cha0s + * + * @param array $aids + * Attribute IDs to load. + * @param int $id + * The ID of the product/class this attribute belongs to. + * + * @return array + * An array of loaded attributes. + */ +function uc_attribute_global_load_multiple(array $aids = array(), $id = NULL) { + $config = config('uc_attribute.attributes'); + $attributes = array(); + foreach($aids as $aid) { + $attr = $config->get("attributes.{$aid}"); + $attributes[$aid] = (object) $attr; + } + + // @todo order by ordering and name. + backdrop_sort($attributes, array('ordering' => SORT_NUMERIC, 'name' => SORT_STRING)); + + return $attributes; +} + +/** + * Loads an attribute. * * @param int $aid * The ID of the attribute. * @param int $id * The ID of the product/class this attribute belongs to. - * @param string $type + * @param string $type * The type of attribute. 'product', or 'class'. Any other type will fetch * a base attribute. * @@ -662,70 +734,118 @@ function uc_attribute_load_multiple(array $aids = array(), $type = '', $id = NUL * The attribute object, or FALSE if it doesn't exist. */ function uc_attribute_load($aid, $id = NULL, $type = '') { - $sql = uc_attribute_type_info($type); - switch ($type) { case 'product': + $attribute = uc_attribute_product_load($aid, $id); + break; case 'class': - // Read attribute data. - $query = db_select('uc_attributes', 'a') - ->fields('a', array('aid', 'name', 'description')) - ->condition('a.aid', $aid); - - $query->leftJoin($sql['attr_table'], 'pa', "a.aid = pa.aid AND pa.{$sql['id']} = :id", array(':id' => $id)); - $query->fields('pa', array('label', 'default_option', 'required', 'ordering', 'display', $sql['id'])); - $query->addField('a', 'label', 'default_label'); - $query->addField('a', 'ordering', 'default_ordering'); - $query->addField('a', 'required', 'default_required'); - $query->addField('a', 'display', 'default_display'); - - $attribute = $query->execute()->fetchObject(); - - // Don't try to build it further if it failed already. - if (!$attribute) { - return FALSE; - } + $attribute = uc_attribute_class_load($aid, $id); + break; + default: + $attribute = uc_attribute_global_load($aid); + break; + } - // Set any missing defaults. - foreach (array('ordering', 'required', 'display', 'label') as $field) { - if (isset($attribute->{"default_$field"}) && is_null($attribute->$field)) { - $attribute->$field = $attribute->{"default_$field"}; - } - } - if (empty($attribute->label)) { - $attribute->label = $attribute->name; - } + return $attribute; +} - // Read option data. - $query = db_select($sql['opt_table'], 'po') - ->fields('po', array($sql['id'], 'oid', 'cost', 'price', 'weight', 'ordering')); - $query->leftJoin('uc_attribute_options', 'ao', "po.oid = ao.oid AND po.{$sql['id']} = :id", array(':id' => $id)); - $query->fields('ao', array('name', 'aid')) - ->condition('aid', $aid) - ->orderBy('po.ordering') - ->orderBy('ao.name'); +/** + * Load attribute used in a class. + * + * @param int $aid + * The ID of the attribute. + * @param int $pcid + * The ID of the class this attribute belongs to. + * + * @return object|false + * The attribute object, or FALSE if it doesn't exist. + */ +function uc_attribute_class_load($aid, $pcid) { + $config = config('uc_attribute.attributes'); + $attribute = new stdClass(); + $class_attribute = $config->get("classes.{$pcid}.{$aid}"); + $attribute_global = $config->get("attributes.{$aid}"); + if ($class_attribute) { + // @todo Sort options by ordering, name. + $class_attribute['name'] = $attribute_global['name']; + $attribute = (object) $class_attribute; + } + else { + return FALSE; + } - $result = $query->execute(); - break; + if (isset($attribute->options)) { + foreach ($attribute->options as $option) { + $attribute->options[$option['oid']] = (object) $option; + } + //backdrop_sort($attribute->options, array('ordering' => SORT_NUMERIC, 'name' => SORT_STRING)); + } + else { + $attribute->options = array(); + } - default: - // Read attribute and option data. - $attribute = db_query("SELECT * FROM {uc_attributes} WHERE aid = :aid", array(':aid' => $aid))->fetchObject(); - $result = db_query("SELECT * FROM {uc_attribute_options} WHERE aid = :aid ORDER BY ordering, name", array(':aid' => $aid)); + uc_attribute_translate($attribute); - // Don't try to build it further if it failed already. - if (!$attribute) { - return FALSE; - } - break; + return $attribute; +} + +/** + * Loads an attribute from the database. + * + * @param int $aid + * The ID of the attribute. + * @param int $id + * The ID of the product/class this attribute belongs to. + * + * @return object|false + * The attribute object, or FALSE if it doesn't exist. + */ +function uc_attribute_product_load($aid, $id = NULL) { + // Read attribute data. + $config = config('uc_attribute.attributes'); + + $query = db_select('uc_product_attributes', 'pa') + ->fields('pa', array('label', 'default_option', 'required', 'ordering', 'display', 'nid')); + $query->condition('pa.nid', $id); + + $attribute = $query->execute()->fetchObject(); + + // Don't try to build it further if it failed already. + if (!$attribute) { + return FALSE; } + $attribute->aid = $aid; + $attribute->name = $config->get("attributes.{$aid}.name"); + $attribute->description = $config->get("attributes.{$aid}.description"); + $attribute->default_label = $config->get("attributes.{$aid}.label"); + $attribute->default_ordering = $config->get("attributes.{$aid}.ordering"); + $attribute->default_required = $config->get("attributes.{$aid}.required"); + $attribute->default_display = $config->get("attributes.{$aid}.display"); + + // Set any missing defaults. + foreach (array('ordering', 'required', 'display', 'label') as $field) { + if (isset($attribute->{"default_$field"}) && is_null($attribute->$field)) { + $attribute->$field = $attribute->{"default_$field"}; + } + } + if (empty($attribute->label)) { + $attribute->label = $attribute->name; + } + + // Read option data. + $query = db_select('uc_product_options', 'po') + ->fields('po', array('nid', 'oid', 'cost', 'price', 'weight', 'ordering')); + $result = $query->execute(); + // Got an attribute? if ($attribute) { // Get its options, too. $attribute->options = array(); foreach ($result as $option) { $attribute->options[$option->oid] = $option; + $attribute->options[$option->oid]->aid = $aid; + $attribute->options[$option->oid]->name = $config->get("attributes.{$aid}.options.{$option->oid}.name"); } uc_attribute_translate($attribute); } @@ -733,6 +853,41 @@ function uc_attribute_load($aid, $id = NULL, $type = '') { return $attribute; } +/** + * Loads an attribute. + * + * @param int $aid + * The ID of the attribute. + * @return object|false + * The attribute object, or FALSE if it doesn't exist. + */ +function uc_attribute_global_load($aid) { + $config = config('uc_attribute.attributes'); + $attribute = new stdClass(); + $global_attribute = $config->get("attributes.{$aid}"); + + if ($global_attribute) { + // @todo Sort options by ordering, name. + $attribute = (object) $global_attribute; + } + else { + return FALSE; + } + + if (isset($attribute->options)) { + foreach ($attribute->options as $option) { + $attribute->options[$option['oid']] = (object) $option; + } + } + else { + $attribute->options = array(); + } + + uc_attribute_translate($attribute); + + return $attribute; +} + /** * Fetches an array of attribute objects that belong to a product. * @@ -743,7 +898,7 @@ function uc_attribute_load($aid, $id = NULL, $type = '') { * The array of attribute objects. */ function uc_attribute_load_product_attributes($nid) { - return uc_attribute_load_multiple(array(), 'product', $nid); + return uc_attribute_product_load_multiple(array(), $nid); } /** @@ -752,13 +907,129 @@ function uc_attribute_load_product_attributes($nid) { * @param object $attribute * The attribute object to save. * - * @return int - * The integer result from backdrop_write_record(). + * @return object + * The created attribute. */ function uc_attribute_save(&$attribute) { - // Insert or update? - $key = empty($attribute->aid) ? array() : 'aid'; - return backdrop_write_record('uc_attributes', $attribute, $key); + return uc_attribute_attribute_save($attribute); +} + +/** + * Save global attribute to config + * + * @param object $attribute + * + * @return object + * The created attribute. + */ +function uc_attribute_attribute_save($attribute) { + $config = config('uc_attribute.attributes'); + // @todo make this use a unique name instead. + if (empty($attribute->aid)) { + $attributes = $config->get('attributes'); + $attribute_keys = is_array($attributes) ? array_keys($attributes) : array(); + $attribute->aid = 1; + if (!empty($attribute_keys)) { + $attribute->aid = max($attribute_keys) + 1; + } + } + $config->set("attributes.{$attribute->aid}", $attribute); + $config->save(); + + return $attribute; +} + +/** + * Save attribute to config + * + * @param object $option + */ +function uc_attribute_attribute_option_save($option) { + $config = config('uc_attribute.attributes'); + // Create a unique oid across all attributes. + if (empty($option->oid)) { + $options = array(); + $attributes = $config->get('attributes'); + foreach ($attributes as $attribute) { + $options += !empty($attribute['options']) ? $attribute['options'] : array(); + } + $option_keys = is_array($options) ? array_keys($options) : array(); + $option->oid = 1; + if (!empty($option_keys)) { + $option->oid = max($option_keys) + 1; + } + } + $config->set("attributes.{$option->aid}.options.{$option->oid}", $option); + $config->save(); +} + +/** + * Delete global attribute, cascading to all instances. + * + * @param int $aid + * + * @return bool + */ +function uc_attribute_global_attribute_cascade_delete($aid) { + // Delete the product attributes and their options. + // uc_attribute_subject_delete($aid, 'product'); + $attribute = uc_attribute_load($aid); + if (!$attribute) { + return FALSE; + } + $options = array_keys($attribute->options); + if ($options) { + db_delete('uc_product_options') + ->condition('oid', $options, 'IN') + ->execute(); + } + if ($nodes = db_query("SELECT nid FROM {uc_product_attributes} WHERE aid = :aid", array(':aid' => $attribute->aid))->fetchCol()) { + db_delete('uc_product_adjustments') + ->condition('nid', $nodes, 'IN') + ->execute(); + } + + db_delete('uc_product_attributes') + ->condition('aid', $aid) + ->execute(); + + $config = config('uc_attribute.attributes'); + $classes = $config->get('classes'); + foreach ($classes as $class_name => $class_info) { + $config->clear("classes.{$class_name}.{$aid}"); + } + $config->clear("attributes.{$aid}"); + $config->save(); + + return TRUE; +} + +/** + * Implements hook_config_create(). + */ +function uc_attribute_config_create($staging_config) { + // Do we need to do anything? +} + +/** + * Implements hook_config_update(). + */ +function uc_attribute_config_update($staging_config, $active_config) { + // If a product class is deleted, delete the attributes on the class, + // and on the products. + + // Compare attributes and remove class, product attributes that are + // removed. +} + +/** + * Implements hook_config_delete(). + */ +function uc_attribute_config_delete($active_config) { + // If product class is deleted, remove all attributes. + + // Remove class, product attributes that are + // removed. } /** @@ -772,18 +1043,13 @@ function uc_attribute_save(&$attribute) { */ function uc_attribute_delete($aid) { // Delete the class attributes and their options. - uc_attribute_subject_delete($aid, 'class'); + uc_attribute_class_attribute_delete($aid); // Delete the product attributes and their options. - uc_attribute_subject_delete($aid, 'product'); + uc_attribute_product_attribute_delete($aid); // Delete base attributes and their options. - db_delete('uc_attribute_options') - ->condition('aid', $aid) - ->execute(); - db_delete('uc_attributes') - ->condition('aid', $aid) - ->execute(); + config_clear('uc_attribute.attributes', "{$aid}"); return SAVED_DELETED; } @@ -798,7 +1064,18 @@ function uc_attribute_delete($aid) { * The attribute option object. */ function uc_attribute_option_load($oid) { - return db_query("SELECT * FROM {uc_attribute_options} WHERE oid = :oid", array(':oid' => $oid))->fetchObject(); + $config = config('uc_attribute.attributes'); + $attributes = $config->get('attributes'); + $option = new stdClass(); + foreach ($attributes as $attribute) { + foreach ($attribute['options'] as $option_id => $option_info) { + if ($option_id == $oid) { + $option = (object) $option_info; + continue; + } + } + } + return $option; } /** @@ -807,13 +1084,12 @@ function uc_attribute_option_load($oid) { * @param object $option * The attribute option object to save. * - * @return int - * The integer result from backdrop_write_record(). + * @return bool */ function uc_attribute_option_save(&$option) { - // Insert or update? - $key = empty($option->oid) ? array() : 'oid'; - return backdrop_write_record('uc_attribute_options', $option, $key); + uc_attribute_attribute_option_save($option); + // @todo better return + return TRUE; } /** @@ -827,19 +1103,37 @@ function uc_attribute_option_save(&$option) { */ function uc_attribute_option_delete($oid) { // Delete the class attribute options. - uc_attribute_subject_option_delete($oid, 'class'); + //uc_attribute_subject_option_delete($oid, 'class'); + uc_attribute_class_option_delete($oid); // Delete the product attribute options (and the adjustments!). uc_attribute_subject_option_delete($oid, 'product'); // Delete base attributes and their options. - db_delete('uc_attribute_options') - ->condition('oid', $oid) - ->execute(); + uc_attribute_global_option_delete($oid); return SAVED_DELETED; } +/** + * Delete attribute from config + * + * @param int $aid + * @param int $oid + */ +function uc_attribute_global_option_cascade_delete($aid, $oid) { + // Delete the product attribute options (and the adjustments!). + uc_attribute_product_option_delete($oid); + + $config = config('uc_attribute.attributes'); + $classes = $config->get('classes'); + foreach ($classes as $class_name => $class_info) { + $config->clear("classes.{$class_name}.{$aid}.options.{$oid}"); + } + $config->clear("attributes.{$aid}.options.{$oid}"); + $config->save(); +} + /** * Saves a product/class attribute. * @@ -853,13 +1147,76 @@ function uc_attribute_option_delete($oid) { * Save the product/class attribute's options, too? * * @return int - * The integer result from backdrop_write_record(). + * The integer result from backdrop_write_record(), or, in the case + * of class configuration, return SAVED_INSERT/SAVED_UPDATE. */ function uc_attribute_subject_save(&$attribute, $type, $id, $save_options = FALSE) { - $sql = uc_attribute_type_info($type); + $return = 0; + if ($type == 'class') { + $return = uc_attribute_class_attribute_save($attribute, $id); + } + elseif ($type == 'product') { + $return = uc_attribute_product_attribute_save($attribute, $id, $save_options); + } + return $return; +} +/** + * Saves a class attribute. + * + * @param &$attribute + * The class attribute. + * @param string $pcid + * The class ID. + * @param bool $save_options + * Save the class attribute's options, too? + * + * @return int + * The int SAVED_INSERT or SAVED_UPDATE. + */ +function uc_attribute_class_attribute_save(&$attribute, $pcid, $save_options = FALSE) { + $exists = uc_attribute_subject_exists($attribute->aid, 'class', $pcid); + // First, save the options. First because if this is an insert, we'll set + // a default option for the class attribute. + if ($save_options && is_array($attribute->options)) { + // Is this an insert? If so, we'll set the default option. + if (!$exists) { + $default_option = 0; + // Make the first option (if any) the default. + if (!empty($attribute->options) && is_array($attribute->options)) { + $option = (object) reset($attribute->options); + $default_option = $option->oid; + } + $attribute->default_option = $default_option; + } + } + + // Merge in the class attribute's ID and save. + $attribute->pcid = $pcid; + + $config = config('uc_attribute.attributes'); + $config->set("classes.{$pcid}.{$attribute->aid}", $attribute); + $config->save(); + + return $exists ? SAVED_UPDATED : SAVED_NEW; +} + +/** + * Saves a product attribute. + * + * @param &$attribute + * The product attribute. + * @param string $nid + * The product ID. + * @param bool $save_options + * Save the product attribute's options, too? + * + * @return int + * The integer result from backdrop_write_record(). + */ +function uc_attribute_product_attribute_save(&$attribute, $nid, $save_options = FALSE) { // Insert or update? - $key = uc_attribute_subject_exists($attribute->aid, $type, $id) ? array('aid', $sql['id']) : array(); + $key = uc_attribute_subject_exists($attribute->aid, 'product', $nid) ? array('aid', 'nid') : array(); // First, save the options. First because if this is an insert, we'll set // a default option for the product/class attribute. @@ -867,7 +1224,7 @@ function uc_attribute_subject_save(&$attribute, $type, $id, $save_options = FALS foreach ($attribute->options as $option) { // Sanity check! $option = (object) $option; - uc_attribute_subject_option_save($option, $type, $id); + uc_attribute_subject_option_save($option, 'product', $nid); } // Is this an insert? If so, we'll set the default option. @@ -883,8 +1240,8 @@ function uc_attribute_subject_save(&$attribute, $type, $id, $save_options = FALS } // Merge in the product/class attribute's ID and save. - $attribute->{$type == 'product' ? 'nid' : 'pcid'} = $id; - $result = backdrop_write_record($sql['attr_table'], $attribute, $key); + $attribute->nid = $nid; + $result = backdrop_write_record('uc_product_attributes', $attribute, $key); return $result; } @@ -903,17 +1260,51 @@ function uc_attribute_subject_save(&$attribute, $type, $id, $save_options = FALS * The Backdrop SAVED_DELETED flag. */ function uc_attribute_subject_delete($aid, $type, $id = NULL) { - $sql = uc_attribute_type_info($type); + if ($type == 'class') { + return uc_attribute_class_attribute_delete($aid); + } + elseif ($type == 'product') { + return uc_attribute_product_attribute_delete($aid, $id); + } +} - $query = db_select('uc_attribute_options', 'a') - ->fields('a', array('oid')); - $query->join($sql['opt_table'], 'subject', 'a.oid = subject.oid'); +/** + * Delete a class attribute + * + * @param int $aid + * + * @return int + * The Backdrop SAVED_DELETED flag. + */ +function uc_attribute_class_attribute_delete($aid) { + $config = config('uc_attribute.attributes'); + $classes = $config->get('classes'); + foreach ($classes as $class_name => $class_info) { + $config->clear("classes.{$class_name}.{$aid}"); + } + $config->clear("attributes.{$aid}"); + $config->save(); + + return SAVED_DELETED; +} + +/** + * Delete a product attribute + * + * @param int $aid + * + * @return int + * The Backdrop SAVED_DELETED flag. + */ +function uc_attribute_product_attribute_delete($aid, $id = NULL) { + $query = db_select('uc_product_options', 'pa') + ->fields('pa', array('oid')); // Base conditions, and an ID check if necessary. $conditions = db_and() ->condition('aid', $aid); if ($id) { - $conditions->condition($sql['id'], $id); + $conditions->condition('nid', $id); } $query->condition($conditions); @@ -921,19 +1312,17 @@ function uc_attribute_subject_delete($aid, $type, $id = NULL) { while ($oid = $result->fetchField()) { // Don't delete the adjustments one at a time. We'll do it in bulk soon for // efficiency. - uc_attribute_subject_option_delete($oid, $type, $id, FALSE); + uc_attribute_subject_option_delete($oid, 'product', $id, FALSE); } - db_delete($sql['attr_table']) + db_delete('uc_product_attributes') ->condition($conditions) ->execute(); // If this is a product attribute, wipe any associated adjustments. - if ($type == 'product') { - uc_attribute_adjustments_delete(array( - 'aid' => $aid, - 'nid' => $id, - )); - } + uc_attribute_adjustments_delete(array( + 'aid' => $aid, + 'nid' => $id, + )); return SAVED_DELETED; } @@ -952,18 +1341,61 @@ function uc_attribute_subject_delete($aid, $type, $id = NULL) { * An object containing the product/class attribute option. */ function uc_attribute_subject_option_load($oid, $type, $id) { - $sql = uc_attribute_type_info($type); + if ($type == 'product') { + $return = uc_attribute_product_option_load($oid, $id); + } + elseif ($type == 'class') { + $return = uc_attribute_class_option_load($oid, $id); + } - $query = db_select($sql['opt_table'], 'po'); - $query->leftJoin('uc_attribute_options', 'ao', 'po.oid = ao.oid'); - $query->fields('po', array($sql['id'], 'oid', 'cost', 'price', 'weight', 'ordering')) - ->fields('ao', array('name', 'aid')) - ->condition('po.oid', $oid) - ->condition("po.{$sql['id']}", $id) - ->orderBy('po.ordering') - ->orderBy('ao.name'); + return $return; +} - return $query->execute()->fetchObject(); +/** + * Loads a class attribute option. + * + * @param int $oid + * The class attribute option ID. + * @param int $id + * The class ID. + * + * @return object + * An object containing the class attribute option. + */ +function uc_attribute_class_option_load($oid, $pcid) { + $config = config('uc_attribute.attributes'); + $class_attributes = $config->get("classes.{$pcid}"); + foreach ($class_attributes as $class_attribute) { + if (!empty($class_attribute['options'][$oid])) { + return $class_attribute['options'][$oid]; + } + } +} + +/** + * Loads a product attribute option. + * + * @param int $oid + * The product attribute option ID. + * @param int $id + * The product/class ID. + * + * @return object + * An object containing the product/class attribute option. + */ +function uc_attribute_product_option_load($oid, $nid) { + $query = db_select('uc_product_options', 'po'); + $query->fields('po', array('nid', 'oid', 'cost', 'price', 'weight', 'ordering')) + ->condition('po.oid', $oid) + ->condition("po.nid", $nid) + ->orderBy('po.ordering'); + $result = $query->execute()->fetchObject(); + // @todo How do we get the aid? Go through all attributes and find the option? + // $config = config('uc_attribute.attributes'); + // $global_attributes = $config->get("attributes"); + // @todo add name and sort. + // backdrop_sort($result, array('ordering' => SORT_NUMERIC, 'name' => SORT_STRING)); + return $result; } /** @@ -980,14 +1412,59 @@ function uc_attribute_subject_option_load($oid, $type, $id) { * The integer result from backdrop_write_record(). */ function uc_attribute_subject_option_save(&$option, $type, $id) { - $sql = uc_attribute_type_info($type); + if ($type == 'class') { + $result = uc_attribute_class_option_save($option, $id); + } + elseif ($type == 'product') { + $result = uc_attribute_product_option_save($option, $id); + } + + return $result; +} +/** + * Saves a class attribute option. + * + * @param &$option + * The class attribute option. + * @param int $pcid + * The class ID. + * + * @return int + * SAVED_INSERT or SAVED_UPDATE + */ +function uc_attribute_class_option_save(&$option, $pcid) { // Insert or update? - $key = uc_attribute_subject_option_exists($option->oid, $type, $id) ? array('oid', $sql['id']) : array(); + $exists = uc_attribute_class_option_exists($option->oid, $pcid); + + // Merge in the class attribute option's ID, and save. + $option->pcid = $pcid; + $config = config('uc_attribute.attributes'); + // @todo make sure $option->aid exists. + $config->set("classes.{$pcid}.{$option->aid}.options.{$option->oid}", $option); + $config->save(); + + return $exists ? SAVED_UPDATED : SAVED_NEW; +} + +/** + * Saves a product attribute option. + * + * @param &$option + * The product attribute option. + * @param int $nid + * The product ID. + * + * @return int + * The integer result from backdrop_write_record(). + */ +function uc_attribute_product_option_save(&$option, $nid) { + // Insert or update? + $key = uc_attribute_product_option_exists($option->oid, $nid) ? array('oid', 'nid') : array(); // Merge in the product/class attribute option's ID, and save. - $option->{$type == 'product' ? 'nid' : 'pcid'} = $id; - $result = backdrop_write_record($sql['opt_table'], $option, $key); + $option->product = $nid; + $result = backdrop_write_record('uc_product_options', $option, $key); return $result; } @@ -1006,31 +1483,105 @@ function uc_attribute_subject_option_save(&$option, $type, $id) { * The Backdrop SAVED_DELETED flag. */ function uc_attribute_subject_option_delete($oid, $type, $id = NULL, $adjustments = TRUE) { - $sql = uc_attribute_type_info($type); + if ($type == 'class') { + uc_attribute_class_option_delete($oid, $id); + } + elseif ($type == 'product') { + uc_attribute_product_option_delete($oid, $id); + } + + return SAVED_DELETED; +} + +/** + * Deletes a class attribute option. + * + * @param $oid + * The base attribute's option ID. + * @param int $pcid + * The class ID. + * + * @return int + * The Backdrop SAVED_DELETED flag. + */ +function uc_attribute_class_option_delete($oid, $pcid = NULL) { + $config = config('uc_attribute.attributes'); + if ($pcid) { + $class_attributes = $config->get("classes.{$pcid}"); + foreach ($class_attributes['options'] as $oid => $option) { + $config->clear("classes.{$pcid}.{$class_attributes['aid']}.options.{$oid}"); + } + } + else { + $classes = $config->get('classes'); + foreach ($classes as $class_name => $class_info) { + foreach ($class_info as $aid => $attribute_info) { + foreach ($attribute_info['options'] as $oid => $option) { + $config->clear("classes.{$class_name}.{$aid}.options.{$oid}"); + } + } + } + } + $config->save(); + return SAVED_DELETED; +} + +/** + * Deletes a product attribute option. + * + * @param $oid + * The base attribute's option ID. + * @param int $id + * The product ID. + * + * @return int + * The Backdrop SAVED_DELETED flag. + */ +function uc_attribute_product_option_delete($oid, $nid = NULL, $adjustments = TRUE) { // Delete the option. - $query = db_delete($sql['opt_table']) + $query = db_delete('uc_product_options') ->condition('oid', $oid); // Base conditions, and an ID check if necessary. - if ($id) { - $query->condition($sql['id'], $id); + if ($nid) { + $query->condition('nid', $nid); } $query->execute(); // If this is a product, clean up the associated adjustments. - if ($adjustments && $type == 'product') { + if ($adjustments) { uc_attribute_adjustments_delete(array( 'aid' => uc_attribute_option_load($oid)->aid, 'oid' => $oid, - 'nid' => $id, + 'nid' => $nid, )); } return SAVED_DELETED; } +/** + * Deletes a global attribute option. + * + * @param $oid + * The base attribute's option ID. + * + * @return int + * The Backdrop SAVED_DELETED flag. + */ +function uc_attribute_global_option_delete($oid) { + $config = config('uc_attribute.attributes'); + $attributes = $config->get('attributes'); + foreach ($attributes as $aid => $attribute_info) { + foreach ($attribute_info['options'] as $oid => $option) { + $config->clear("attributes.{$aid}.options.{$oid}"); + } + } + $config->save(); +} + /** * Deletes an attribute adjustment. * @@ -1086,11 +1637,49 @@ function uc_attribute_adjustments_delete(array $fields) { * TRUE if the attribute exists. */ function uc_attribute_subject_exists($aid, $type, $id) { - $sql = uc_attribute_type_info($type); - $query = db_select($sql['attr_table'], 'a') + if ($type == 'class') { + $exists = uc_attribute_class_exists($aid, $id); + } + elseif ($type == 'product') { + $exists = uc_attribute_product_exists($aid, $id); + } + + return $exists; +} + +/** + * Checks if a class attribute exists. + * + * @param int $aid + * The base attribute ID. + * @param int $pcid + * The class attribute's ID. + * + * @return bool + * TRUE if the attribute exists. + */ +function uc_attribute_class_exists($aid, $pcid) { + $config = config('uc_attribute.attributes'); + $class_attribute = $config->get("classes.{$pcid}.{$aid}"); + return !empty($class_attribute); +} + +/** + * Checks if a product/class attribute exists. + * + * @param int $aid + * The base attribute ID. + * @param int $nid + * The product attribute's ID. + * + * @return bool + * TRUE if the attribute exists. + */ +function uc_attribute_product_exists($aid, $nid) { + $query = db_select('uc_product_attributes', 'a') ->fields('a', array('aid')) ->condition('aid', $aid) - ->condition($sql['id'], $id); + ->condition('nid', $nid); return FALSE !== $query->execute()->fetchField(); } @@ -1108,44 +1697,56 @@ function uc_attribute_subject_exists($aid, $type, $id) { * TRUE if the attribute option exists. */ function uc_attribute_subject_option_exists($oid, $type, $id) { - $sql = uc_attribute_type_info($type); - $query = db_select($sql['opt_table'], 'o') - ->fields('o', array('oid')) - ->condition('oid', $oid) - ->condition($sql['id'], $id); - return FALSE !== $query->execute()->fetchField(); + if ($type == 'class') { + $exists = uc_attribute_class_exists($oid, $id); + } + elseif ($type == 'product') { + $exists = uc_attribute_product_exists($oid, $id); + } + + return $exists; } /** - * Returns a list of names to abstract queries between products and classes. + * Checks if a class attribute option exists. * - * @param string $type - * Is this a product or a class? + * @param int $oid + * The base attribute option ID. + * @param int $pcid + * The class attribute option's ID. * - * @return array - * Array of information helpful for creating SQL queries dealing - * with attributes. + * @return bool + * TRUE if the attribute option exists. */ -function uc_attribute_type_info($type) { - switch ($type) { - case 'product': - return array( - 'attr_table' => 'uc_product_attributes', - 'opt_table' => 'uc_product_options', - 'id' => 'nid', - ); - - break; - - case 'class': - return array( - 'attr_table' => 'uc_class_attributes', - 'opt_table' => 'uc_class_attribute_options', - 'id' => 'pcid', - ); - - break; +function uc_attribute_class_option_exists($oid, $pcid) { + $config = config('uc_attribute.attributes'); + $class_attributes = $config->get("classes.{$pcid}"); + $exists = FALSE; + foreach ($class_attributes as $aid => $class_attribute) { + if (!empty($class_attribute['options'][$oid])) { + $exists = TRUE; + continue; + } } + return $exists; +} + +/** + * Checks if a product attribute option exists. + * + * @param int $oid + * The base attribute option ID. + * @param int $nid + * The product attribute option's ID. + * @return bool + * TRUE if the attribute option exists. + */ +function uc_attribute_product_option_exists($oid, $nid) { + $query = db_select('uc_product_options', 'o') + ->fields('o', array('oid')) + ->condition('oid', $oid) + ->condition('nid', $nid); + return FALSE !== $query->execute()->fetchField(); } /** @@ -1160,11 +1761,15 @@ function uc_attribute_type_info($type) { function uc_product_get_attributes($nid) { $attributes = array(); - $result = db_query("SELECT upa.aid FROM {uc_product_attributes} upa LEFT JOIN {uc_attributes} ua ON upa.aid = ua.aid WHERE upa.nid = :nid ORDER BY upa.ordering, ua.name", array(':nid' => $nid)); + $result = db_query("SELECT upa.aid FROM {uc_product_attributes} upa WHERE upa.nid = :nid ORDER BY upa.ordering", array(':nid' => $nid)); + foreach ($result as $attribute) { - $attributes[$attribute->aid] = uc_attribute_load($attribute->aid, $nid, 'product'); + $attributes[$attribute->aid] = uc_attribute_product_load($attribute->aid, $nid); } + // @todo order by ordering, name. + // backdrop_sort($attributes, array('ordering' => SORT_NUMERIC, 'name' => SORT_STRING)); + return $attributes; } @@ -1175,19 +1780,86 @@ function uc_product_get_attributes($nid) { * The product class id. * * @return array - * The attributes. + * An aarray of attribute objects. */ function uc_class_get_attributes($pcid) { $attributes = array(); - $result = db_query("SELECT uca.aid FROM {uc_class_attributes} uca LEFT JOIN {uc_attributes} ua ON uca.aid = ua.aid WHERE uca.pcid = :type ORDER BY uca.ordering, ua.name", array(':type' => $pcid)); - foreach ($result as $attribute) { - $attributes[$attribute->aid] = uc_attribute_load($attribute->aid, $pcid, 'class'); + $config = config('uc_attribute.attributes'); + $class_attributes = $config->get("classes.{$pcid}"); + foreach ($class_attributes as $aid => $class_attribute) { + $global_attribute = $config->get("attributes.{$aid}"); + $class_attribute['name'] = $global_attribute['name']; + $attributes[$aid] = (object) $class_attribute; } + // @todo order by ordering, name. + backdrop_sort($result, array('ordering' => SORT_NUMERIC, 'name' => SORT_STRING)); + return $attributes; } +/** + * Loads all attributes associated with a product class. + * + * @param int $pcid + * The product class id. + * + * @return array + * Array of attributes objects. + */ +function uc_class_get_attributes_config($pcid) { + $attributes = array(); + + $config = config('uc_attribute.attributes'); + $class_attributes = $config->get("classes.{$pcid}"); + if ($class_attributes) { + $attributes = $class_attributes; + foreach ($class_attributes as $aid => $class_attribute) { + $attribute_global = $config->get("attributes.{$aid}"); + $class_attribute['name'] = $attribute_global['name']; + $oid = 0; + if (isset($attribute_global['options'])) { + if ($option = reset($attribute_global['options'])) { + $oid = $option['oid']; + } + } + $class_attribute['default_option'] = $oid; + $attributes[$aid] = (object) $class_attribute; + } + } + + return $attributes; +} + +/** + * Loads all attributes associated with a product class. + * + * @param int $pcid + * The product class id. + * + * @return array + * The attributes. + */ +function uc_class_get_attributes_options_config($pcid) { + $class_attributes = array(); + + $config = config('uc_attribute.attributes'); + // $attributes = $config->get("attributes"); + $class_attributes = $config->get("classes.{$pcid}"); + foreach ($class_attributes as $id => $attribute) { + if (!empty($attribute['default_option'])) { + $default_option = $attribute['options'][$attribute['default_option']]; + // $attributes[$id]['default_label'] = $default_option['name']; + // $attributes[$id]['default_ordering'] = $default_option['ordering']; + // $attributes[$id]['default_required'] = $default_option['required']; + // $attributes[$id]['default_display'] = $default_option['display']; + } + } + + return $class_attributes; +} + /** * Gets the options chosen for a product that is in the cart. * @@ -1477,7 +2149,17 @@ function _uc_attribute_display_types() { * Array of attribute ids that have price affecting options. */ function uc_attribute_priced_attributes($nid) { - $aids = db_query("SELECT DISTINCT (pa.aid) FROM {uc_product_attributes} pa INNER JOIN {uc_attribute_options} ao ON ao.aid = pa.aid INNER JOIN {uc_product_options} po ON (po.oid = ao.oid AND po.nid = pa.nid) WHERE pa.nid = :nid AND po.price <> :price AND pa.display <> :display", array(':nid' => $nid, ':price' => 0, ':display' => 0))->fetchCol(); + $aids = array(); + $oids = db_query("SELECT DISTINCT (po.oid) FROM {uc_product_options} po WHERE pa.nid = :nid AND po.price <> :price", array(':nid' => $nid, ':price' => 0))->fetchCol(); + $config = config('uc_attribute.attributes', 'attributes'); + $attributes = $config->get('attributes'); + foreach ($oids as $oid) { + foreach ($attributes as $attribute) { + if (in_array($oid, $attribute['options']) && $attribute['display'] != 0) { + $aids[] = $attribute['aid']; + } + } + } return $aids; } @@ -1530,15 +2212,14 @@ function uc_attribute_i18n_string_info() { * Refreshes translated attribute and option strings. */ function uc_attribute_i18n_string_refresh() { - $attributes = db_query("SELECT aid, name, label, description FROM {uc_attributes}"); + $attributes = config_get('uc_attribute.attributes', 'attributes'); foreach ($attributes as $attribute) { - i18n_string_update('uc_attribute:attribute:' . $attribute->aid . ':name', $attribute->name); - i18n_string_update('uc_attribute:attribute:' . $attribute->aid . ':label', $attribute->label); - i18n_string_update('uc_attribute:attribute:' . $attribute->aid . ':description', $attribute->description); + i18n_string_update('uc_attribute:attribute:' . $attribute['aid'] . ':name', $attribute['name']); + i18n_string_update('uc_attribute:attribute:' . $attribute['aid'] . ':label', $attribute['label']); + i18n_string_update('uc_attribute:attribute:' . $attribute['aid'] . ':description', $attribute['description']); - $options = db_query("SELECT oid, name FROM {uc_attribute_options} WHERE aid = :aid", array(':aid' => $attribute->aid)); - foreach ($options as $option) { - i18n_string_update('uc_attribute:option:' . $option->oid . ':name', $option->name); + foreach ($attributes['options'] as $option) { + i18n_string_update('uc_attribute:option:' . $option['oid'] . ':name', $option['name']); } } @@ -1562,10 +2243,3 @@ function uc_attribute_translate(&$attribute) { } } } - -/** - * Implements hook_autoload_info(). - */ -function uc_attribute_autoload_info() { - return array(); -} diff --git a/uc_attribute/uc_attribute.rules.inc b/uc_attribute/uc_attribute.rules.inc index 61eac63a..c49fb83f 100644 --- a/uc_attribute/uc_attribute.rules.inc +++ b/uc_attribute/uc_attribute.rules.inc @@ -69,9 +69,16 @@ function uc_attribute_condition_ordered_product_option($order, $oid) { */ function uc_attribute_condition_ordered_product_options_list() { $options = array(); - $result = db_query("SELECT a.aid, a.name AS attr_name, a.ordering, o.oid, o.name AS opt_name, o.ordering FROM {uc_attributes} a JOIN {uc_attribute_options} o ON a.aid = o.aid ORDER BY a.ordering, o.ordering"); - foreach ($result as $option) { - $options[$option->attr_name][$option->oid] = $option->opt_name; + $attributes_from_config = config_get('uc_attribute.attributes', "attributes"); + $all_options = array(); + foreach ($attributes_from_config as $attribute) { + $attribute['options']['attr_name'] = $attribute['name']; + $attribute['options']['attr_ordering'] = $attribute['ordering']; + $all_options += $attribute['options']; + } + backdrop_sort($all_options, array('attr_ordering' => SORT_NUMERIC, 'ordering' => SORT_NUMERIC)); + foreach ($all_options as $option) { + $options[$attribute['name']][$option['oid']] = $option['name']; } return $options; diff --git a/uc_order/uc_order.rules.inc b/uc_order/uc_order.rules.inc index b78b56c3..f9984108 100644 --- a/uc_order/uc_order.rules.inc +++ b/uc_order/uc_order.rules.inc @@ -535,9 +535,9 @@ function uc_order_condition_has_product_class($order, $product_classes, $require function uc_order_condition_has_product_class_classes_options() { $options = array(); - $result = db_query('SELECT * FROM {uc_product_classes}'); - foreach ($result as $class) { - $options += array($class->pcid => $class->name); + $classes = uc_product_class_load(); + foreach ($classes as $class_id => $class) { + $options += array($class_id => $class['name']); } return $options; diff --git a/uc_product/config/uc_product.settings.json b/uc_product/config/uc_product.settings.json index 03f4baef..bf66d74a 100644 --- a/uc_product/config/uc_product.settings.json +++ b/uc_product/config/uc_product.settings.json @@ -3,5 +3,6 @@ "uc_product_image_widget": "", "uc_product_add_to_cart_qty": false, "uc_product_update_node_view": false, - "uc_product_shippable_product": true + "uc_product_shippable_product": true, + "classes": [] } diff --git a/uc_product/uc_product.admin.inc b/uc_product/uc_product.admin.inc index a2a21e48..5007e1e1 100644 --- a/uc_product/uc_product.admin.inc +++ b/uc_product/uc_product.admin.inc @@ -427,7 +427,7 @@ function uc_product_class_form($form, &$form_state, $class = NULL) { '#title' => t('Class ID'), '#required' => TRUE, '#maxlength' => 32, - '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the create content page for this content type. This name may consist only of lowercase letters, numbers, and underscores. Dashes are not allowed. Underscores will be converted into dashes when constructing the URL of the create content page. This name must be unique to this content type.'), + '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the create content page for this content type. This name may consist only of lowercase letters, numbers, and underscores. Dashes are not allowed. Underscores will be converted into dashes when constructing the URL of the create content page. This name must be unique to this content type. Existing !content_types can be added here and made into a product class.', array('!content_types' => l(t('content types'), 'admin/structure/types'))), ); } @@ -487,41 +487,12 @@ function uc_product_class_form_submit($form, &$form_state) { // Convert whitespace to underscores, and remove other non-alphanumeric characters. $pcid = preg_replace(array('/\s+/', '/\W/'), array('_', ''), strtolower($pcid)); - $result = db_merge('uc_product_classes') - ->key(array('pcid' => $pcid)) - ->fields(array( - 'name' => $form_state['values']['name'], - 'description' => $form_state['values']['description'], - )) - ->execute(); - - $type = new \stdClass(); - $type->type = $pcid; - $type->name = $form_state['values']['name']; - $type->base = 'uc_product'; - $type->module = 'uc_product'; - $type->description = $form_state['values']['description']; - $type = node_type_set_defaults($type); - node_type_save($type); - - uc_product_node_info(TRUE); - - if ($result == MergeQuery::STATUS_INSERT) { - module_invoke_all('uc_product_class', $pcid, 'insert'); - } - else { - module_invoke_all('uc_product_class', $pcid, 'update'); - } - - node_type_cache_reset(); - if ($is_new) { - $type = node_type_get_type($pcid); - node_add_body_field($type, t('Description')); - uc_product_add_default_image_field($pcid); - } - menu_rebuild(); - - backdrop_set_message(t('Product class saved.')); + $class = array( + 'pcid' => $pcid, + 'name' => $form_state['values']['name'], + 'description' => $form_state['values']['description'] + ); + uc_product_class_create($class, $is_new); } /** @@ -554,15 +525,20 @@ function uc_product_class_delete_confirm($form, &$form_state, $class) { * @see uc_product_class_delete_confirm() */ function uc_product_class_delete_confirm_submit($form, &$form_state) { + $config = config('uc_product.settings'); + $classes = $config->get('classes'); + if (($key = array_search($form_state['values']['pcid'], $classes)) !== false) { + unset($classes[$key]); + } + $config->set('classes', array_values(array_filter($classes))); + $config->save(); + $type = node_type_get_type($form_state['values']['pcid']); $type->base = 'node_content'; $type->module = 'node'; $type->custom = 1; node_type_save($type); - db_delete('uc_product_classes') - ->condition('pcid', $form_state['values']['pcid']) - ->execute(); module_invoke_all('uc_product_class', $form_state['values']['pcid'], 'delete'); // TODO migrate This needed? uc_product_node_info(TRUE); diff --git a/uc_product/uc_product.api.php b/uc_product/uc_product.api.php index a7651d2d..39e8b047 100644 --- a/uc_product/uc_product.api.php +++ b/uc_product/uc_product.api.php @@ -27,7 +27,7 @@ function hook_uc_product_alter(&$node) { $combination = array(); foreach ($node->data['attributes'] as $aid => $value) { if (is_numeric($value)) { - $attribute = uc_attribute_load($aid, $node->nid, 'product'); + $attribute = uc_attribute_product_load($aid, $node->nid); if ($attribute && ($attribute->display == 1 || $attribute->display == 2)) { $combination[$aid] = $value; } diff --git a/uc_product/uc_product.install b/uc_product/uc_product.install index 6e782e37..b00062e7 100644 --- a/uc_product/uc_product.install +++ b/uc_product/uc_product.install @@ -10,31 +10,6 @@ function uc_product_schema() { $schema = array(); - $schema['uc_product_classes'] = array( - 'description' => 'The list of product node types.', - 'fields' => array( - 'pcid' => array( - 'description' => 'The node type identifier.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The human-readable name.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'description' => array( - 'description' => 'The description of the node type.', - 'type' => 'text', - ), - ), - 'primary key' => array('pcid'), - ); - $schema['uc_product_features'] = array( 'description' => 'Stores information of features added to products.', 'fields' => array( @@ -264,6 +239,37 @@ function uc_product_set_teaser_display($type) { field_bundle_settings('node', $type, $settings); } +/** + * Implements hook_install(). + */ +function uc_product_install() { + _uc_product_type_create(); +} + +/** + * Creates the product type. + */ +function _uc_product_type_create() { + // Create an additional node type. + $product_node_type = array( + 'type' => 'product', + 'name' => t('Product'), + 'base' => 'uc_product', + 'description' => t('Use products to represent items for sale on the website, including all the unique information that can be attributed to a specific model number.'), + 'custom' => 1, + 'modified' => 1, + 'locked' => 0, + 'is_new' => TRUE, + 'settings' => array( + 'promote_enabled' => FALSE, + ), + ); + + $product_node_type = node_type_set_defaults($product_node_type); + node_type_save($product_node_type); + node_add_body_field($product_node_type); +} + /** * Implements hook_update_last_removed(). */ @@ -455,32 +461,23 @@ function uc_product_update_1006() { } /** - * Implements hook_install(). + * Convert product classes to CMI. */ -function uc_product_install() { - _uc_product_type_create(); -} +function uc_product_update_1007() { + if (!db_table_exists('uc_product_classes')) { + return; + } + $classes_db = db_query("SELECT * FROM {uc_product_classes}"); -/** - * Creates the product type. - */ -function _uc_product_type_create() { - // Create an additional node type. - $product_node_type = array( - 'type' => 'product', - 'name' => t('Product'), - 'base' => 'uc_product', - 'description' => t('Use products to represent items for sale on the website, including all the unique information that can be attributed to a specific model number.'), - 'custom' => 1, - 'modified' => 1, - 'locked' => 0, - 'is_new' => TRUE, - 'settings' => array( - 'promote_enabled' => FALSE, - ), - ); + $classes = array(); + foreach ($classes_db as $class) { + $classes[] = $class->pcid; + } + if (!empty($classes)) { + $config = config('uc_product.settings'); + $config->set('classes', $classes); + $config->save(); + } - $product_node_type = node_type_set_defaults($product_node_type); - node_type_save($product_node_type); - node_add_body_field($product_node_type); + db_drop_table('uc_product_classes'); } diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module index 94fa37f8..386cf1d7 100644 --- a/uc_product/uc_product.module +++ b/uc_product/uc_product.module @@ -262,28 +262,6 @@ function uc_product_node_info($reset = FALSE) { return $types; } -/** - * Implements hook_node_type_update(). - * - * Ensure product class names and descriptions are synchronised if the - * content type is updated. - */ -function uc_product_node_type_update($info) { - $existing_type = !empty($info->old_type) ? $info->old_type : $info->type; - - db_update('uc_product_classes') - ->fields(array( - 'pcid' => $info->type, - 'name' => $info->name, - 'description' => $info->description, - )) - ->condition('pcid', $existing_type) - ->execute(); - - // Reset static variable data and load new info. - uc_product_node_info(TRUE); -} - /** * Implements hook_forms(). * @@ -1586,16 +1564,33 @@ function uc_product_get_description($product) { /** * Load a product class or all classes. + * + * @param string|null $class_id + * @param bool $reset + * + * @return array + * Array of classes. */ function uc_product_class_load($class_id = NULL, $reset = FALSE) { static $classes; if (!isset($classes) || $reset) { - // Load classes from database. + // Load classes from config. $classes = array(); - $result = db_query("SELECT * FROM {uc_product_classes}"); - while ($class = $result->fetchObject()) { - $class->locked = FALSE; - $classes[$class->pcid] = $class; + + $config = config('uc_product.settings'); + $classes_config = $config->get('classes'); + + if (!$classes_config) { + return $classes; + } + + foreach($classes_config as $pcid) { + $type = node_type_get_type($pcid); + $class['locked'] = FALSE; + $class['pcid'] = $pcid; + $class['name'] = $type->name; + $class['description'] = $type->description; + $classes[$pcid] = (object) $class; } // Merge any module-defined classes. @@ -1616,8 +1611,8 @@ function uc_product_class_load($class_id = NULL, $reset = FALSE) { $classes[$pcid] = (object) array_merge($class, (array) $classes[$pcid]); } else { - // Ensure the class info is saved in the database. - backdrop_write_record('uc_product_classes', $class); + // Ensure the class info is saved. + uc_product_class_sync($pcid); $classes[$pcid] = (object) $class; } @@ -1923,3 +1918,54 @@ function uc_product_autoload_info() { 'uc_product_handler_filter_product' => 'views/uc_product_handler_filter_product.inc', ); } + +/** + * Create product class + * + * @param array $class + * @param bool $is_new + */ +function uc_product_class_create($class, $is_new = FALSE) { + uc_product_class_sync($class['pcid']); + + $type = new \stdClass(); + $type->type = $class['pcid']; + $type->name = $class['name']; + $type->base = 'uc_product'; + $type->module = 'uc_product'; + $type->description = $class['description']; + $type = node_type_set_defaults($type); + node_type_save($type); + + uc_product_node_info(TRUE); + + if ($is_new) { + module_invoke_all('uc_product_class', $class['pcid'], 'insert'); + } + else { + module_invoke_all('uc_product_class', $class['pcid'], 'update'); + } + + node_type_cache_reset(); + if ($is_new) { + $type = node_type_get_type($class['pcid']); + node_add_body_field($type, t('Description')); + uc_product_add_default_image_field($class['pcid']); + } + menu_rebuild(); + + backdrop_set_message(t('Product class saved.')); +} + +/** + * Save product class + * + * @param array $class_id + */ +function uc_product_class_sync($class_id) { + $config = config('uc_product.settings'); + $classes = $config->get('classes'); + $classes[] = $class_id; + $config->set('classes', array_values(array_filter($classes))); + $config->save(); +} diff --git a/uc_store/tests/test_helper.inc b/uc_store/tests/test_helper.inc index bc060070..806431e3 100644 --- a/uc_store/tests/test_helper.inc +++ b/uc_store/tests/test_helper.inc @@ -109,9 +109,10 @@ class UbercartTestHelper extends BackdropWebTestCase { 'name' => $this->randomName(8), 'description' => $this->randomName(8), ); - $product_class = (object) $product_class; - backdrop_write_record('uc_product_classes', $product_class); + uc_product_class_create($product_class, TRUE); + + $product_class = (object) $product_class; return $product_class; }