Используя PHP добавим товары в woocommerce с атрибутами и изображениями в придачу

от 2018 - 08 - 20

Следующие строки научат тебя, мой юный падаван, создавать товары для woocommerce руками из PHP скрипта. Сможешь потом много много товаров добавлять тем способом, который нравится.

Во первых полезный совет, есть удобный умелый плагин WooCommerce Advanced Bulk Edit , рекомендую. Если хочешь воткнуться в тему самостоятельно — читай далее.

Мне доводилось писать скрипт для загрузки большого количества квартир с картинками планировок и кучей дополнительных параметров вроде этаж, номер подъезда, номер дома, номер квартиры и так далее из файла формата JSON в список товаров WooCommerce.

Для реализации я написал отдельный самостоятельный PHP скрипт, который разместил в корне сайта, рядом с точкой входа WP — index.php, короче сразу в httpdocs/. Назовём его upload_script.php. Так же у меня имелась выгрузка квартир из 1С формата json, назовём apartments.txt следующего содержания:

{
"apartaments": [
{
"32": [
{
"7": {
"regular_price": 100500,
"pa_etazh": 1,
"pa_obshhaya_ploshhad": 56.96,
"pa_ploshhad_kuhni": 28.29,
"pa_stoimoste_za_m_kv": 62000,
"pa_tip_kvartiry": "2",
"pa_zhilaya_ploshhad": 10.64,
"pa_paradnaya": "1",
"stock_status": "instock"
}
},
{
"8": {
"regular_price": 100500,
"pa_etazh": 2,
"pa_obshhaya_ploshhad": 51.01,
"pa_ploshhad_kuhni": 24.63,
"pa_stoimoste_za_m_kv": 62000,
"pa_tip_kvartiry": "2",
"pa_zhilaya_ploshhad": 13.18,
"pa_paradnaya": "1",
"stock_status": "instock"
}
},

*** и т.д. , json, чего тут объяснять? 8)
32 - номер дома, если что. 7,8 - квартиры. Остальное должно быть понятно.

Ну и куча изображений планировок, конечно. В отдельном каталоге. При этом я знал каким квартирам какие планировки добавлять, в скрипте будет приведён массив соответствий названий файлов картинок номерам квартир.

Теперь код upload_script.php , частями, дабы поберечь твой мозг:

Для начала мы объясняем нашему скрипту, что он не сам по себе, а является элементом системы wordpress — бездушной машины по перемалыванию блогеров:

<?php 
include('wp-load.php'); // подгружаем мозг wordpress
include_once( ABSPATH . 'wp-admin/includes/image.php' ); // добавляем это, что бы иметь возможность работать с изображениями средствами wordpress

global $woocommerce;  // напомним скрипту о глобальной переменной $woocommerce одноименного плагина. Пригодится.

Настала пора прочитать данные из файла apartments.txt в оперативную память сервера.

$temp = file_get_contents('apartments.txt'); // apartments.txt лежит рядышком с нашим скриптом, путь не требуется.

$arrJSON = array();
if (!empty($temp)) $arrJSON = json_decode($temp, true); // всего делов, $arrJSON массив для работы готов.

Теперь переиначим массив так, как удобнее мне. Вам это может и не зачем, мало ли, пригодится. Оставлю:

$arrApartaments = array();
foreach($arrJSON['apartaments'] as $arrHouse) {
    if (!empty($arrHouse)) {
        forEach ($arrHouse as $strHouseKey => $arrWryApartaments) {
            foreach($arrWryApartaments as $arrWryApartament) {
                $strApartamentNumber = key($arrWryApartament);
                $arrApartaments[$strHouseKey][$strApartamentNumber] = $arrWryApartament[$strApartamentNumber];
// Тут я ещё над ценой изголяюсь (перемножаю площадь на стоимость за метр). 
// Вы тоже у себя можете что-нибудь поделать, на этапе сбора информации, если надо.
$arrApartaments[$strHouseKey][$strApartamentNumber]['regular_price'] = (float)$arrWryApartament[$strApartamentNumber]['pa_obshhaya_ploshhad'] * 
                                                                       (int)$arrWryApartament[$strApartamentNumber]['pa_stoimoste_za_m_kv'];
		}
	}
    }
}

Внимание, произведение искусства, функция для загрузки изображения к посту — товару:

function attach_image($fileurl, $filealt, $post_id)  
{
    if (empty($fileurl)) return false;
    $filename = $fileurl; // Используем имя файла, включая расширение, из $fileurl. Вроде myimage.jpg

    $source = __DIR__.'/apartments-images/'.$filename; // Тут получим расположение исходного файла, абсолютный путь от корня unix сервера.

    if (!is_file($source)) return false; // Если исходный файл отсутствует - всё тлен. Завершаем функцию.

    $filetype = wp_check_filetype($destination); // Get the mime type of the file
    $attachment = array( // Настроим данные для загружаемого изображения, пункт назначения, тип, название файла, ID автора, сопроводительная записка.
        'guid'           => get_option('siteurl') . '/wp-content/uploads/myuploads/'.$filename, 
        'post_mime_type' => $filetype['type'],
        'post_title'     => $filename,
        'post_author'    => 1,
        'post_content'   => ''
    );

    $attach_id = wp_insert_attachment( $attachment, $source , $post_id ); // Присоединить/загрузить изображение к указанному ID поста, 
    // читай добавим новый пост картинки к посту продукта. Функция вернёт ID созданного поста-картинки.

    $attach_data = wp_generate_attachment_metadata( $attach_id, $source ); // Генерируем необходимую attachment data, filesize, height, width etc.

    wp_update_attachment_metadata( $attach_id, $attach_data ); // Добавим ранее полученную метадату к загруженному изображению.

    add_post_meta($attach_id, '_wp_attachment_image_alt', $filealt); // Добавим альтернативный текст к загруженному изображению. 
// (Пригодится если картинки не окажется на месте, или посетитель использует текстовый браузер, без возможности просмотра изображений)

    return $attach_id; // Вернём ID загруженного изображения, для дальнейшей работы с ним.
}

Теперь покажу отрывок PHP массива соответствия номеров квартир именам файлов изображений. Сам его писал, время тратил, примерно так:

$arrImagesAccordance32 = array(
	'plan_32dom_1.jpg' => array( 305, 297, 289, 281, 273, 265, 257, 249, 241, 233 ),
	'plan_32dom_2.jpg' => array( 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 156, 152, 148, 144 ),
	'plan_32dom_3.jpg' => array( 225, 218, 211, 204, 197 )
    );
// В принципе ничего сложного. Ассоциативный массив массивов, с ключами - именами файлов.

Второе чудо света, функция добавления нового товара woocommerce продукта типа квартира. Она принимает параметрами массив с данными о квартире (тот, что мы создавали по началу из файла), номер дома, номер самой квартиры, и массив соответствия номеров квартир картинкам (по ссылке, на всякий, что бы не засорять оперативку).


function add_new_apartament( $arrApartment, $strHouseNumber, $intApartmantNumber, &$arrImagesAccordance32 ) {

	$strFileName = '';
        // Пот тут побежали по массиву соответствия файлов картинок планировок проверять наличие текущего номера квартиры
	foreach( $arrImagesAccordance32 as $fileName => $arrApartmentNumbers) {
		if ( in_array( (int)$intApartmantNumber, $arrApartmentNumbers) ) // Если есть - то знаем какую картинку использовать (название файла).
		{
			$strFileName = $fileName;
			break;
		}
	}
	
	$strApartmentTitle = 'Квартира'; // Будущий заголовок продукта. Мы его ещё с номером квартиры конкатенируем - будет красиво.
		
    $post_id = wp_insert_post( array(
        'post_author' => 1,
        'post_title' => $strApartmentTitle . ' №' . $intApartmantNumber ,
        'post_content' => '',
        'post_status' => 'publish',
        'post_type' => 'product',
    ) );

        // Вот и хорошо. Создали новый пост в wordpress.

	// теперь я присоединю изображение, функция attach_image полностью описана выше. Применим.
	$attachment_id = attach_image($strFileName, 'План квартиры', $post_id);

        // если всё хорошо и вернулся новый id прикрепленной картинки, мы запишем его как параметр в свежую квартиру.
	if ($attachment_id) {
	    update_post_meta( $post_id, '_thumbnail_id', $attachment_id);
	}
	
	// Ну а теперь впишем всё остальное, что просит woocommerce, что бы считать wp пост - продуктом (товаром).
        wp_set_object_terms( $post_id, 'simple', 'product_type' );

        // Тут я малость схитрил. У меня уже созданы через админку wp woocommerce категории для квартир. И это номера домов. 
        // И я уже знаю их ID. Вы тоже можете так сделать, id будет видно в адресной строке, 
        // когда создадите категорию и перейдёте в неё.
	if ($strHouseNumber == 31) {
		wp_set_object_terms( $post_id, array(3131), 'product_cat'); // Итак, если дом 31 то ID категории = 3131;
	} else {
		wp_set_object_terms( $post_id, array(3232), 'product_cat'); // А если дом 32 то ID категории = 3232;
	}
	
        // Дальше хреначим всю дополнительную информацию какая нравится, не в чём себе не отказывайте.
        // можно даже спросить гугл и почитать, что такое термины вордпресс и как они используются в таксономиях.
	wp_set_object_terms( $post_id, (string)$strHouseNumber, 'pa_korpus');
	wp_set_object_terms( $post_id, (string)$intApartmantNumber, 'pa_nomer-kvartiry');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_etazh'], 'pa_etazh');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_obshhaya_ploshhad'], 'pa_obshhaya-ploshhad');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_zhilaya_ploshhad'], 'pa_ploshhad-kuhni');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_ploshhad_kuhni'], 'pa_zhilaya-ploshhad');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_stoimoste_za_m_kv'], 'pa_stoimost-za-m-kv');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_tip_kvartiry'], 'pa_tip-kvartiry');
	wp_set_object_terms( $post_id, (string)$arrApartment['pa_paradnaya'], 'pa_paradnaya');
	wp_set_object_terms( $post_id, (string)'Нет', 'pa_ochered');
	
