Error in Programmatically creating Taxonomy in drupal - php

I'm getting the below error while building taxonomy voccabulary and terms using the following code
FieldException: Attempt to create an instance of a field field_my_custom_vocab002 that doesn't exist or is currently inactive. in field_create_instance() (line 476 of C:\wamp\www\pur_theme\modules\field\field.crud.inc).
I checked the voccabulary and it is created, the problem is only with term creation
code
<?php
$new_vocab = (object) array(
'name' => 'My custom vocabulary002',
'description' => 'Test',
'machine_name' => 'my_custom_vocab002',
);
taxonomy_vocabulary_save($new_vocab);
$vocab = taxonomy_vocabulary_machine_name_load('my_custom_vocab002');
$term1 = (object) array(
'name' => 'Term 1',
'description' => 'This is term 1',
'vid' => $vocab->vid,
);
taxonomy_term_save($term1);
What/where did I wrong?

I think it's because you try to create a term with more fields than the 2 basical ones (name and vid). You have a "description" field in addition.
Try this code instead:
$term = new stdClass();
$term->name = 'Term 1';
$term->vid = $vocab->vid;
$term->field_description[LANGUAGE_NONE][0]['value'] = 'This is term 1';
taxonomy_term_save($term);
Source: http://www.lightrains.com/blog/programmatically-create-taxonomy-term-drupal

I found the only solution to this was to create the field it's looking for before creating the vocabulary. Personally I did this with a custom function:
function MY_MODULE_create_new_taxonomy($taxonomy_name, $taxonomy_machine_name, $taxonomy_description){
//Add field you know is going to cause trouble
$field = array(
'field_name' => 'field_'.$taxonomy_machine_name,
'type' => 'text',
'label' => 'Label'
);
field_create_field($field);
//create the vocab
$new_vocabulary = new stdClass();
$new_vocabulary->name = $taxonomy_name;
$new_vocabulary->machine_name = $taxonomy_machine_name;
$new_vocabulary->description = t($taxonomy_description);
$new_vocabulary->module = 'taxonomy';
taxonomy_vocabulary_save($new_vocabulary);
}

Related

How would you PHPUnit test a Wordpress function containing get_post_meta()?

I've just start learning PHPUnit with Wordpress. I have a widget that gets post data from and metadata of it.
My Code Looks like the following:
if ($postMeta = get_post_meta($post->ID, 'feature_news_colours_overlay')) {
$colourOverlay = $postMeta['0'];
}
$items[] = [
'title' => $title,
'url' => get_permalink($post->ID),
'description' => $content,
'colourOverlay' => $colourOverlay,
];
The widget is getting data from a feature_news_colours_overlay custom_field for the post. In my PhpUnit test i can add the following to make it test the if conditions.
$post = new \stdClass();
$post->post_title = 'Some title text to test.';
$post->ID = 1;
$GLOBALS['post_meta'][0] = '#FFCC00';
This actually tests my Item array with an assert:
'title' => 'Some title text to test.',
'url' => 'http://something..',
'description' => 'test desc',
'colourOverlay' => '#FFoooo'
This actually testes my if condition, as i see this is not a standard way if i have to check more then 1 posts i cant actually test with a GLOBAL value.
$GLOBALS['post_meta'][0] = '#FFCC00';
Is there any way i could add a mock meta value to each post so as to check the if condition.?
If you put the post ID you're testing into $post_id, then
add_post_meta( $post_id, 'your_meta_key', 'your_test_value' );
in your testing code just before your assertions should do the trick.

Adding column to Wordpress Post (Specifically WP E-Commerce)

Right, I've got WordPress E-commerce installed on WordPress and I need to add additional columns to the post type.
I've done some investigating. It appears that E-commerce just submits a post type called "Products" and changes the columns in order to add things like Price etc.
I need to add another input. Just a little checkbox that the admin can set to true or false as they add a product. The only problem for me at the moment is finding where exactly to do this.
I think I've found the WordPress E-Commerce post type column settings, but obviously just adding an additional one isn't working.
/wp-content/plugins/wp-e-commerce/wpsc-admin/display-items.page.php
function wpsc_additional_column_names( $columns ){
$columns = array();
$columns['cb'] = '';
$columns['image'] = '';
$columns['title'] = __('Name', 'wpsc');
$columns['stock'] = __('Stock', 'wpsc');
$columns['price'] = __('Price', 'wpsc');
$columns['sale_price'] = __('Sale', 'wpsc');
$columns['SKU'] = __('SKU', 'wpsc');
$columns['weight'] = __('Weight', 'wpsc');
$columns['cats'] = __('Categories', 'wpsc');
$columns['featured'] = '';
$columns['hidden_alerts'] = '';
$columns['date'] = __('Date', 'wpsc');
return $columns;
}
Don't edit the core files. You can add custom metaboxes to WP e-Commerce's Products post type just as you would any other post type.
My preferred solution is to use Custom Metaboxes and Fields for WordPress
This sample function will output a checkbox on products using the above plugin (note 'pages' => array('wpsc-product'), this targets products only):
function base_meta_boxes_ba($meta_boxes) {
/**
* Page Options meta box
*/
$meta_boxes[] = array(
'id' => 'product_options',
'title' => 'Extra Product Options',
'pages' => array('wpsc-product'),
'context' => 'normal',
'priority' => 'high',
'show_names' => true,
'fields' => array(
array(
'name' => 'Test Checkbox',
'desc' => 'field description (optional)',
'id' => $prefix . 'test_checkbox',
'type' => 'checkbox'
),
)
);
return $meta_boxes;
}
add_filter('cmb_meta_boxes', 'base_meta_boxes_ba');

