Saturday, December 1, 2012

Ultimate module creator available on Magento connect

The Ultimate Module Creator is now available on Magneto connect: http://www.magentocommerce.com/magento-connect/catalog/product/view/id/15449/s/ultimate-modulecreator-8949/

You can still follow closely it's development here: https://github.com/tzyganu/moduleCreator

A few details about it: http://marius-strajeru.blogspot.ro/p/ultimate-module-creator.html#v1-0-0

Can someone please send me a design for the 'extension logo'? I'm reaaaaaly lousy at Photoshop.
Thanks in advance.
Marius.

Friday, November 2, 2012

Product attribute with custom options

Magento gives you the possibility to create dropdown product attributes. The problem I have sometimes is that the options for all the dropdown attributes are all in one single table. So for each option I get a (somehow) random ID. There are cases that I really need to know the id of one specific option and I don't want to hard code it. The solution for this is to have a custom source model for your dropdown attributes. The following example creates an attribute called 'Provider' with custom options. For the purpose of the example I'm going to use the namespace 'Easylife' and my extension is going to be names 'Provider'.
Here are the files you need to create:
app/code/local/Easylife/Provider/etc/config.xml - the extension config file
<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Provider>
            <version>0.0.1</version>
        </Easylife_Provider>
    </modules>
    <global>
        <models>
            <provider>
                <class>Easylife_Provider_Model</class>
            </provider>
        </models>
        <resources>
            <easylife_provider_setup>
                <setup>
                    <module>Easylife_Provider</module>
                    <class>Mage_Catalog_Model_Resource_Eav_Mysql4_Setup</class>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </easylife_provider_setup>
        </resources>
    </global>
</config>
app/code/local/Easylife/Provider/sql/easylife_provider_setup/mysql4-install-0.0.1.php - this will add an attribute named 'Provider' to your product
<?php
$this->startSetup();
//if you want to add the attribute to the category instead of the product change on the line below 'catalog_product' to 'catalog_category'
$this->addAttribute('catalog_product', 'provider', array(
        'group'                => 'General',
        'type'              => 'int',
        'backend'           => '',
        'frontend_input'    => '',
        'frontend'          => '',
        'label'             => 'Provider',
        'input'             => 'select',
        'class'             => '',
        'source'            => 'provider/attribute_source_provider',
        'global'             => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,//can be SCOPE_WEBSITE or SCOPE_STORE
        'visible'           => true,
        'used_in_product_listing' =>true,//can also be false
        'frontend_class'     => '',
        'required'          => false,//can be true
        'user_defined'      => true,
        'default'           => '',
        'searchable'        => false,//can be true
        'filterable'        => false,//can be true
        'comparable'        => false,//can be true
        'visible_on_front'  => false,//can be true
        'unique'            => false,
        'position'            => 60,//put any number here
    ));
$this->endSetup();
app/code/local/Easylife/Provider/Model/Attribute/Source/Provider.php - this is the source of you attribute
<?php
class Easylife_Provider_Model_Attribute_Source_Provider extends Mage_Eav_Model_Entity_Attribute_Source_Abstract{
    protected $_options = null;
    public function getAllOptions($withEmpty = false){
        if (is_null($this->_options)){
            $this->_options = array();
                       //$this->_options[] = array('label'=>'HERE GOES THE LABEL', 'value'=>'HERE GOES THE VALUE');
            //as example
            $this->_options[] = array('label'=> $this->__('Provider 1'), value=>1);
            $this->_options[] = array('label'=> $this->__('Provider 2'), value=>2);
            $this->_options[] = array('label'=> $this->__('Provider 3'), value=>3);
        }
        $options = $this->_options;
        if ($withEmpty) {
            array_unshift($options, array('value'=>'', 'label'=>''));
        }
        return $options;
    }
    public function getOptionText($value)
    {
        $options = $this->getAllOptions(false);

        foreach ($options as $item) {
            if ($item['value'] == $value) {
                return $item['label'];
            }
        }
        return false;
    }
    public function getFlatColums()
    {
        $attributeCode = $this->getAttribute()->getAttributeCode();
        $column = array(
            'unsigned'  => false,
            'default'   => null,
            'extra'     => null
        );

        if (Mage::helper('core')->useDbCompatibleMode()) {
            $column['type']     = 'int(10)';
            $column['is_null']  = true;
        } else {
            $column['type']     = Varien_Db_Ddl_Table::TYPE_SMALLINT;
            $column['length']   = 10;
            $column['nullable'] = true;
            $column['comment']  = $attributeCode . ' column';
        }

        return array($attributeCode => $column);
    }
    public function getFlatUpdateSelect($store)
    {
        return Mage::getResourceModel('eav/entity_attribute')
            ->getFlatUpdateSelect($this->getAttribute(), $store);
    }
}
app/etc/modules/Easylife_Provider.xml - the module declaration
<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Provider>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </Easylife_Provider>
    </modules>