// А вот теперь более важные параметры, обязательные, это уже для woocommerce. 
// Список наших чудных атрибутов надо запихнуть в виде массива с параметрами в мета информацию определенного типа _product_attributes к посту.
    $arrAttributes = Array(
		'pa_korpus' => Array(
			'name'=>'pa_korpus',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),		
		'pa_nomer-kvartiry' => Array(
			'name'=>'pa_nomer-kvartiry',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_etazh' => Array(
			'name'=>'pa_etazh',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_obshhaya-ploshhad' => Array(
			'name'=>'pa_obshhaya-ploshhad',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_ploshhad-kuhni' => Array(
			'name'=>'pa_ploshhad-kuhni',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_zhilaya-ploshhad' => Array(
			'name'=>'pa_zhilaya-ploshhad',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_stoimost-za-m-kv' => Array(
			'name'=>'pa_stoimost-za-m-kv',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_tip-kvartiry' => Array(
			'name'=>'pa_tip-kvartiry',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_paradnaya' => Array(
			'name'=>'pa_paradnaya',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
		'pa_ochered' => Array(
			'name'=>'pa_ochered',
			'value'=>'',
			'position' => '0',
			'is_visible' => '1', 
			'is_variation' => '1',
			'is_taxonomy' => '1'
		),
	);
    update_post_meta( $post_id, '_product_attributes', $arrAttributes);	// Вот тут, гляди, запихал перечень атрибутов.

// Дальше, если не ошибаюсь, всё обязательно, их надо просто создать, можно пустыми.
// По контексту понятно, что это за свойства поста. Товара. Продукта. В wordpress всё есть post, любая сущность.
    update_post_meta( $post_id, '_visibility', 'visible' );
    update_post_meta( $post_id, '_stock_status', $arrApartment['stock_status']);
    update_post_meta( $post_id, 'total_sales', '0' );
    update_post_meta( $post_id, '_downloadable', 'no' );
    update_post_meta( $post_id, '_virtual', 'yes' );
    update_post_meta( $post_id, '_regular_price', $arrApartment['regular_price'] );
    update_post_meta( $post_id, '_sale_price', $arrApartment['regular_price'] );
    update_post_meta( $post_id, '_price', $arrApartment['regular_price'] );
    update_post_meta( $post_id, '_purchase_note', '' );
    update_post_meta( $post_id, '_featured', 'no' );
    update_post_meta( $post_id, '_weight', '' );
    update_post_meta( $post_id, '_length', '' );
    update_post_meta( $post_id, '_width', '' );
    update_post_meta( $post_id, '_height', '' );
    update_post_meta( $post_id, '_sku', '' );
    update_post_meta( $post_id, '_sale_price_dates_from', '' );
    update_post_meta( $post_id, '_sale_price_dates_to', '' );
    update_post_meta( $post_id, '_sold_individually', '' );
    update_post_meta( $post_id, '_manage_stock', 'no' );
    update_post_meta( $post_id, '_backorders', 'no' );
    update_post_meta( $post_id, '_stock', '' );
	
    if ( !empty($post_id) ) {
        // Если не пуст пост id - успех. Можно вывести в лог инфу, типа добавили такую-то квартирку, 
        // уже 145-ую счётом, идём на рекорд, ещё что нибудь, можете написать как любите маму и папу.
    }
}

Почти готово. Основные функции заданы. Написаны. Остаётся только пройтись в цикле по всем квартирам из json файла, да создать все их нашими функциями:

$countApparts = 0;
foreach($arrApartaments as $strHouseKey => $arrApartamentsStore ) {
	foreach($arrApartamentsStore as $strApartmentNumber => $arrApartament ) {
		if (!empty($strApartamentNumber)) {
			add_new_apartament($arrApartament, $strHouseKey, $strApartmentNumber, $arrImagesAccordance32);
			$countApparts++;
		}
	}
}
echo '<pre>Всего добавлено '.$countApparts.' новых квартир </pre>';

Всё.
Понравилось? — Поблагодарите в комментариях.
Поправить или дополнить можешь? — Моё почтение. Пиши.
Появились вопросы? — Добро пожаловать туда же. Всем рад, всех жду.
Пока.