Usando o Shell Script do Magento para Importar Opções de Atributos por Arquivos CSV

Olá pessoal,

Estes dias me deparei com uma tarefa simples, porém bem corriqueira para quem trabalha com atributos que possuam muitas opções no Magento.

O que eu precisava era basicamente importar em torno de 1.500 novas opções de um atributos específico que vou denominar aqui como atributos marcas. Com isto em mente logo pensei se importava diretamente pelo banco ou se criava uma forma mais intuitiva e com a possibilidade de reutilização pelo cliente. Foi aí que cheguei a uma conclusão: deveria utilizar os shell scripts que o Magento disponibiliza para tarefas corriqueiras como esta.

Vamos lá, a idéia é bem simples: você tem um arquivo .csv (separado por vírgulas, por exemplo) com todas as opções de um certo atributo, você sobe este arquivo em uma pasta dentro do projeto no servidor, roda um comando shell e simplesmente “BOOOOOMMM”, todas as opções daquele atributo foram importadas com sucesso!

Basicamente o que você precisa fazer é criar um arquivo shell script no diretório shell do Magento e fazer o upload do arquivo .csv para algum diretório do servidor.

Criando o arquivo shell script

Crie o seguinte arquivo shell/attributeOptions.php com o seguinte código fonte:

[php]
<?php

require_once realpath(dirname(__FILE__)) . ‘/abstract.php’;

/**
* Class Mage_Shell_AttributeOptions
*
* This shell script is provided to import any product attribute option.
* File must be have .csv extension
*
* @author Tiago Sampaio <tiago@tiagosampaio.com>
*/
class Mage_Shell_AttributeOptions extends Mage_Shell_Abstract
{
/** @var string */
protected $_attributeCode = null;
/** @var Mage_Catalog_Model_Resource_Eav_Attribute */
protected $_attribute = null;
/** @var int */
protected $_entityTypeId = null;
/** @var array */
protected $_options = array();

/**
* Constructs the class
*/
public function __construct()
{
parent::__construct();
$this->_entityTypeId = Mage::getModel(‘eav/entity’)->setType(‘catalog_product’)->getTypeId();
}

/**
* Run script
*/
public function run()
{
$this->_attributeCode = $this->getArg(‘a’);
if (empty($this->_attributeCode)) {
$this->_attributeCode = $this->getArg(‘attribute’);
}

if (empty($this->_attributeCode)) {
Mage::throwException("Attribute code needs to be passed as a param like ‘-a’ or ‘–attribute’.");
}

$this->_initAttribute();
$this->_initFileOptions();

/** @var Mage_Eav_Model_Entity_Setup $setup */
$setup = Mage::getModel(‘eav/entity_setup’, ‘core_setup’);
$setup->addAttributeOption(array(
‘attribute_id’ => $this->_attribute->getId(),
‘values’ => $this->_options
));

}

/**
* Initialize options in the file.
*
* @throws Mage_Core_Exception
*/
protected function _initFileOptions()
{
$filePath = $this->_getFilePath();

$row = 0;
$handle = fopen($filePath, ‘r’);
while (($data = fgetcsv($handle, 1000, $this->_getSeparator())) !== false) {
$label = trim($data[0]);

/**
* If the $this->_attributeCode variable is empty then we try to get it from first row of the file.
*/
if (empty($this->_attributeCode)) {
if ($row == 0) {
$this->_attributeCode = $label;

$this->_initAttribute();

$row++;
continue;
}
}

$index = $row * 10;
$this->_options[$index] = $label;

$row++;
}

fclose ($handle);
}

/**
* Initialize the attribute that will be updated.
*
* @param null $attributeCode
*/
protected function _initAttribute($attributeCode = null)
{
if (empty($attributeCode)) {
$attributeCode = $this->_attributeCode;
}

$this->_attribute = $this->_getAttribute($attributeCode);
}

/**
* Load model by attribute ID or code
*
* @param integer|string $attribute
* @return Mage_Catalog_Model_Resource_Eav_Attribute
*/
protected function _getAttribute($attribute)
{
$model = Mage::getResourceModel(‘catalog/eav_attribute’)->setEntityTypeId($this->_entityTypeId);

if (is_numeric($attribute)) {
$model->load(intval($attribute));
} else {
$model->load($attribute, ‘attribute_code’);
}

if (!$model->getId()) {
Mage::throwException("The attribute ‘{$attribute}’ does not exist.");
}

return $model;
}

/**
* Get the file path that will be read.
*
* @return string
*
* @throws Mage_Core_Exception
*/
protected function _getFilePath()
{
$filePath = realpath($this->getArg(‘f’));

if (empty($filePath)) {
$filePath = realpath($this->getArg(‘file’));
}

if (empty($filePath)) {
$varDir = Mage::getBaseDir(‘var’);
$mainDir = $varDir . DS . ‘import’ . DS . ‘attribute’ . DS . ‘options’ . DS;
$filePath = $mainDir . $this->_attributeCode . ‘.csv’;
}

if (!file_exists($filePath)) {
Mage::throwException("File ‘{$filePath}’ does not exist.");
}

return $filePath;
}

/**
* Get the field separator for the CSV file.
*
* @return string
*/
protected function _getSeparator()
{
$separator = $this->getArg(‘s’);

if (empty($separator)) {
$separator = $this->getArg(‘separator’);
}

if (empty($separator)) {
$separator = ‘,’;
}

return $separator;
}

/**
* Retrieve Usage Help Message
*
*/
public function usageHelp()
{
return <<<USAGE
Imports attribute options to any attribute that supports options (select and multi-select) based on a CSV file.

Usage: php attributeOption.php –attribute [attribute code] [-f /relative/path/to/file.csv]

-f | –file Sets the file that will be imported.
-a | –attribute Sets the attribute code
-s | –separator Sets the field separator for csv files.
help This help

Note: if file is not passed as argument then a file with the name ‘[attribute code].csv’ needs to be at:
[Magento Root]/var/import/attribute/options/[attribute code].csv

USAGE;
}

}