</config>
That's it. Clear the cache and try again. If you choose to include the attribute in the product listing or if you made it filtrable
'used_in_product_listing' =>true,
...
'filterable'        => false
then you need to rebuild your indexes. Now if you need to get all the products that belong to 'Provider 1' all you need to do is this:
$products = Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('provider', 1);
Enjoy, Marius.

Tuesday, October 30, 2012

Add a new field to the store view

Here is the clear way to add a new field on the store view in Magento.
In order to do this you should create a new extension.
For the purpose of this demo I will use the 'namespace' Easylife. replace it with your own namespace if you want.
The extension is named Core because I'm overriding something in the core. You can name it how ever you want. Here are the files that need to be created:

app/code/local/Easylife/Core/etc/config:
<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Core>
            <version>0.0.1</version>
        </Easylife_Core>
    </modules>
    <global>
        <resources>
            <easylife_core_setup>
                <setup>
                    <module>Easylife_Core</module>
                </setup>
            </easylife_core_setup>
        </resources>
        <blocks>
            <adminhtml>
                <rewrite>
                    <system_store_edit_form>Easylife_Core_Block_Adminhtml_System_Store_Edit_Form</system_store_edit_form>
                </rewrite>
            </adminhtml>
        </blocks>
    </global>
</config>
app/code/local/Easylife/Core/sql/easylife_core_setup/mysql4-install-0.0.1.php
<?php 
$this->startSetup();
$this->run("ALTER TABLE `{$this->getTable('core/store')}` ADD COLUMN `custom` VARCHAR(255)");//change the name and type of the column if you need.
$this->endSetup();
app/code/local/Easylife/Core/Block/Adminhtml/System/Store/Edit/Form.php - this overrides the admin block.
<?php 
class Easylife_Core_Block_Adminhtml_System_Store_Edit_Form extends Mage_Adminhtml_Block_System_Store_Edit_Form{
    protected function _prepareForm(){
        parent::_prepareForm();
        if (Mage::registry('store_type') == 'store'){
            $storeModel = Mage::registry('store_data');
            $fieldset = $this->getForm()->getElement('store_fieldset');
            $fieldset->addField('custom', 'text', array(
                    'name'      => 'store[custom]',
                    'label'     => Mage::helper('core')->__('Custom'),
                    'required'  => true,//or false
                    'value'        => $storeModel->getData('custom') 
                ));
        }
        return $this;
    }
}
In order to activate your module you need this file
app/etc/modules/Easylife_Core.xml
<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Core>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Core/>
            </depends>
        </Easylife_Core>
    </modules>
</config>
Enjoy.
Marius.

Friday, September 28, 2012

Module creator

After a few months of development, not because of the work volume, but because of the lack of time, I finally did it.
I have created my own module creator.
For an (almost) complete description see this: Ultimate Module creator Let me know what you think.

Marius.

Monday, August 27, 2012

How to make CMS pages available only to logged in customers

