the following code is in the Wooimporter.php page (a plugin to import products into woocommerce), the script reads the data taken from a web page and imports them as products, I have to make sure that when importing it reads an attribute of name 'red oranges' must replace it and insert it in the database with name 'reds', the code is as follows:
how to edit or add php code? thank you
public static function addAttributes(array $features, $product_id)
{
if (!$product = \wc_get_product($product_id))
return false;
$attributes = $product->get_attributes();
$taxonomy_count = 0;
foreach ($features as $feature)
{
$f_name = \wc_clean($feature['name']);
$f_slug = self::getSlug($f_name);
// exists?
if (isset($attributes[$f_slug]) || isset($attributes['pa_' . $f_slug]))
continue;
$f_value = \wc_sanitize_term_text_based($feature['value']);
$f_value_array = self::value2Array($f_value);
$term_ids = array();
$taxonomy = '';
// Taxonomy exists?
if (\taxonomy_exists(\wc_attribute_taxonomy_name($f_name)))
{
// Taxonomy attribute
$taxonomy_count++;
if ($attr_id = self::createTaxonomyAttribute($f_name, $f_slug))
{
$taxonomy = \wc_attribute_taxonomy_name_by_id($attr_id);
// Register the taxonomy now so that the import works!
if (!\taxonomy_exists($taxonomy))
{
$taxonomy = TextHelper::truncate($taxonomy, 32, '');
\register_taxonomy($taxonomy, apply_filters('woocommerce_taxonomy_objects_' . $taxonomy, array('product')), apply_filters('woocommerce_taxonomy_args_' . $taxonomy, array('hierarchical' => true, 'show_ui' => false, 'query_var' => true, 'rewrite' => false)));
}
// Creates the term and taxonomy relationship if it doesn't already exist.
// It may be confusing but the returned array consists of term_taxonomy_ids instead of term_ids.
\wp_set_object_terms($product->get_id(), $f_value_array, $taxonomy);
$term_ids = array();
foreach ($f_value_array as $term)
{
if ($term_info = \term_exists($term, $taxonomy))
$term_ids[] = $term_info['term_id'];
}
$term_ids = array_map('intval', $term_ids);
} else
$attr_id = 0;
} else
{
// Local Attribute
$attr_id = 0;
}
$attribute = new \WC_Product_Attribute();
$attribute->set_id($attr_id); // 0 for product level attributes. ID for global attributes.
if ($taxonomy)
$attribute->set_name($taxonomy);
else
$attribute->set_name($f_name);
// attribute value or array of term ids/names.
if ($term_ids)
$attribute->set_options($term_ids);
else
$attribute->set_options($f_value_array);
$attribute->set_visible(true); // If visible on frontend.
$attributes[] = $attribute;
}
$product->set_attributes($attributes);
$res = $product->save();
if ($taxonomy_count)
\flush_rewrite_rules();
return $res;
}
public static function createTaxonomyAttribute($slug, $name, $type = 'text')
{
global $wpdb;
$attribute = array();
$attribute['attribute_label'] = \wc_clean($slug);
$attribute['attribute_name'] = \wc_sanitize_taxonomy_name($name);
$attribute['attribute_type'] = $type;
$attribute['attribute_orderby'] = 'menu_order';
// validate slug
if (strlen($attribute['attribute_name']) >= 28 || \wc_check_if_attribute_name_is_reserved($attribute['attribute_name']))
return false;
if (\taxonomy_exists(\wc_attribute_taxonomy_name($attribute['attribute_name'])))
return \wc_attribute_taxonomy_id_by_name($attribute['attribute_name']);
// Create the taxonomy
$insert = $wpdb->insert($wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute);
if (\is_wp_error($insert))
return false;
$id = $wpdb->insert_id;
\wp_schedule_single_event(time(), 'woocommerce_flush_rewrite_rules');
\delete_transient('wc_attribute_taxonomies');
return $id;
}
Related
I am trying to create product attribute on user input, but whenever i send the request the attribute gets created but it overrides the previous attributes linked to the product.
public static function create_variable_product(){
$variant_data = wp_clean(isset($_POST['data']) ? wp_unslash($_POST['data']) : '');
$product_id = wp_clean(isset($_POST['product_id']) ? wp_unslash($_POST['product_id']) : '');
$variant_data = json_decode($variant_data);
$product = wc_get_product($product_id);
if($product->has_child() == false){
$product = new WC_Product_Variable($product_id);
}
$attribute = self::create_product_attribute($product,$variant_data);
wp_send_json($attribute);
}
public static function create_product_attribute($product,$variant_data){
$id = [];
for($i=0; $i<count($variant_data); $i++){
$attribute = new WC_Product_Attribute();
$attribute->set_id(0);
foreach($variant_data[$i] as $key => $value){
if($key == 'attribute_name'){
$attribute->set_name($value);
}
if($key == 'options'){
$attribute->set_options($value);
}
}
$attribute->set_position( 1 );
$attribute->set_visible( 1 );
$attribute->set_variation( 1 );
$attribute->is_taxonomy(0);
$product->set_attributes(array($attribute));
array_push($id,$product->save());
}
return $id;
}
The data being passed to $_POST['data'] is:
[{
attribute_name: 'Color',
options: ['red','yellow','blue']
}]
There are some mistakes in your code. Try the following instead (untested):
public static function create_variable_product(){
$data = wp_clean(isset($_POST['data'] ? wp_unslash($_POST['data']) : '');
$product_id = wp_clean(isset($_POST['product_id']) ? wp_clean( wp_unslash($_POST['product_id']) ) : '');
$product = wc_get_product($product_id);
if( is_a($product, 'WC_Product') && ! $product->is_type('variable') && isset($_POST['data']) ){
$product = new WC_Product_Variable($product_id);
}
$attribute = self::create_product_attribute($product, $data);
$product->save()
wp_send_json($attribute);
}
public static function create_product_attribute( $product, $data ) {
$attributes = $product->get_attributes();
$attribute = new WC_Product_Attribute();
foreach($variant_data[$i] as $key => $values){
if( $key === 'attribute_name'){
$attribute->set_name($values);
} elseif( $key === 'options' ){
$attribute->set_options($values);
}
}
$attribute->set_position( 1 );
$attribute->set_visible( 1 );
$attribute->set_variation( 1 );
$attribute->is_taxonomy(0);
$attributes[$attribute->get_name()] = $attribute;
$product->set_attributes($attributes);
}
It should better work.
What i am trying to do is if the user selects a Taxonomy attribute then is should fetch the terms id and set it into the options but, while doing so it just sets the id in the attribute variation rather than the name of the term
public static function create_product_attribute($product,$attribute_data){
$id = [];
function matchTerms($attribute,$types,$isTaxonomy){
$term_id = [];
($isTaxonomy == true) ? $options = (array) $attribute : $options = (array) $attribute->get_options();
foreach($options as $option){
($isTaxonomy == true) ? $name = $option->name : $name = get_term($option)->name;
foreach($types as $type){
if(strtolower($type) == strtolower($name)){
array_push($term_id,($isTaxonomy == true) ? $option->term_id : $option);
}
}
}
return $term_id;
}
foreach($attribute_data as $input_attribute){
$attributes = (array) $product->get_attributes();
$attribute = new KKART_Product_Attribute();
$attribute->set_name($input_attribute['name']);
if(strpos($input_attribute['name'],'pa_') !== false){
$terms = get_terms([
'taxonomy' => $input_attribute['name'],
'hide_empty' => false,
]);
$term_id = matchTerms($terms,$input_attribute['types'],$isTaxonomy = true);
// $terms = [];
// foreach($term_id as $term){
// array_push($terms,get_term($term)->name);
// }
}
(!empty($term_id)) ? $options = $term_id : $options = $input_attribute['types'];
$attribute->set_options($options);
$attribute->set_position( 1 );
$attribute->set_visible( 1 );
$attribute->set_variation( 1 );
$attributes[$attribute->get_name()] = $attribute;
$product->set_attributes($attributes);
array_push($id,$product->save());
}
return $id;
}
The data in `$attribute_data is:
[
{"name":"Color","types":["red"]},
{"name":"pa_material","types":["Type"]}
]
It turns out that when the term_id gets passed in the backend it works fine, but when you check it on the attribute product section it just shows the term id not name for that you just need to save the attribute and let it reload, But even if you dont save it and keep it untouched, it will work fine for the end user.
I am adding in my main WordPress menu to WooCommerce product category menu items, the children subcategory terms as submenu items with the following code and it works.
The code:
add_filter("wp_get_nav_menu_items", function ($items, $menu, $args) {
// don't add child categories in administration of menus
if (is_admin()) {
return $items;
}
foreach ($items as $index => $i) {
if ("product_cat" !== $i->object) {
continue;
}
$term_children = get_term_children($i->object_id, "product_cat");
// add child categories
foreach ($term_children as $index2 => $child_id) {
$child = get_term($child_id);
$url = get_term_link($child);
$e = new \stdClass();
$e->title = $child->name;
$e->url = $url;
$e->menu_order = 500 * ($index + 1) + $index2;
$e->post_type = "nav_menu_item";
$e->post_status = "published";
$e->post_parent = $i->ID;
$e->menu_item_parent = $i->ID;
$e->type = "custom";
$e->object = "custom";
$e->description = "";
$e->object_id = 0;
$e->db_id = 0;
$e->ID = 0;
$e->position = 0;
$e->classes = array();
$items[] = $e;
}
}
return $items;
}, 10, 3);
But I would like to sort that submenu items alphabetically in Ascending order and I didn't find a way to do it yet. How can I sort that submenu items by name (alphabetically) in Ascending order?
An additional foreach loop is required, to sort category sub menu items by name. Also there was some little oversights in your code (to avoid some errors). To finish you should always name your hooked functions for many different reasons.
The revisited code:
add_filter( 'wp_get_nav_menu_items', 'custom_submenu_product_categories', 10, 3 );
function custom_submenu_product_categories( $items, $menu, $args ) {
// don't add child categories in administration of menus
if (is_admin()) {
return $items;
}
$taxonomy = 'product_cat';
foreach ($items as $index => $post) {
if ( $taxonomy !== $post->object ) {
continue;
}
$children_terms_ids = get_term_children( $post->object_id, $taxonomy );
$unsorted_terms = array(); // Initializing
// Loop through children terms to prepare them to be sorted by name
foreach ( $children_terms_ids as $child_id ) {
$child_term = get_term( $child_id, $taxonomy );
$unsorted_terms[$child_term->name] = $child_term; // Set each term in an array for sorting
}
ksort($unsorted_terms); // Sort menu sub-items by term name ASC
$index2 = 0; // Initializing
// Loop through sorted child terms to set them as sorted sub menu items
foreach ( $unsorted_terms as $child_term_name => $child_term ) {
$item = new \stdClass();
$item->title = $child_term_name;
$item->url = get_term_link( $child_term, $taxonomy );
$item->menu_order = 500 * ($index + 1) + $index2;
$item->post_type = 'nav_menu_item';
$item->post_status = 'published';
$item->post_parent = $post->ID;
$item->menu_item_parent = $post->ID;
$item->type = 'custom';
$item->object = 'custom';
$item->description = '';
$item->object_id = 0;
$item->db_id = 0;
$item->ID = 0;
$item->position = 0;
$item->classes = array();
$item->target = ''; // <= Missing - Mandatory to avoid an error
$item->xfn = ''; // <= Missing - Mandatory to avoid an error
$items[] = $item;
$index2++;
}
}
return $items;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
I have a question, can I make menu with product category with one click or Dynamically in WordPress
I have many category And Very Time consuming to make this
I found a plugin for this :
https://awesometogi.com/dynamic-menu-product-categories/
but I don't know work this
You can do that with a bit of code by forcing WooCommerce categories to be added to a specific menu ID.
Add this to your theme's functions.php file:
function _custom_nav_menu_item( $title, $url, $order, $parent = 0 ){
$item = new stdClass();
$item->ID = 1000000 + $order + $parent;
$item->db_id = $item->ID;
$item->title = $title;
$item->url = $url;
$item->menu_order = $order;
$item->menu_item_parent = $parent;
$item->type = '';
$item->object = '';
$item->object_id = '';
$item->classes = array();
$item->target = '';
$item->attr_title = '';
$item->description = '';
$item->xfn = '';
$item->status = '';
return $item;
}
add_filter("wp_get_nav_menu_items", function ($items, $menu, $args) {
if( $menu->term_id != 24 ) return $items; // Where 24 is Menu ID, so the code won't affect other menus.
// don't add child categories in administration of menus
if (is_admin()) {
return $items;
}
$ctr = ($items[sizeof($items)-1]->ID)+1;
foreach ($items as $index => $i)
{
if ("product_cat" !== $i->object) {
continue;
}
$menu_parent = $i->ID;
$terms = get_terms( array('taxonomy' => 'product_cat', 'parent' => $i->object_id ) );
foreach ($terms as $term) {
$new_item = _custom_nav_menu_item( $term->name, get_term_link($term), $ctr, $menu_parent );
$items[] = $new_item;
$new_id = $new_item->ID;
$ctr++;
$terms_child = get_terms( array('taxonomy' => 'product_cat', 'parent' => $term->term_id ) );
if(!empty($terms_child))
{
foreach ($terms_child as $term_child)
{
$new_child = _custom_nav_menu_item( $term_child->name, get_term_link($term_child), $ctr, $new_id );
$items[] = $new_child;
$ctr++;
}
}
}
}
return $items;
}, 10, 3);
In the above code notice the $menu->term_id != 24 part. Then change the value of 24 with your menu ID.
The code above will also exclude empty product categories.
There is also a plugin which can do the same and not just product categories: https://wordpress.org/plugins/jc-submenu/
I use Woocommerce latest version 3.4.2.
In this case, we collect the order data: the product and its additives ( I take in meta data).
How to assign the index of variable $skus[] = $product->get_sku(); as the value of variable $product_mod[] = '';?
$product_mod[1] = "0"; // The product ( ingredient Sugar) with key 1 is the product modifier with key 0.
// Get product details
$skus = $item_quantities = $line_item_totals = $product_mod = array();
// Loop though order items
foreach( $order->get_items() as $item_id => $item){
$product_id = $item->get_product_id();
$product = $item->get_product();
$item_quantities[] = $item->get_quantity();
$line_item_totals[] = $item->get_total();
$skus[] = $product->get_sku();
$product_mod[] = NULL;
$ai = $item->get_meta('Optionally select');
if( strpos( $ai, 'Cinnamon' ) !== false ) {
$skus[] = '10001';
$item_quantities[] ='1';
$line_item_totals[] = '50';
$product_mod[] = '';
}
if( strpos( $ai, 'Sugar' ) !== false ) {
$skus[] = '10002';
$item_quantities[] ='1';
$line_item_totals[] = '50';
$product_mod[] = '';
}
if( strpos( $ai, 'Mint' ) !== false ) {
$skus[] = '10003';
$item_quantities[] ='1';
$line_item_totals[] = '50';
$product_mod[] = '';
}
}
// Product details
foreach ($skus as $key => $value){
$data .= "&product_sku[".$key."]=".$value."";
$data .= "&product_quantity[".$key."]=".$item_quantities[$key]."";
$data .= "&product_price[".$key."]=".$line_item_totals[$key]."";
if( isset($product_mod[$key]) ) {
$data .= "&product_mod[".$key."]=".$key."";
}
}
print_r( $data ); now show:
// For the convenience of reading, I wrote in a column, but this is a string.
&product_sku[0]=10030
&product_quantity[0]=1
&product_price[0]=499
&product_sku[1]=10002
&product_quantity[1]=1
&product_price[1]=50
&product_mod[1]=1
Need:
&product_sku[0]=10030 // Coffe sku
&product_quantity[0]=1 // Coffe quantity
&product_price[0]=499 // Coffe price
&product_sku[1]=10002 // Sugar sku
&product_quantity[1]=1 // Sugar quantity
&product_price[1]=50 // Sugar price
&product_mod[1]=0 // Ingredient Sugar with number 1, is a product modifier with number 0.
I think this is right way:
You have been complicating a bit the thing… You need to set the main order item index in a variable to get it for your product modifier in the selected additional options. No need of any complications…
I have revisited, simplified and compacted your code:
// Array of defined options ( sku => option name )
$options = array(
'10001' => 'Cinnamon',
'10002' => 'Sugar',
'10003' => 'Mint',
);
$count = 0;
// Loop though order items
foreach( $order->get_items() as $item_id => $item){
$product_id = $item->get_product_id();
$product = $item->get_product();
$data .= '&product_sku['.$count.']='.$product->get_sku();
$data .= '&product_quantity['.$count.']='.$item->get_quantity();
$data .= '&product_price['.$count.']='.$item->get_total();
$ind = $count; // Here we set the main order item index in a variable
$count++;
// Get order item selected options
$options_selected = $item->get_meta('Optionally select');
// Loop though order items selected options
foreach( $options as $sku_key => $label_value ){
if( strpos( $options_selected, $label_value ) !== false ) {
$data .= '&product_sku['.$count.']='.$sku_key;
$data .= '&product_quantity['.$count.']=1';
$data .= '&product_price['.$count.']=50';
$data .= '&product_mod['.$count.']='.$ind;
$count++;
}
}
}
// Testing output
print_r( $data );
Untested, but it should work as expected.
Just make a class. That's what they are made for.
That way you will always have your data neatly grouped together the way you want it without the messiness of having to remain an order or multiple arrays.
The class below is just a proof of concept, getters and setters you'll have to make yourself, but if you do it like this, you have control of the data, where it goes, what belongs with what, and you can even move data from one array to another or copy the objects over.
What you do is make your own object that has the product data you want.
Then you add an array in it "extras" where you can "extras" to the product.
Then you add a function to count the total of "own price + price of extras"
And some other helper functions you need.
Then you wrap it up and have a nice class to help you.
Please note, it's pseudo code, will need some work.
class ProductData
{
protected $product_id;
protected $product;
protected $quantity;
protected $total;
protected $sku;
protected $extras = [];
public function __construct($product_id, $product, $sku, $total, $quantity)
{
$this->product_id = $product_id;
$this->product = $product;
$this->sku = $sku;
$this->total = $total;
$this->quantity = $quantity;
}
public function addExtra($product)
{
$this->extras[] = $product;
}
public function getTotal()
{
$total = $this->total;
foreach($this->extras as $extra) {
$this->total += $extra->getTotal();
}
return total;
}
public static function fromItem($item)
{
$product = new self(
$item->get_product_id(),
$item->get_product(),
$item->get_sku(),
$item->line_item_totals(),
$item->get_quantity()
);
return $product;
}
}
$preregistered_extras = [
'Cinnamon' => new Product(
wc_get_product_id_by_sku('10001'),
wc_get_product(wc_get_product_id_by_sku('10001')),
'10001',
'50',
'1'),
'Sugar' => new Product(
wc_get_product_id_by_sku('10002'),
wc_get_product(wc_get_product_id_by_sku('10002')),
'10002',
'50',
'1'),
'Mint' => new Product(
wc_get_product_id_by_sku('10003'),
wc_get_product(wc_get_product_id_by_sku('10003')),
'10003',
'50',
'1'),
];
$product_list = [];
foreach( $order->get_items() as $item_id => $item){
$product = Product::fromItem($item);
$ai = $item->get_meta('Optionally select');
foreach($preregistered_extras as $name => $extra) {
if( stripos( $ai, $name ) !== false ) {
$product->addExtra($extra);
}
}
}
var_dump($product_list);
$total = 0;
foreach($product_list as $item) {
$total += $item->getTotal();
}
echo "Total = $total";