$shell = new Mage_Shell_AttributeOptions();
$shell->run();

[/php]

Ok! Vamos aos detalhes do script. Vou detalhar por método, ok?

Primeiramente, para facilitar nosso trabalho, todo shell script deve extender a classe abstrata de shell script do Magento: Mage_Shell_Abstract. É exatamente isso que as primeiras linhas do arquivo fazem:

[php]
<?php

require_once realpath(dirname(__FILE__)) . ‘/abstract.php’;

/**
* Class Mage_Shell_AttributeOptions
*
* This shell script is provided to import any product attribute option.
* File must be have .csv extension
*
* @author Tiago Sampaio <tiago@tiagosampaio.com>
*/
class Mage_Shell_AttributeOptions extends Mage_Shell_Abstract
{
…….
}
[/php]

No final do arquivo instanciamos a classe Mage_Shell_AttributeOptions e chamamos o método run():

[php]

$shell = new Mage_Shell_AttributeOptions();
$shell->run();


[/php]

O método __construct() é iniciado na instância da classes e é responsável pela contrução da abstração da classe (bootstrap do Magento) e por inicializar a variável $_entityTypeId:

[php]

/**
* Constructs the class
*/
public function __construct()
{
parent::__construct();
$this->_entityTypeId = Mage::getModel(‘eav/entity’)->setType(‘catalog_product’)->getTypeId();
}


[/php]

Por sequência vem o método run() que é responsável por iniciar a execução da importação:

[php]

/**
* Run script
*/
public function run()
{
$this->_attributeCode = $this->getArg(‘a’);
if (empty($this->_attributeCode)) {
$this->_attributeCode = $this->getArg(‘attribute’);
}

if (empty($this->_attributeCode)) {
Mage::throwException("Attribute code needs to be passed as a param like ‘-a’ or ‘–attribute’.");
}

$this->_initAttribute();
$this->_initFileOptions();

/** @var Mage_Eav_Model_Entity_Setup $setup */
$setup = Mage::getModel(‘eav/entity_setup’, ‘core_setup’);
$setup->addAttributeOption(array(
‘attribute_id’ => $this->_attribute->getId(),
‘values’ => $this->_options
));
}


[/php]

Neste método primeiramente fazemos a verificação se o código do atributo foi passado por parâmetro na execução do script via terminal e, em caso negativo, um erro é retornado.
Em sequência chamamos o método _initAttribute() que é responsável por instanciar o resource model para o respectivo atributo:

[php]

/**
* Initialize the attribute that will be updated.
*
* @param null $attributeCode
*/
protected function _initAttribute($attributeCode = null)
{
if (empty($attributeCode)) {
$attributeCode = $this->_attributeCode;
}

$this->_attribute = $this->_getAttribute($attributeCode);
}

