SQL to sort nested comments in wordpress by likes - sql

I hope some kind person can help me out here.
I want to sort nested comments in wordpress by likes. I have only found one plugin that does this and it doesn't meet my needs, so I'm attempting to write my own. Most of it is actually pretty straightforward, but the sql is eluding me (not really my strength).
I need an SQL Query to sort comments by likes, with replies immediately following their parent, and replies to each parent also sorted by likes. Top level comments and replies are differentiated by 'layer'. There is only one level of replies. My table looks like this:
ID (Int)
Comment_Name (VarChar)
Layer (Int)... 1 for top level comment, 2 for reply
ID_of_Parent_Comment (Int)... replys must be grouped under top level comment with this id
Likes (Int)
For example, if top level comments are represented by numbers and replies by letters, it would look something like this:
1, 2, 3, 3a, 3b, 4, 5, 5a... etc
Anyone have any ideas?

It turns out that the other answer did not quite work out after all. It sure looked right. Replies were grouped nicely beneath the appropriate parent comment, everthing was sorted by likes. But if you look closely, the sqlfiddle test returned 14 records where there are only 12 available.
After spending way too much time fiddling with it on my site, I couldn't resolve it any further. One group or the other (top level comments or replies) were always either left off or duplicated.
I finally gave up, assuming that it could not be done with SQL, so I went back to something I was familiar with: php. Here is my solution. Hopefully someone will find it useful. If nothing else, it was a fun project.
myComments.php
<?php
global $wpdb;
$post_ID = get_the_ID();
// Get Comment Table
$sql =
" SELECT *"
." FROM wp_comments"
." WHERE comment_post_ID = " . $post_ID // only retrieve comments for this post
." AND comment_parent = '0'" // only retrieve top level comments
." ORDER BY likes DESC"
.";";
$tlc = $wpdb->get_results($sql, ARRAY_A); // Retrieve all records into $tlc
// this should never be
// large enough to be a problem.
$commentCount = count( $tlc ); // Number of TopLevelComments
// Adjust Comments
for ( $i = 0; $i <= $commentCount-1; $i++ ) {
$tlc[$i]['layer'] = 0; // Layer 0 indicates top level comment
$tlc[$i]['index'] = $i; // index is used to group parents
// with children
}
// Get Reply Table
$sql =
" SELECT *"
." FROM wp_comments"
." WHERE comment_post_ID = " . $post_ID
." AND comment_parent > '0'" // only retrieve replies
." ORDER BY likes DESC"
.";";
$replies = $wpdb->get_results($sql, ARRAY_A);
$replyCount = count( $replies );
// Adjust Replies
for ( $i = 0; $i <= $commentCount-1; $i++ ) {
$replies[$i]['layer'] = 1; // Layer 1 indicates replies
}
// Set child index to that of parent
// then add child record to parent array
for ( $i = 0; $i <= $replyCount-1; $i++ ) {
$x = $replies[$i]['comment_parent']; // Get ID of parent
for ( $j = 0; $j <= $commentCount-1; $j++ ) {
if ( $tlc[$j]['comment_ID'] == $x ) { // If parent found
$value = $tlc[$j]['index']; // Get parent's index
$replies[$i]['index'] = $value; // Give child parent's index
array_push ( $tlc, $replies[$i]);
}
}
}
// Sort comments
// Note that $tlc was sorted by select
// and index was assigned while in that order
$tlc = array_orderby($tlc, 'index', SORT_ASC,
'layer', SORT_ASC,
'likes', SORT_DESC);
// Display comments
$commentCount = count($tlc);
if ( $commentCount ) {
echo "<ol class='commentNumbering'>";
// Used to determine if we have opened a second <ol> for nested comments
// and ensure we close it before we are done.
$inReplyList = false;
// We don't want to close the <ol> before we've opened it.
$firstComment = true;
for ( $i = 0; $i <= $commentCount-1; $i++ ) {
$myComment = $tlc[$i];
// Set $depth (needed by reply-link on myCommentTemplate page)
$depth = 0;
$comment_ID = $myComment['comment_ID'];
while( $comment_ID > 0 ) {
$tempComment = get_comment( $comment_ID );
$comment_ID = $tempComment->comment_parent;
$depth++;
}
// Treat each group of nested comments as a separate ordered group
if ( $depth == 2 ) {
if ( ! $inReplyList ) {
echo "<ol>";
$inReplyList = true;
}
} else {
if ( ! $firstComment ) {
if ( $inReplyList ) {
echo "</ol>";
$inReplyList = false;
}
}
}
$firstComment = false;
// Display each comment
include ('myCommentTemplate.php');
}
if ( $inReplyList ) {
echo "</ol>";
}
echo "</ol>";
} else {
echo 'No comments found.';
}
// Where comments are made
include('myCommentForm.php');
$wpdb->flush();
?>
function array_orderby() (located in functions.php)
/* SORT PHP ARRAYS OF RECORDS */
// PHP function 'array_multisort' requires columns //
// This function handles the conversion from row to col and back again //
// Example:
// $sorted = array_orderby($data, 'volume', SORT_DESC, 'edition', SORT_ASC);
function array_orderby()
{
$args = func_get_args();
$data = array_shift($args);
foreach ($args as $n => $field) {
if (is_string($field)) {
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
return array_pop($args);
}

Looks like this should be close:
select
post.ID,
post.likes as postLikes,
reply.ID,
reply.likes as replyLikes
from MyTable post
left join MyTable reply
on post.ID = reply.ID_of_Parent_Comment
where post.ID_of_Parent_Comment is null
order by post.likes desc, reply.likes desc
;
It will give you the parent ID's sorted by parent likes and the related child ID's for each parent (if any) sorted by most liked child

Related

Automatically update ACF field upon date

Here is what I am trying to accomplish: turn off a True/False Advanced Custom Fields(ACF) option on a post if the current date is on or past a selected date on the same post. Also after that code, turn off a Sample Lesson True/False option inside of the lessons attached to the current post.
At first, all I had was the update_sample_child_lessons function with an 'init' action (i.e.add_action( 'init', 'update_sample_child_lessons' );), but that seemed to only run when I clicked update on the post. It did work and everything switched over, but it only ran when I manually clicked Update on the post. So then I did a little research and found that a Cron job should do the trick if I want the code to run automatically without me having to click update, but for some reason I can't seem to get it to work.
So if you know of a way to accomplish what I am trying to do with the code below, or with other code that is completely different, any suggestions or help would be much appreciated.
//CRON JOB TO RUN EVERYDAY
function myprefix_custom_cron_schedule( $schedules ) {
$schedules['every_day'] = array(
'interval' => 86400, //24 HOURS IN SECONDS
'display' => __( 'Every 24 hours' ),
);
return $schedules;
}
add_filter( 'cron_schedules', 'myprefix_custom_cron_schedule' );
if ( ! wp_next_scheduled( 'myprefix_cron_hook' ) ) {
wp_schedule_event( time(), 'every_day', 'myprefix_cron_hook' );
}
//AUTOMATICALLY ADJUSTS SAMPLE LESSON FREE OPTIONS AND FREE BANNER IF DATE IS PASSED
add_action( 'myprefix_cron_hook', 'update_sample_child_lessons' );
function update_sample_child_lessons() {
$allcourses = array(
'post_type' => 'sfwd-courses', //CUSTOM POST TYPE: COURSES
'posts_per_page' => -1 //QUERY ALL OF THEM
);
$query = new WP_Query($allcourses);
if ($query->have_posts()) {
global $post;
if ( ( in_array( $post->post_type, array( 'sfwd-courses' ), true ) )) { //ONLY DO ACTION IF ON CPT OF COURSES
while ($query->have_posts()) {
$query->the_post();
$course_id = learndash_get_course_id( $post->ID ); //GET THE COURSE ID
$free = get_field('display_free_lessons', $course_id); //GET THE FREE COURSE OPTION (TRUE/FALSE TICKER)
if (!empty($free)) { //ONLY DO REST OF CODE IF FREE OPTION IS TURNED ON
$freeDate = get_field('free_until', $course_id); //GET THE DATE FIELD THAT THE COURSE IS FREE UNTIL
$currentDate = date('Ymd'); //GET CURRENT DATE
$diff = strtotime($freeDate) - strtotime($currentDate); //GET THE DIFFERENCE BETWEEN THE TWO DATES
if ($diff <= 0) { //ONLY DO REST OF CODE IF DATE DIFFERENCE IS LESS THAN OR EQUAL TO ZERO
$value = '';
update_field('display_free_lessons', $value, $course_id); //UPDATES THE FREE OPTION FIELD TO FALSE(OR NOTHING)
//LESSON CODE
$lessons = array_slice(learndash_course_get_lessons($course_id), 1); //GET ALL THE LESSONS FROM THE COURSE EXCEPT FOR THE FIRST ONE
foreach ($lessons as $lesson) {
$lessonID = $lesson->ID; //GET THE LESSON ID
$lesson_meta = get_post_meta($lessonID); //GET THE METADATA FOR THE LESSON
if ( is_array( $lesson_meta ) ) {
foreach ( $lesson_meta as $meta_key => $meta_value ) {
if ( '_sfwd-lessons' === $meta_key ) {
$lesson_settings = maybe_unserialize( $meta_value[0] ); //SOME OF THE ARRAYS ARE SERIALIZED, SO UNSERIALIZE IF NEEDED
if ( isset( $lesson_settings['sfwd-lessons_sample_lesson'] ) ) {
$lesson_settings['sfwd-lessons_sample_lesson'] = ''; //TURN OFF THE SAMPLE LESSON OPTION ON THE LESSONS
}
update_post_meta( $lessonID, $meta_key, $lesson_settings );
}
}
}
} //END FOREACH
} //END IF DIFF IS 0
wp_reset_postdata();
}
}
}
}
}
Thanks for the comment #Luke Chaffey, I was actually able to figure it out after finding I had my cron actions reversed. Below is the final code that I got working so that it runs every day at 12am:
//CRON JOB TO RUN EVERYDAY
function custom_cron_schedule( $schedules ) {
$schedules['every_day'] = array(
'interval' => 86400,
'display' => __( 'Every 24 hours' ),
);
return $schedules;
}
add_filter( 'cron_schedules', 'custom_cron_schedule' );
$ve = get_option('gmt_offset') > 0 ? '-' : '+';
if ( ! wp_next_scheduled('cron_sample_lesson' ) ) {
wp_schedule_event(strtotime('00:00 tomorrow ' . $ve .
absint(get_option('gmt_offset')) . ' HOURS'), 'daily','cron_sample_lesson' );
}
add_action('cron_sample_lesson', 'update_sample_child_lessons' );
function update_sample_child_lessons() {...

posts_orderby not displaying the posts

I would like to customize my wordpress search page
First, i used the "posts_where" to modify the clause
function search_posts_where ($where) {
global $wp_query, $wpdb;
// Searching and not in admin
if (!is_admin() && $wp_query->is_search && isset($wp_query->query_vars['s'])) {
// Tables names
$post_title = "{$wpdb->prefix}posts.post_title";
$post_excerpt = "{$wpdb->prefix}posts.post_excerpt";
$post_content = "{$wpdb->prefix}posts.post_content";
$post_type = "{$wpdb->prefix}posts.post_type";
$post_status = "{$wpdb->prefix}posts.post_status";
$post_author = "{$wpdb->prefix}posts.post_author";
$post_ID = "{$wpdb->prefix}posts.ID";
$post_date = "{$wpdb->prefix}posts.post_date";
// Get the 's' parameters
$wp_query->query_vars['s'] ? $search_text = $wp_query->query_vars['s'] : $search_text = 'IS NULL';
// Write the where clause
$where = " AND ((($post_title LIKE '%$search_text%')";
$where .= " OR ($post_excerpt LIKE '%$search_text%')";
$where .= " OR ($post_content LIKE '%$search_text%')))";
$where .= " AND $post_type IN ('parcours')";
$where .= " AND $post_status = 'publish'";
$where .= " GROUP BY $post_ID";
}
return $where;
}
add_filter('posts_where', 'search_posts_where', 10, 2);
It works fine. All posts belonging to my custom post type 'parcours' are shown, depending on what I entered for the 's' query.
Second, i used the "posts_join" to add the meta table (not used yet !)
function search_posts_join ($join) {
global $wp_query, $wpdb;
// Searching and not in admin
if (!is_admin() && $wp_query->is_search && isset($wp_query->query_vars['s'])) {
// Tables names
$post_meta = "{$wpdb->prefix}postmeta";
$post_ID = "{$wpdb->prefix}posts.ID";
$post_meta_ID = "{$wpdb->prefix}postmeta.post_id";
// Join clause
$join .= "LEFT JOIN $post_meta ON ($post_ID = $post_meta_ID)";
}
return $join;
}
add_filter('posts_join', 'search_posts_join', 10, 2);
Still works perfectly !
Now the problem, i would like to order my posts in ascending direction (default is descending). So, i added the "posts_orderby" hook.
function search_posts_orderby ($orderby) {
global $wp_query, $wpdb;
// Searching and not in admin
if (!is_admin() && $wp_query->is_search) {
// Tables names
$post_title = "{$wpdb->prefix}posts.post_title";
$post_date = "{$wpdb->prefix}posts.post_date";
$post_ID = "{$wpdb->prefix}posts.ID";
// Order by clause
$orderby .= " ORDER BY $post_title ASC,";
$orderby .= " $post_date DESC";
}
return $orderby;
}
add_filter('posts_orderby', 'search_posts_orderby', 10, 2);
And here is the problem. All posts disapeared. Removing the "orderby" and they come back.
Looking at the SQL query, i have
"SELECT SQL_CALC_FOUND_ROWS wp_128444_posts.* FROM wp_128444_posts LEFT JOIN wp_128444_postmeta ON (wp_128444_posts.ID = wp_128444_postmeta.post_id) WHERE 1=1 AND (((wp_128444_posts.post_title LIKE '%tour%') OR (wp_128444_posts.post_excerpt LIKE '%tour%') OR (wp_128444_posts.post_content LIKE '%tour%'))) AND wp_128444_posts.post_type IN ('parcours') AND wp_128444_posts.post_status = 'publish' GROUP BY wp_128444_posts.ID ORDER BY wp_128444_posts.post_title LIKE '{5a35f6e9144541f93e08829126b2cb633436cebf95d774062fff749a12e6a465}tour{5a35f6e9144541f93e08829126b2cb633436cebf95d774062fff749a12e6a465}' DESC, wp_128444_posts.post_date DESC ORDER BY wp_128444_posts.post_title ASC, wp_128444_posts.post_date DESC LIMIT 0, 6"
I don't know why WP is adding the default ORDER BY, that i don't want.
Is is possible to remove it ?
I tried to replace my hook with "pre_get_posts" hook
function order_posts_by_title( $query ) {
if ( $query->is_search() && $query->is_main_query() ) {
$query->set( 'orderby', 'title' );
$query->set( 'order', 'ASC' );
}
}
add_action( 'pre_get_posts', 'order_posts_by_title' );
With this hook, it works !! Sounds very strange to me
Any explanation ?
Thanks

Wordpress Custom Post Type Sort by Title Last Word ASC

I am taking a custom post type and trying to display it by the last word of the post title. From there, I want to pass in a variable of what letter I would like to see (eg "starts_with" = "A") in the WP_Query. Lastly, I want to display this in ASC order. From this code, how do I display in ASC order?
function dp_posts_where( $where, $query ) {
global $wpdb;
$starts_with = $query->get( 'starts_with' );
if ( $starts_with ) {
$last_name = "RIGHT($wpdb->posts.post_title, LOCATE(' ', REVERSE($wpdb->posts.post_title)) - 1)";
$where .= " AND $last_name LIKE '$starts_with%'";
}
return $where;
}
add_filter( 'posts_where', 'dp_posts_where', 10, 2 );
Please advise! Thank you!

Query giving double result instead of single

I have two tables: products and current_product_attribute_values
I have tried a join query to filter them as per attribute selected by the user but when I try this with an additional condition it gives me 2 results instead of one it is including the first one which is not matching as per query:
select * from `products` inner join `current_product_attribute_values` on `products`.`id` = `current_product_attribute_values`.`product_id` where `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `product_name` LIKE ?
here is my laravel Controller code :
$all = Input::all();
$q = Input::get('search_text');
$att_val = Input::get('attribute_value');
$subcat = Input::get('subcat_id');
$subcat_name = DB::table('subcategories')->where('id', $subcat)->value('subcategory_name');
$brandname = DB::table('brands')->where('subcat_id', $subcat)->value('brand_name');
$brand_id = DB::table('brands')->where('subcat_id', $subcat)->value('id');
$product_count = DB::table('products')->where('brand_id', $brand_id)->count();
if ($q != "") {
// getting multiple same name params
$query = DB::table('products');
$query->join('current_product_attribute_values', 'products.id', '=', 'current_product_attribute_values.product_id');
$j = 0;
foreach ($all as $key => $values) {
//echo 'my current get key is : ' . urldecode($key). '<br>';
if ($key == $name[$j]) {
$query->where('current_product_attribute_values.attribute_id', '=', $att_id_value[$j]);
echo'<br>';
print_r($query->toSql());
echo'<br>';
//echo '<br> key matched and have some value : <br>';
//echo count($values);
if (count($values) >= 1) {
//echo '<br> it has array inside <br>';
foreach ($values as $val) {
// or waali query in same attribute
echo'<br>';
$query->orwhere('current_product_attribute_values.attribute_value_id', '=', $val);
print_r($query->toSql());
echo'<br>';
}
}
$j++;
}
}
$records = $query->toSql();
$query->where('product_name', 'LIKE', '%' . $q . '%');
$records = $query->toSql();
print_r($records);
$products = $query->paginate(10)->setPath('');
$pagination = $products->appends(array(
'q' => Input::get('q')
));
if (count($products) > 0) {
$filters = DB::table('product_attributes')->where('subcategory_id', $subcat)->get(['attribute_title']);
} else {
$filters = array();
}
$categories = categories::where('add_to_menu', 1)->with('subcategories')->with('brands')->get();
$categoryhome = categories::where('add_to_menu', 1)->with('subcategories')->get();
return view('searchfilter')
->with('productsdata', $products)
->with('filtersdata', $filters)
->with('categories', $categories)
->with('categorieshome', $categoryhome)
->with('subcat_name', $subcat_name)
->with('subcat_id', $subcat)
->with('brandname', $brandname)
->with('product_count', $product_count)
->with('querytext', $q);
}
return 'No Details found. Try to search again !';
its easier if you use raw sql as calling db select function. ex:
$query=DB::select("select * from `products` inner join `current_product_attribute_values` on `products`.`id` = `current_product_attribute_values`.`product_id` where `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `current_product_attribute_values`.`attribute_id` = ? or `current_product_attribute_values`.`attribute_value_id` = ? and `product_name` LIKE ?
");
indeed you can concat vars in raw sql if you need to, ex:
$queryBrands = "select id from brands where subcat_id =".$subcat;
//echo $queryBrands
$queryBrands = DB::select($queryBrands);
By looking at your tables, product table with id value 17 has two records in table current_product_attribute_values in column product_id (I assume this column is used as foreign key to product table).
With select *, you select all of the columns from both tables. So it would most likely cause your query to return multiple records.
My suggestions:
Only select the columns you need. Avoid using select * in the long run, i.e. select product.id, product.description, current_product_attribute_values.attribute_values ......
Make use of GROUP BY
Hope these helps.

Magento API: Assigning preexisting simple products to configurable products

I've got a client database with a large range of stock items, which are being uploaded to Magento as simple products.
Now I need to group them up and assign them to configurable products with their size and colour being their configurable attributes.
The Magento API has a Product_Link class, with a promising looking method: catalogue-product-link.assign (link), but I can't for the life of me figure out what arguments I need to make it work with configurable products, providing this is how assign was meant to be used.
Well the notes here helped me get this running. So I thought I'd share with you the code to add a simple product to an existing Configurable Product.
This code assumes the simple product is a valid one to add, I'm not sure what would happen if it wasn't.
private function _attachProductToConfigurable( $_childProduct, $_configurableProduct ) {
$loader = Mage::getResourceModel( 'catalog/product_type_configurable' )->load( $_configurableProduct );
$ids = $_configurableProduct->getTypeInstance()->getUsedProductIds();
$newids = array();
foreach ( $ids as $id ) {
$newids[$id] = 1;
}
$newids[$_childProduct->getId()] = 1;
$loader->saveProducts( $_configurableProduct->getId(), array_keys( $newids ) );
}
The code from the accepted answer by Scimon does not work anymore in recent versions of magento (at least in 1.7). But fortunately, you need just a small fix to get it working again:
private function _attachProductToConfigurable( $_childProduct, $_configurableProduct ) {
$loader = Mage::getResourceModel( 'catalog/product_type_configurable' )->load( $_configurableProduct, $_configurableProduct->getId() );
$ids = $_configurableProduct->getTypeInstance()->getUsedProductIds();
$newids = array();
foreach ( $ids as $id ) {
$newids[$id] = 1;
}
$newids[$_childProduct->getId()] = 1;
//$loader->saveProducts( $_configurableProduct->getid(), array_keys( $newids ) );
$loader->saveProducts( $_configurableProduct, array_keys( $newids ) );
}
I'm working on doing this right now.
So far I've found these items helpful as references:
http://snippi.net/magento-programmatically-add-configurable-product-color-api
http://www.omnisubsole.com/blog/2009/07/01/configurable-products-in-magento.html
http://www.magentocommerce.com/boards/viewthread/6941/P30/
I'll post my code so far, and hopefully update it once it works..
// Set 'item_size' as the super attribute # choose your own attribute!
// this is the 'choose-able' field that differenciates products
$super_attributes=array( Mage::getModel('eav/entity_attribute')
->loadByCode('catalog_product','item_size')
->getData('attribute_id')
);
$product_collection=Mage::getModel('catalog/product')->getCollection();
// Fetch configurable orders
$product_collection->addFieldToFilter('type_id',Array('eq'=>"configurable"));
#$product_collection->addFieldToFilter('sku',Array('eq'=>"ASMCL000002"));
$product_collection->addAttributeToSelect('*');
$count=0;
foreach($product_collection as $product) {
$sku = $product->getSku();
echo "SKU: $sku\n";
$simple_children_collection = Mage::getModel('catalog/product')->getCollection();
$simple_children_collection->addAttributeToSelect('*');
$simple_children_collection->addFieldToFilter('sku',Array('like'=>$sku . "-%"));
echo "children: ";
foreach($simple_children_collection as $child) {
$child_sku = $child->getSku();
echo "$child_sku ";
#visiblity should be 'nowhere'
}
echo "\n";
if (!$product->getTypeInstance()->getUsedProductAttributeIds()) {
# This is a new product without the Configurable Attribue Ids set
$product->getTypeInstance()
->setUsedProductAttributeIds( $super_attributes );
//$product->setConfigurableAttributesData(array($_attributeData));
$product->setCanSaveConfigurableAttributes(true); # Not sure if this is needed.
$product->setConfigurableProductsData(''); # Use this to add child products.
}
$count++;
try {
$product->save();
$productId = $product->getId();
echo $product->getId() . ", $sku updated\n";
}
catch (Exception $e){
echo "$sku not added\n";
echo "exception:$e";
}
}
echo "\nCount is $count\n";
Okay, this uses 'item_size' as the attribute that differentiates the "simple" products. Also, this assumes that the "configurable" parent SKU is the root of the child SKU. For example, ABC001 is the parent while ABC001-SMALL and ABC001-LARGE are the simple children.
Hope that helps someone.
I this is an un-educated guess, but I think what your asking for can't be done with the existing API. You will have to write your own or just got directly to the DB.
Here is the hack-y way that I did this straight with PHP. There are three related tables. I was using color and size as my attributes.
My parent products (configurable) don't actually exist in my catalog. They are essentially model level and then the products are the SKU level.
So LIKE 'parentproductsku%' works out for the children.
$query1 = "SELECT * FROM mage_catalog_product_entity WHERE type_id= 'configurable'";
//Find the parent id
$statusMessage = "Ok, found a product with a confgurable attribute";
$result1 = $this->runQuery($query1, "query1", $statusMessage);
while ($row1 = mysql_fetch_assoc($result1)) { //entering the first loop where products are configurable
$this->parentId = $row1['entity_id'];
$this->parentSku = $row1['sku'];
echo "The SKU was $this->parentSku" . "<br />";
//insert these into the link table for association
$query2 = "SELECT * FROM mage_catalog_product_entity WHERE type_id= 'simple' AND sku LIKE '" . $this->parentSku . "%';";
// find the child ids that belong to the parent
$statusMessage = "Found some children for $this->parentSku";
$result2 = $this->runQuery($query2, "query2", $statusMessage);
while ($row2 = mysql_fetch_assoc($result2)) {//entering the second loop where SKU is like model sku
$this->childId = $row2['entity_id'];
$this->childSku = $row2['sku'];
echo "Now we're working with a child SKU $this->childSku" . "<br />";
//"REPLACE INTO catalog_product_super_attribute SET product_id='".$product->entity_id."', attribute_id='".$attribute->attribute_id."', position='".$position."'";
$query3 = "REPLACE INTO mage_catalog_product_super_attribute (product_id, attribute_id, position) VALUES ('" . $this->childId . "', '76', '0');";
$message3 = "Inserted attribute for color for ID $this->childId SKU $this->childSku";
$result3 = $this->runQuery($query3, "query3", $message3);
$query4 = "REPLACE INTO mage_catalog_product_super_attribute_label (product_super_attribute_id, store_id, use_default, value) VALUES (LAST_REPLACE_ID(), '0', '0', 'Color');";
$message4 = "Inserted attribute for Color SKU $this->childSku ID was $this->db->insert_id";
$result4 = $this->runQuery($query4, "query4", $message4);
$query5 = "REPLACE INTO mage_catalog_product_super_attribute (product_id, attribute_id, position) VALUES ('" . $this->childId . "', '529', '0');";
$message5 = "Inserted attribute for Product Size SKU $this->childSku";
$result5= $this->runQuery($query5, "query5", $message5);
$query6 = "REPLACE INTO mage_catalog_product_super_attribute_label (product_super_attribute_id, store_id, use_default, value) VALUES (LAST_REPLACE_ID(), '0', '0', 'Size');";
$message6 = "Inserted attribute for Size SKU $this->childSku ID was $this->db->insert_id";
$result6 = $this->runQuery($query6, "query6", $message6);
$query7 = "REPLACE INTO mage_catalog_product_super_link (product_id, parent_id) VALUES ('" . $this->childId . "', '" . $this->parentId . "');";
$message7 = "Inserted $this->childId and $this->parentId into the link table";
$result7 = $this->runQuery($query7, "query7", $message7);
$query8 = "REPLACE INTO mage_catalog_product_relation (parent_id, child_id) VALUES ('" . $this->parentId . "', '" . $this->childId . "');";
$message8 = "Inserted $this->childId and $this->parentId into the link table";
$result8 = $this->runQuery($query8, "query8", $message8);
} //end while row 2 the child ID
} //end while row 1 the parent id
Surprisingly, this works, if all your simple products share the same price:
$childProducts = $configurable->getTypeInstance(true)->getUsedProductIds($configurable);
// Don't add this product if it's already there
if(!in_array($child->getId(), $childProducts)) {
$childProducts[] = $child->getId();
}
$existingIds = $configurable->getTypeInstance(true)->getUsedProductAttributeIds($configurable);
$newAttributes = array();
foreach($configurable->getTypeInstance(true)->getSetAttributes($configurable) as $attribute) {
if(!in_array($attribute->getId(), $existingIds) && $configurable->getTypeInstance(true)->canUseAttribute($attribute)
&& $child->getAttributeText($attribute->getAttributeCode())) {
// Init configurable attribute
$configurableAtt = Mage::getModel('catalog/product_type_configurable_attribute')
->setProductAttribute($attribute);
// Add new attribute to array
$newAttributes[] = array(
'id' => $configurableAtt->getId(),
'label' => $configurableAtt->getLabel(),
'position' => $attribute->getPosition(),
'values' => $configurableAtt->getPrices() ? $configurable->getPrices() : array(),
'attribute_id' => $attribute->getId(),
'attribute_code' => $attribute->getAttributeCode(),
'frontend_label' => $attribute->getFrontend()->getLabel(),
);
}
}
if(!empty($newAttributes)) {
$configurable->setCanSaveConfigurableAttributes(true);
$configurable->setConfigurableAttributesData($newAttributes);
}
$configurable->setConfigurableProductsData(array_flip($childProducts));
$configurable->save();
#aeno's solution did not work for me, so I refined it a bit. This has been tested using a product instantiated via the Mage::getModel( 'catalog/product' )->load() method.
private function _attachProductToConfigurable( $childProduct, $configurableProduct )
{
$childIds = $configurableProduct->getTypeInstance()->getUsedProductIds();
$childIds[] = $childProduct->getId();
$childIds = array_unique( $childIds );
Mage::getResourceModel( 'catalog/product_type_configurable' )
->saveProducts( $configurableProduct, $childIds );
}