Hello
Here is a possible solution on how to make cms mages available only to logged in customers.
Override the Mage_Cms_PageController. Here is a how to do it: http://www.extensionprogrammer.com/skin/frontend/default/dermodpro/pdf/magento-cheatsheet.pdf

Now add the following method in your new controller

public function preDispatch(){
		$restrictedIdentifiers = array('about-magento-demo-store');
		$pageId = $this->getRequest()
            ->getParam('page_id', $this->getRequest()->getParam('id', false));
        $page = Mage::getModel('cms/page')->load($pageId);
    	parent::preDispatch();
    	if (in_array($page->getIdentifier(), $restrictedIdentifiers)){
	    	if (!Mage::getSingleton('customer/session')->authenticate($this)) {
	            $this->setFlag('', 'no-dispatch', true);
	        }
    	}
    }
That's it. Enjoy. I know it's not 100% clean solution. If you want the clean solution, add a new field for the cms_page table (let's call it 'password_protect'), add the field in the admin form. After doing this the code above becomes:
public function preDispatch(){
		$pageId = $this->getRequest()
            ->getParam('page_id', $this->getRequest()->getParam('id', false));
        $page = Mage::getModel('cms/page')->load($pageId);
    	parent::preDispatch();
    	if ($page->getPasswordProtect()){
	    	if (!Mage::getSingleton('customer/session')->authenticate($this)) {
	            $this->setFlag('', 'no-dispatch', true);
	        }
    	}
    }
Marius.

Monday, August 13, 2012

Tips & tricks to speed up Magento

I've found on LikedIn a comment about tips and tricks for speeding up Mangeto.
It seams like a good list to consider when having troubles with the site speed.
The list was provided by Pieter Pabst.
Here it goes:


- Use KeepAlive in Apache.
- Make sure the query buffer on your MySQL instance is large enough.
- Hosting MySQL on another (V)Machine will help
- Migrate .htaccess to you apache config and turn off AllowOverride.
- Use some kind of opcode cashing, like APC of even better, Zend Server
- Use mod_deflate
- Use mod_expires
- Put var/cache in a /dev/ramdisk of tmpfs
- Use the CDN option, or at least something that is accessible via another DNS name (like content.yoursite of static.yoursite) as browsers are limited to X connections to a certain host at one time (use the URLs in System -> Configuration -> Web to accomplish this), but be aware of the fall-back mechanism for skins. Also beware that the upload script in the backend does not like it when it's hosted on another hostname (potential XSS). Your uploads will fail. To overcome this, make sure the backend uses local JS and the frontend uses CDN. To accomplish this use the configuration scope in the Web config section (default to local, storeviews to CDN).
- Move /media to another server
- After putting var/cache in ramdisk, put the rest somewhere with fast I/O. Like SAS disks or a SSD. Don't put it on something like iSCSI or SATA. Put media on cheap I/O like SATA, preferably on another server, or something assebeble via another hostname.
- If you have the option, don't use apache at all. Try ngnix.

Magento:
- Use flat catalog (System -> Configuration -> Catalog -> Frontend)
- Enable cache (System -> Cache management)
- Compile Magento when opcode caching is no option (System -> Tools -> - Compiling)

Hacking
- Disabling the fall-back mechanism for themes / skins and merging the base and your current into one will save you a lot of IO
- Try to code your modules in a way they can use cache
- Try full page caching, all tough you will have to buy a license for products like Zend Server, this will boost performance.
- Try cashing frequently used queries (in a request kind of context I mean) in f.e.a arrays. But beware of the memmory limit. Unset things of necessary.

Webdesign
- Use sprites
- Combine everything in one CSS file or use one of the extensions already mentioned.
- The same goes for javascript
- Use robots.txt to stop robots from crawling certain URLs with producs they probably already indexed trough other URLs. It will just cause additional load.

After you've done all that, you site needs to be immense popular to require load balancing of MySQL clustering.

Friday, April 20, 2012

Retrieve bestsellers

Here is how you can retrieve a list of bestsellers
$collection = Mage::getResourceModel('sales/report_bestsellers_collection')
            ->setModel('catalog/product')
            ->addStoreFilter(Mage::app()->getStore()->getId())//if you want the bestsellers for a specific store view. if you want global values remove this
            ->setPageSize(5)//se there the number of products you want returned
            ->setCurPage(1);
foreach ($collection as $_product){
    $realProduct = Mage::getModel('catalog/product')->load($_product->getProductId());
    //do something with $realProduct;
}
Make sure you refresehd the statistic for bestsellers. Reports->Refresh statistics. Select refresh lifetime statistics for bestsellers. Enjoy.

Wednesday, March 7, 2012

Add files to products - no extension required

Here is how you can add files to products without using an extension. It can be useful for adding specification files, pdf catalogs or any other files you want. Put all your files in one folder let's say (media/productfiles/). Now in the product description you just add a simple link to that file using the WYSIWYG editor. If you don't want to use the description field for this you can create a new attribute (let's call it 'attachment') type 'Textarea', scope 'Store view' (in case you want to have different files for different languages), 'Enable WYSIWYG' Yes, 'Allow HTML Tags on Frontend' Yes. If you want to show the link(s) to file(s) in the product list not only the product details page set 'Used in Product Listing' to 'Yes'. If not set it to 'No'. Now add your new attribute to the attribute set that you need and start editing products. Add links to your files just like described above, but use the 'Attachment' attribute instead of Description. now all you have to do is edit the app/design/frontend/{interface}/{theme}/template/catalog/product/view.phtml and add this line where ever you want the links to appear:
<?php echo $this->getProduct()->getAttachment();?>
If you want to add the links to the list of products also edit app/design/frontend/{interface}/{theme}/template/catalog/product/list.phtml and add this piece of code inside the foreach loop for the products:
<?php echo $_product->getAttachment();?>
Cheers, Marius.

Thursday, February 16, 2012

Create admin user by code

Here is a piece of code that will allow you to create admin users. It's really useful if you forget the admin password: Create a file called adminuser.php on the same level as index.php (in the root of the application) with the following content:
<?php
error_reporting(E_ALL | E_STRICT);
$mageFilename = 'app/Mage.php';
require_once $mageFilename;
Mage::setIsDeveloperMode(true);
ini_set('display_errors', 1);

umask(0);
Mage::app('default');//if you changed the code for the default store view change it here also
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
$username = 'yourUsername';//desired username
$firstname = "Firstname";//desired firstname
$lastname = "Lastname";//desired lastname
$email = "email@example.com";//desired email
$pass = 'yourPaSSWordHere';//desired password
$user = Mage::getModel('admin/user')->load($username, 'username');
if ($user->getId()){
 echo "User {$username} already exists";
 exit;
}
$user->setUsername($username)
  ->setFirstname($firstname)
  ->setLastname($lastname)
  ->setEmail($email)
  ->setPassword($pass)
  ;
$result = $user->validate();
if (is_array($result)){
 foreach ($result as $res){
  echo $res."\n";
 }
 exit;
}
try{
 $user->setForceNewPassword(true);
 $user->save();
 $user->setRoleIds(array(1))->saveRelations();
 echo "User {$username} was created";
 exit;
}
catch (Exception $e){
 echo $e->getMessage();
}
Now call in you browser this url www.mysite.com/adminuser.php. You should see a message on the screen with the result of your action. Cheers, Marius.

Friday, February 10, 2012

Keep products in the wishlist after adding them to cart.

Here is an interesting question I found on the Magento forum:
Leave Wishlist as it is after adding item to shopping cart?

Here is what you can do.
Override the wishlist index controller (app/code/core/Mage/Wishlist/controllers/IndexController.php) or edit it if you don't care about keeping the core clean (I recommend to keep it clean though)
and inside the method cartAction() there is this line:
$item->addToCart($cart, true);
The second parameter of the method addToCart means 'Remove from wishlist'.
You can change the second parameter to false or remove it completely - it defaults to false.

Marius.