Back in 2009, I made this post about my first multiple image import module. It was quite popular, and worked all the way up through Magento 1.4.x. With the release of 1.5, it no longer worked due to core changes Magento made.
We finally ran into a client who needed this functionality on 1.5, so I have re-factored the module, and added a couple new features. Instead of just packaging up the module though, like last time, I will walk you through how to create it yourself (so that hopefully some of you will learn a little more about how to create modules).
Note: This will not work on Magento 1.4.x and lower. For a version that works on 1.4.x and lower (which is a little more basic too), please see this post.
What does this module do?
- Imports multiple images (of course) in addition to the 3 main product images (image, small_image, thumbnail)
- Allows you to enable or disable (in the system config) excluding any main product images (image, small_image, thumbnail) from showing up in the gallery (the exclude checkbox)
- Allows you to specify the column name for your multiple images in the system config
How it works: When you do a product import, all you have to do is add a new column to your CSV with the header of your choice (which you can change in the system config under Catalog > Catalog > Import Image Options – By default it’s ‘additional_images’). In that column, you can have as many images as you’d like. Simply separate the image names with a semi-colon: ‘image1.jpg;image2.jpg;image3.jpg’. Just be sure you’ve put the images in /media/import/ like you would normally do.
Enough talk, let’s get to building the module. In my example, I’m using the ‘Prattski’ namespace for my module. You can certainly change that if you would like, just make sure to change it in ever place you see it.
Step 1
Create the file ‘/app/etc/modules/Prattski_ImageImport.xml’. This file tells Magento that your module exists, if it’s enabled or not, and where to find it.
<?xml version="1.0"?>
<config>
<modules>
<Prattski_ImageImport>
<active>true</active>
<codePool>local</codePool>
</Prattski_ImageImport>
</modules>
</config>
Step 2
Now we create our module directory. Create ‘/app/code/local/Prattski/ImageImport/’. This is where our module will reside. Inside that directory, create this file: ‘etc/config.xml’. This file does a number of things: It tells Magento which version your module is, defines your model namespace and directory, rewrites a core model with your own, defines your helper namespace and directory, and sets default values in the system config.
<?xml version="1.0"?>
<config>
<modules>
<Prattski_ImageImport>
<version>0.1.0</version>
</Prattski_ImageImport>
</modules>
<global>
<models>
<imageimport>
<class>Prattski_ImageImport_Model</class>
</imageimport>
<catalog>
<rewrite>
<convert_adapter_product>Prattski_ImageImport_Model_Catalog_Convert_Adapter_Product</convert_adapter_product>
</rewrite>
</catalog>
</models>
<helpers>
<imageimport>
<class>Prattski_ImageImport_Helper</class>
</imageimport>
</helpers>
</global>
<default>
<catalog>
<catalog>
<image_exclude_enabled>1</image_exclude_enabled>
<multiple_image_column_name>additional_images</multiple_image_column_name>
</catalog>
</catalog>
</default>
</config>
Step 3
Let’s create our module’s helper. This isn’t necessary, but always a good practice to include it, as it is required for locale stuff, and it’s a ready utility if ever needed. In our case, we’ll just create a blank helper. Create this file in your module directory ‘Helper/Data.php’:
<?php
/**
* Prattski Import Images Module
*
* @category Prattski
* @package Prattski_ImageImport
* @copyright Copyright (c) 2011 Prattski (http://prattski.com)
* @author Prattski (Josh Pratt)
*/
/**
* Helper
*
* @category Prattski
* @package Prattski_ImageImport
*/
class Prattski_ImageImport_Helper_Data extends Mage_Core_Helper_Abstract
{
}
Step 4
Now we will create our system config. This will give us the ability to turn on or off excluding the main product images, and specify the column in our CSV for the additional images. Note: In our config.xml file that we created, you’ll see the ‘default’ tag near the bottom. There we pre-populate our system config fields that we are creating here with data so that they aren’t blank upon module installation. Create this file in your module directory: ‘etc/system.xml’:
<?xml version="1.0"?>
<config>
<sections>
<catalog>
<groups>
<catalog>
<label>Import Image Options</label>
<frontend_type>text</frontend_type>
<sort_order>999</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<fields>
<image_exclude_enabled translate="label" module="imageimport">
<label>Enable Excluded Images</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<comment>If the product image is a main product image (image, small_image, or thumbnail), automatically check the exlcude checkbox.</comment>
</image_exclude_enabled>
<multiple_image_column_name translate="label" module="imageimport">
<label>Multiple Image Column Name</label>
<frontend_type>text</frontend_type>
<sort_order>20</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<comment>The header in your CSV where you will put your additional images to import. When importing multiple images, separate with a semi-colon: 'image1.jpg;image2.jpg;image3.jpg'</comment>
</multiple_image_column_name>
</fields>
</catalog>
</groups>
</catalog>
</sections>
</config>
Step 5
Finally, we’ll create the model that will rewrite the core module to add in our new functionality. You’ll see towards the bottom (at line 230) that I’ve added two code blocks. The first block will exclude (or not) based on the system configuration setting we setup. The second is what imports all the additional images. Create this file in your module directory ‘Model/Catalog/Convert/Adapter/Product.php’:
<?php
/**
* Prattski Import Images Module
*
* @category Prattski
* @package Prattski_ImageImport
* @copyright Copyright (c) 2011 Prattski (http://prattski.com)
* @author Prattski (Josh Pratt)
*/
/**
* Product Import Adapter
*
* @category Prattski
* @package Prattski_ImageImport
*/
class Prattski_ImageImport_Model_Catalog_Convert_Adapter_Product extends Mage_Catalog_Model_Convert_Adapter_Product
{
/**
* Save product (import)
*
* @param array $importData
* @throws Mage_Core_Exception
* @return bool
*/
public function saveRow(array $importData)
{
$product = $this->getProductModel()
->reset();
if (empty($importData['store'])) {
if (!is_null($this->getBatchParams('store'))) {
$store = $this->getStoreById($this->getBatchParams('store'));
} else {
$message = Mage::helper('catalog')->__(
'Skipping import row, required field "%s" is not defined.',
'store'
);
Mage::throwException($message);
}
}
else {
$store = $this->getStoreByCode($importData['store']);
}
if ($store === false) {
$message = Mage::helper('catalog')->__(
'Skipping import row, store "%s" field does not exist.',
$importData['store']
);
Mage::throwException($message);
}
if (empty($importData['sku'])) {
$message = Mage::helper('catalog')->__('Skipping import row, required field "%s" is not defined.', 'sku');
Mage::throwException($message);
}
$product->setStoreId($store->getId());
$productId = $product->getIdBySku($importData['sku']);
if ($productId) {
$product->load($productId);
}
else {
$productTypes = $this->getProductTypes();
$productAttributeSets = $this->getProductAttributeSets();
/**
* Check product define type
*/
if (empty($importData['type']) || !isset($productTypes[strtolower($importData['type'])])) {
$value = isset($importData['type']) ? $importData['type'] : '';
$message = Mage::helper('catalog')->__(
'Skip import row, is not valid value "%s" for field "%s"',
$value,
'type'
);
Mage::throwException($message);
}
$product->setTypeId($productTypes[strtolower($importData['type'])]);
/**
* Check product define attribute set
*/
if (empty($importData['attribute_set']) || !isset($productAttributeSets[$importData['attribute_set']])) {
$value = isset($importData['attribute_set']) ? $importData['attribute_set'] : '';
$message = Mage::helper('catalog')->__(
'Skip import row, the value "%s" is invalid for field "%s"',
$value,
'attribute_set'
);
Mage::throwException($message);
}
$product->setAttributeSetId($productAttributeSets[$importData['attribute_set']]);
foreach ($this->_requiredFields as $field) {
$attribute = $this->getAttribute($field);
if (!isset($importData[$field]) && $attribute && $attribute->getIsRequired()) {
$message = Mage::helper('catalog')->__(
'Skipping import row, required field "%s" for new products is not defined.',
$field
);
Mage::throwException($message);
}
}
}
$this->setProductTypeInstance($product);
if (isset($importData['category_ids'])) {
$product->setCategoryIds($importData['category_ids']);
}
foreach ($this->_ignoreFields as $field) {
if (isset($importData[$field])) {
unset($importData[$field]);
}
}
if ($store->getId() != 0) {
$websiteIds = $product->getWebsiteIds();
if (!is_array($websiteIds)) {
$websiteIds = array();
}
if (!in_array($store->getWebsiteId(), $websiteIds)) {
$websiteIds[] = $store->getWebsiteId();
}
$product->setWebsiteIds($websiteIds);
}
if (isset($importData['websites'])) {
$websiteIds = $product->getWebsiteIds();
if (!is_array($websiteIds) || !$store->getId()) {
$websiteIds = array();
}
$websiteCodes = explode(',', $importData['websites']);
foreach ($websiteCodes as $websiteCode) {
try {
$website = Mage::app()->getWebsite(trim($websiteCode));
if (!in_array($website->getId(), $websiteIds)) {
$websiteIds[] = $website->getId();
}
}
catch (Exception $e) {}
}
$product->setWebsiteIds($websiteIds);
unset($websiteIds);
}
foreach ($importData as $field => $value) {
if (in_array($field, $this->_inventoryFields)) {
continue;
}
if (is_null($value)) {
continue;
}
$attribute = $this->getAttribute($field);
if (!$attribute) {
continue;
}
$isArray = false;
$setValue = $value;
if ($attribute->getFrontendInput() == 'multiselect') {
$value = explode(self::MULTI_DELIMITER, $value);
$isArray = true;
$setValue = array();
}
if ($value && $attribute->getBackendType() == 'decimal') {
$setValue = $this->getNumber($value);
}
if ($attribute->usesSource()) {
$options = $attribute->getSource()->getAllOptions(false);
if ($isArray) {
foreach ($options as $item) {
if (in_array($item['label'], $value)) {
$setValue[] = $item['value'];
}
}
} else {
$setValue = false;
foreach ($options as $item) {
if ($item['label'] == $value) {
$setValue = $item['value'];
}
}
}
}
$product->setData($field, $setValue);
}
if (!$product->getVisibility()) {
$product->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE);
}
$stockData = array();
$inventoryFields = isset($this->_inventoryFieldsProductTypes[$product->getTypeId()])
? $this->_inventoryFieldsProductTypes[$product->getTypeId()]
: array();
foreach ($inventoryFields as $field) {
if (isset($importData[$field])) {
if (in_array($field, $this->_toNumber)) {
$stockData[$field] = $this->getNumber($importData[$field]);
}
else {
$stockData[$field] = $importData[$field];
}
}
}
$product->setStockData($stockData);
$mediaGalleryBackendModel = $this->getAttribute('media_gallery')->getBackend();
$arrayToMassAdd = array();
foreach ($product->getMediaAttributes() as $mediaAttributeCode => $mediaAttribute) {
if (isset($importData[$mediaAttributeCode])) {
$file = trim($importData[$mediaAttributeCode]);
if (!empty($file) && !$mediaGalleryBackendModel->getImage($product, $file)) {
$arrayToMassAdd[] = array('file' => trim($file), 'mediaAttribute' => $mediaAttributeCode);
}
}
}
/**
* Added code to enable/disable the exclude checkbox for main product images
*/
$_exclude = (Mage::getStoreConfig('catalog/catalog/image_exclude_enabled')) ? true : false;
$addedFilesCorrespondence = $mediaGalleryBackendModel->addImagesWithDifferentMediaAttributes(
$product,
$arrayToMassAdd, Mage::getBaseDir('media') . DS . 'import',
false,
$_exclude
);
/**
* End new code
*/
/**
* Added code to allow the import of additional product images
*/
$_column = Mage::getStoreConfig('catalog/catalog/multiple_image_column_name');
if ($importData[$_column]) {
$additionalImages = explode(';',$importData[$_column]);
foreach ($additionalImages as $image) {
if (!empty($image) && !$mediaGalleryBackendModel->getImage($product, $image)) {
$arrayToMassAdd2[] = array('file' => trim($image));
}
}
}
$addedFilesCorrespondence .= $mediaGalleryBackendModel->addImagesWithDifferentMediaAttributes(
$product,
$arrayToMassAdd2, Mage::getBaseDir('media') . DS . 'import',
false,
false
);
/**
* End new code
*/
foreach ($product->getMediaAttributes() as $mediaAttributeCode => $mediaAttribute) {
$addedFile = '';
if (isset($importData[$mediaAttributeCode . '_label'])) {
$fileLabel = trim($importData[$mediaAttributeCode . '_label']);
if (isset($importData[$mediaAttributeCode])) {
$keyInAddedFile = array_search($importData[$mediaAttributeCode],
$addedFilesCorrespondence['alreadyAddedFiles']);
if ($keyInAddedFile !== false) {
$addedFile = $addedFilesCorrespondence['alreadyAddedFilesNames'][$keyInAddedFile];
}
}
if (!$addedFile) {
$addedFile = $product->getData($mediaAttributeCode);
}
if ($fileLabel && $addedFile) {
$mediaGalleryBackendModel->updateImage($product, $addedFile, array('label' => $fileLabel));
}
}
}
$product->setIsMassupdate(true);
$product->setExcludeUrlRewrite(true);
$product->save();
return true;
}
}
Step 6
To recap all the files we have made:
- /app/etc/modules/Prattski_ImageImport.xml
- /app/code/local/Prattski/ImageImport/etc/config.xml
- /app/code/local/Prattski/ImageImport/etc/system.xml
- /app/code/local/Prattski/ImageImport/Helper/Data.php
- /app/code/local/Prattski/ImageImport/Model/Catalog/Convert/Adapter/Product.php
Test it out! You’ll be able to see if Magento recognizes your module by going to System > Configuration > Advanced. If you see the module in the list, Magento knows it’s there. You can also go to System > Configuration > Catalog > Catalog > Import Image Options to see the configuration options we setup there.
If all looks good, then you are ready for a test. Setup your CSV import file, including a new column called ‘additional_images’ (or whatever you changed it to in the system config) and populate it with images to import. When ready, go to System > Import/Export > Dataflow – Profiles and select the Import All Products and give it a go!
I hope you enjoyed learning how to build this module. If you find any issues or bugs, please let me know. Remember, this is for Magento 1.5.x and probably will not work on 1.4.x and lower.