/**
* Load model by attribute ID or code
*
* @param integer|string $attribute
* @return Mage_Catalog_Model_Resource_Eav_Attribute
*/
protected function _getAttribute($attribute)
{
$model = Mage::getResourceModel(‘catalog/eav_attribute’)->setEntityTypeId($this->_entityTypeId);

if (is_numeric($attribute)) {
$model->load(intval($attribute));
} else {
$model->load($attribute, ‘attribute_code’);
}

if (!$model->getId()) {
Mage::throwException("The attribute ‘{$attribute}’ does not exist.");
}

return $model;
}


[/php]

Este método chama o método _getAttribute($attribute) passando como parâmetro o código do atributo em questão, que por sua vez retorna um objeto do tipo Mage_Catalog_Model_Resource_Eav_Attribute.

Voltando ao método run() o próximo método a ser chamado é o _initFileOptions() que é responsável por fazer a leitura do arquivo .csv, capturando o nome do arquivo com o método _getFilePath(), percorrendo linha a linha do arquivo utilizando o método _getSeparator() para capturar o separados de campos do arquivo e salvando todas as opções na variável _options:

[php]

/**
* Initialize options in the file.
*
* @throws Mage_Core_Exception
*/
protected function _initFileOptions()
{
$filePath = $this->_getFilePath();

$row = 0;
$handle = fopen($filePath, ‘r’);
while (($data = fgetcsv($handle, 1000, $this->_getSeparator())) !== false) {
$label = trim($data[0]);

/**
* If the $this->_attributeCode variable is empty then we try to get it from first row of the file.
*/
if (empty($this->_attributeCode)) {
if ($row == 0) {
$this->_attributeCode = $label;

$this->_initAttribute();

$row++;
continue;
}
}

$index = $row * 10;
$this->_options[$index] = $label;

$row++;
}

fclose ($handle);
}

/**
* Get the file path that will be read.
*
* @return string
*
* @throws Mage_Core_Exception
*/
protected function _getFilePath()
{
$filePath = realpath($this->getArg(‘f’));

if (empty($filePath)) {
$filePath = realpath($this->getArg(‘file’));
}

if (empty($filePath)) {
$varDir = Mage::getBaseDir(‘var’);
$mainDir = $varDir . DS . ‘import’ . DS . ‘attribute’ . DS . ‘options’ . DS;
$filePath = $mainDir . $this->_attributeCode . ‘.csv’;
}

if (!file_exists($filePath)) {
Mage::throwException("File ‘{$filePath}’ does not exist.");
}

return $filePath;
}

/**
* Get the field separator for the CSV file.
*
* @return string
*/
protected function _getSeparator()
{
$separator = $this->getArg(‘s’);

if (empty($separator)) {
$separator = $this->getArg(‘separator’);
}

if (empty($separator)) {
$separator = ‘,’;
}

return $separator;
}


[/php]

O interessante é que aqui, ao tentar capturar o nome do arquivo, primeiro é verificado se o arquivo foi passado como parâmetro na chamada do script, em caso positivo, o próprio nome passado é retornado, caso contrário, o nome que o script tentará buscar por padrão é o código do atributo com a extensão .csv dentro do diretório [Instalação do Magento]/var/import/attribute/options/. Levando em consideração que estamos trabalhando com o atributo marcas o caminho automático do arquivo ficaria: [Instalação do Magento]/var/import/attribute/options/marcas.csv.

Por último criei um método de ajuda para quem quer saber das opções disponíveis ao rodar o script. Basicamente as opções são -f (ou –file) para declarar o caminho do arquivo a ser importado, -a (ou –attribute) para declarar o código do atributo ao qual serão associados todos os valores e -s (ou –separator) para definir um separador de colunas específico de campos para o arquivo.

[php]

/**
* Retrieve Usage Help Message
*
*/
public function usageHelp()
{
return <<<USAGE
Imports attribute options to any attribute that supports options (select and multi-select) based on a CSV file.

Usage: php attributeOption.php –attribute [attribute code] [-f /relative/path/to/file.csv]

-f | –file Sets the file that will be imported.
-a | –attribute Sets the attribute code
-s | –separator Sets the field separator for csv files.
help This help

Note: if file is not passed as argument then a file with the name ‘[attribute code].csv’ needs to be at:
[Magento Root]/var/import/attribute/options/[attribute code].csv

USAGE;
}


[/php]

O arquivo a ser importado deve conter o seguinte aspecto:

Importando Opções de Atributos Via Shell Script

Bom galera, vou ficando por aqui. Espero que este post tenha te ajudado em suas tarefas rotineiras.

Grande abraço e até a próxima!

Tiago Sampaio

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s