Avoid duplicates by associating to inserted records with CakePHP saveMany

I am trying to take advantage of CakePHP's saveMany feature (with associated data feature), however am creating duplicate records. I think it is because the find() query is not finding authors, as the transaction has not yet been committed to the database.
This means that if there are two authors with the same username, for example, in the spreadsheet, then CakePHP will not associate the second with the first, but rather create two. I have made up some code for this post:
/*
* Foobar user (not in database) entered twice, whereas Existing user
* (in database) is associated
*/
$spreadsheet_rows = array(
array(
'title' => 'New post',
'author_username' => 'foobar',
'content' => 'New post'
),
array(
'title' => 'Another new post',
'author_username' => 'foobar',
'content' => 'Another new post'
),
array(
'title' => 'Third post',
'author_username' => 'Existing user',
'content' => 'Third post'
),
array(
'title' => 'Fourth post', // author_id in this case would be NULL
'content' => 'Third post'
),
);
$posts = array();
foreach ($spreadsheet_rows as $row) {
/*
* This query doesn't pick up the authors
* entered automatically (see comment 2.)
* within the db transaction by CakePHP,
* so creates duplicate author names
*/
$author = $this->Author->find('first', array('conditions' => array('Author.username' => $row['author_username'])));
$post = array(
'title' => $row['title'],
'content' => $row['content'],
);
/*
* Associate post to existing author
*/
if (!empty($author)) {
$post['author_id'] = $author['Author']['id'];
} else {
/*
* 2. CakePHP creates and automatically
* associates new author record if author_username is not blank
* (author_id is NULL in db if blank)
*/
if (!empty($ow['author_username'])) {
$post['Author']['username'] = $row['author_username'];
}
}
$posts[] = $post;
}
$this->Post->saveMany($posts, array('deep' => true));
Is there any way that this can be achieved, while also keeping transactions?
Update
You new requirement to save also posts that have no associated authors changes the situation a lot, as mentioned in the comments, CakePHPs model save methods are not ment to be able to save data from different models at once if it's not an association, if you need to do this in a transaction, then you'll need to handle this manually.
Save authors and their posts instead of posts and their authors
I would suggest that you save the data the other way around, that is save authors and their associated posts, that way you can easily take care of the duplicate users by simply grouping their data by using the username.
That way around CakePHP will create new authors only when neccessary, and add the appropriate foreign keys to the posts automatically.
The data should then be formatted like this:
Array
(
[0] => Array
(
[username] => foobar
[Post] => Array
(
[0] => Array
(
[title] => New post
)
[1] => Array
(
[title] => Another new post
)
)
)
[1] => Array
(
[id] => 1
[Post] => Array
(
[0] => Array
(
[title] => Third post
)
)
)
)
And you would save via the Author model:
$this->Author->saveMany($data, array('deep' => true));
Store non associated posts separately and make use of transactions manually
There is no way around this if you want to use the CakePHP ORM, just imagine what the raw SQL query would need to look like if it would need to handle all that logic.
So just split this into two saves, and use DboSource::begin()/commit()/rollback() manually to wrap it all up.
An example
Here's a simple example based on your data, updated for your new requirements:
$spreadsheet_rows = array(
array(
'title' => 'New post',
'author_username' => 'foobar',
'content' => 'New post'
),
array(
'title' => 'Another new post',
'author_username' => 'foobar',
'content' => 'Another new post'
),
array(
'title' => 'Third post',
'author_username' => 'Existing user',
'content' => 'Third post'
),
array(
'title' => 'Fourth post',
'content' => 'Fourth post'
),
array(
'title' => 'Fifth post',
'content' => 'Fifth post'
),
);
$authors = array();
$posts = array();
foreach ($spreadsheet_rows as $row) {
// store non-author associated posts separately
if (!isset($row['author_username'])) {
$posts[] = $row;
} else {
$username = $row['author_username'];
// prepare an author only once per username
if (!isset($authors[$username])) {
$author = $this->Author->find('first', array(
'conditions' => array(
'Author.username' => $row['author_username']
)
));
// if the author already exists use its id, otherwise
// use the username so that a new author is being created
if (!empty($author)) {
$authors[$username] = array(
'id' => $author['Author']['id']
);
} else {
$authors[$username] = array(
'username' => $username
);
}
$authors[$username]['Post'] = array();
}
// group posts under their respective authors
$authors[$username]['Post'][] = array(
'title' => $row['title'],
'content' => $row['content'],
);
}
}
// convert the string (username) indices into numeric ones
$authors = Hash::extract($authors, '{s}');
// manually wrap both saves in a transaction.
//
// might require additional table locking as
// CakePHP issues SELECT queries in between.
//
// also this example requires both tables to use
// the default connection
$ds = ConnectionManager::getDataSource('default');
$ds->begin();
try {
$result =
$this->Author->saveMany($authors, array('deep' => true)) &&
$this->Post->saveMany($posts);
if ($result && $ds->commit() !== false) {
// success, yay
} else {
// failure, buhu
$ds->rollback();
}
} catch(Exception $e) {
// failed hard, ouch
$ds->rollback();
throw $e;
}
You need to use saveAll, which is a mix between saveMany and saveAssociated (you will need to do both of them here).
Plus, you need to change the structure of each post.
Here is an example of the structures you will need to create inside the loop.
<?php
$posts = array();
//This is a post for a row with a new author
$post = array (
'Post' => array ('title' => 'My Title', 'content' => 'This is the content'),
'Author' => array ('username' => 'new_author')
);
$posts[] = $post;
//This is a post for a row with an existing author
$post = array (
'Post' => array ('title' => 'My Second Title', 'content' => 'This is another content'),
'Author' => array ('id' => 1)
);
$posts[] = $post;
//This is a post for a row with no author
$post = array (
'Post' => array ('title' => 'My Third Title', 'content' => 'This is one more content')
);
$posts[] = $post;
$this->Post->saveAll($posts, array ('deep' => true));
?>
Following the "use transactions manually" bit suggested by ndm, this piece of code (written in a unit test!) seemed to do the trick:
public function testAdd() {
$this->generate('Articles', array());
$this->controller->loadModel('Article');
$this->controller->loadModel('Author');
$csv_data = array(
array(
'Article' => array(
'title' => 'title'
)),
array(
'Article' => array(
'title' => 'title'
),
'Author' => array(
'name' => 'foobar'
),
),
array(
'Article' => array(
'title' => 'title2'
),
'Author' => array(
'name' => 'foobar'
)
),
/* array( */
/* 'Article' => array( */
/* 'title' => '' */
/* ), */
/* 'Author' => array( */
/* 'name' => '' // this breaks our validation */
/* ) */
/* ), */
);
$db = $this->controller->Article->getDataSource();
$db->begin();
/*
* We want to inform the user of _all_ validation messages, not one at a time
*/
$validation_errors = array();
/*
* Do this by row count, so that user can look through their CSV file
*/
$row_count = 1;
foreach ($csv_data as &$row) {
/*
* If author already exists, don't create new record, but associate to existing
*/
if (!empty($row['Author'])) {
$author = $this->controller->Author->find('first',
array(
'conditions' => array(
'name' => $row['Author']['name']
)
));
if (!empty($author)) {
$row['Author']['id'] = $author['Author']['id'];
}
}
$this->controller->Article->saveAssociated($row, array('validate' => true));
if (!empty($this->controller->Article->validationErrors)) {
$validation_errors[$row_count] = $this->controller->Article->validationErrors;
}
$row_count++;
}
if (empty($validation_errors)) {
$db->commit();
} else {
$db->rollback();
debug($validation_errors);
}
debug($this->controller->Article->find('all'));
}

