Magento Image Information via SOAP API

Here’s a little snippet that’ll dump out image information for … well, as written here, every active SKU in your Magento Installation.

The reason for the for loop breaking SKUs down based on the first letter is to keep from blowing up my PHP memory with too many products. Your mileage may vary.

try {
    $proxy = new SoapClient('http://magento.store.url/index.php/api/?wsdl');
    $sid = $proxy->login('username', '********');
}
catch( Exception $e ) {
    echo "Can't establish connection: " . $e->getMessage() . "\n";
    exit(-3);
}
 
for( $cat = 'A', $i=0; $i < 26; $i++, $cat++ ) {     $filter = array(         'status' => array( '='=>'1' ),
        'sku' => array( 'like' => $cat.'%' )
    );
    echo $cat.".";
    try {
        $productList = $proxy->call( $sid, 'product.list', array($filter) );
    }
    catch( Exception $e ) {
        echo "Can't retrieve product listing: " . $e->getMessage() . "\n";
        exit(-4);
    }
    foreach( $productList as $prodInfo ) {
        echo $prodInfo['sku'] . "\t" . $prodInfo['name'] . "\n";
        $media = $proxy->call($sid, 'product_media.list', $prodInfo['sku']);
        print_r($media);
    }
}
exit(0);

Magento Customer Passwords

Recently I had occasion to export some customer data and use it in another system. This way I don’t need 2 different sign-ons for an external CMS.

Magento stores the hashed password with its salt. You can use this code as a guide to checking Magento customer passwords outside (well, inside, too, I guess…) Magento. This is a php command line script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* Substitute appropriate Mage path: */
require_once( "../../app/Mage.php" );
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
 
/* argv[1] is assumed to be the email address in our program
 * argv[2] is the password
 */
$customer = Mage::getModel('customer/customer')->setWebsiteId(Mage::app()->getStore()->getWebsiteId())->loadByEmail($argv[1]);
echo "Customer ID: " . $customer->getId() . "\n";
 
/* Load in the password_hash: */
$hash_in_db = $customer->getPasswordHash();
echo "Hashed password as stored in the database: ". $hash_in_db . "\n";
 
/* The hashed password is stored as:
 *      hash.':'.salt
 * We explode at the ':' such that hash is in element 0, salt is in element 1
 */
$a = explode(':', $hash_in_db);
$hash = $a[0];
$salt = $a[1];
 
/* Generate a hash based on what was passed in at argv[2] */
$password_in = $argv[2];
$computed_password =  md5($salt . $password_in);
 
echo "Database=[$hash]  Computed=[$computed_password]\n\n";
 
/* Well, did it match? */
echo ( $computed_password === $hash ) ? "BOOM-MATCH!\n" : "*bbzzt* nope\n";
exit(0);

Thank you, unexpected[it]!

Magento: “An error occurred while saving the URL rewrite”

Working on a new Magento store, I got this error while rebuilding the Catalog URL Rewrites index:

! An error occurred while saving the URL rewrite

After the usual Google searches, I came across this post, which proved to be the answer to my problem: http://www.magentocommerce.com/boards/main.php/viewthread/198534/#t334426

Turns out there’s a conflict introduced by the installation of the osCommerce Migration Extension. However, the post wasn’t exactly clear on which code to change, so I figured I’d post a little more on the issue.

First, I moved all of the osCommerce code out of app/code/core/Mage and into app/code/local/Mage. I should point out, though, that I had already done that even before I ran into this problem because I ran into several issues with the import process that I was (cautiously) optimistic that I could actually fix, as well as several things that I wanted to change (for example, my import source had categories that contained embedded HTML I wished to strip out – and I had several thousand categories …)

The file we need to edit is Mage/Oscommerce/Model/Mysql4/Catalog/Url.php. In that file, we find the Mage_Oscommerce_Model_Mysql4_Catalog_Url, which extends Mage_Catalog_Model_Resource_Eav_Mysql4_Url:

class Mage_Oscommerce_Model_Mysql4_Catalog_Url extends Mage_Catalog_Model_Resource_Eav_Mysql4_Url
{
    protected function _getCategories($categoryIds, $storeId = null, $path = null)
    {
        $isActiveAttribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_category', 'is_active');
        $categories = array();

Basically, all the post wants us to do is short circuit the _getCategories function by returning its parents’ version instead.

class Mage_Oscommerce_Model_Mysql4_Catalog_Url extends Mage_Catalog_Model_Resource_Eav_Mysql4_Url
{
    protected function _getCategories($categoryIds, $storeId = null, $path = null)
    {
        return parent::_getCategories();
 
        $isActiveAttribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_category', 'is_active');
        $categories = array();

Note: Please don’t confuse this with a “good, permanent” solution. This is a nice way to be able to continue to refine my import using osCommerce Migration, and be minimally invasive. Once I have this thing working properly, I will simply remove the osCommerce Migration Extension from my site – once I’m live, I won’t need it any more.

Magento API: Select Orders by Date Range

Here’s snippet of code I was able to use to get all of yesterday’s orders. You can see more variations for the use of DateTime here. I also wrote a front end to allow a selection of dates, but you can figure that out. This was the hard part to me. How the heck do I pass the from and to dates to Magento’s sales_order.list call?

Also note that Magento internally store dates and times in UTC. So I have to convert to that offset to get my orders in my local time. time_local_to_utc is a little function I wrote to do that. Conversely, time_utc_to_local converts it back. (Code later in this post).

Remember, much of the code I publish here is very specific for a particular implementation. This code is designed to be able to pull back all payment information into a spreadsheet for an accounting department. This is not a generic solution to a generic Magento problem. Rather, I intended this to help other folks get a start on pulling back orders by date!

$day = new DateTime();
$day->modify( "-1 day" );
 
$fromDate = $day->format("Y-m-d 00:00:00");
$toDate = $day->format("Y-m-d 23:59:59" );
 
$utc_from=time_local_to_utc($fromDate);
$utc_to=time_local_to_utc($toDate);
 
$proxy = new SoapClient('YOURSITE/api/soap/?wsdl');
$sessionId = $proxy->login('YUOUR_LOGIN', 'YOUR_PASS');
$order_headers = $proxy->call($sessionId, 'sales_order.list',
        array(
            array(
                'created_at'=>
                    array(
                        'from'=>$utc_from,
                        'to'=>$utc_to
                    )
                )
            )
        );
$howManyOrders = count($order_headers);

Here are the 2 time functions. They could certainly be made more general and really should be refactored (pass in “to” and “from” zones…), but you get the idea. And maybe there’s something to be said for the inline main code being simple to read.

function
time_local_to_utc($date) {
    $dt = new DateTime($date, new DateTimeZone("America/New_York"));
    $dt->setTimezone(new DateTimeZone("UTC"));
    return( $dt->format("Y-m-d H:i:s") );
}
 
function
time_utc_to_local($date) {
    $dt = new DateTime($date, new DateTimeZone("UTC"));
    $dt->setTimezone(new DateTimeZone("America/New_York"));
    return( $dt->format("Y-m-d H:i:s") );
}