get configuration scope selector for custom module

I'm working on a module where I need to save the records based on the scope of website and store. For that I need to have the select box as its in the system->configuration.
How can I get that select box in my form and save the website/store value in database so that it can be displayed in the specific store/website? Any suggestions on it?
Now I'm trying it breaking into two fields -> website and store. Now, how can I change the options in store based on the website selected?
Finally, I'd written my own code for this.
$scope = array('default' => 'default');
foreach (Mage::app()->getWebsites() as $website) {
$scope['website_' . $website->getCode()] = $website->getName();
foreach ($website->getGroups() as $group) {
$stores = array();
foreach ($group->getStores() as $store) {
$stores[] = array(
'label' => $store->getName(),
'value' => 'store_' . $store->getCode()
);
}
$scope[] = array(
'label' => $website->getName(),
'value' => $stores
);
}
}
$fieldset->addField('website', 'select', array(
'label' => Mage::helper('designer')->__('Website'),
'name' => 'website',
'values' => $scope
));
Thanks to this post by Marius

Bigcommerce API PHP

I want to add products in bigcommerce store using bigcommerce Api PHP.
My code is like below:
$fields = array( "name" => "Apple" );
BigCommerce_Api::createBrand($fields);
store is connected successfully but products are not creating.Please help...
It looks like you are creating a brand and not a product. If you want to create a product, you can do something like below -
$filter = array('name' => 'iPhone sticker', 'price' => '12.99', 'categories' => array(2), 'type' => 'physical', 'availability' => 'available', 'weight' => 0);
$product = Bigcommerce_Api::createProduct($filter);
$new_product = new Bigcommerce_Api_Product();
$new_product->name = $_POST['name'];
$new_product->type= "type";
$product = $new_product->create();
You can create brand by using xml,please check below code :
BigCommerce_Api::useXml();
$xml = '<brand>
<name>'.$product[brand_name].'</name>
</brand>';
$brand_exe = BigCommerce_Api::createBrand($xml);

Categories