Inchoo » Magento http://inchoo.net Magento Design and Magento Development Professionals - Inchoo Fri, 24 Oct 2014 07:45:02 +0000 en-US hourly 1 http://wordpress.org/?v=4.0 Frontend performance: why should you care? http://inchoo.net/ecommerce/magento-frontend-optimization/ http://inchoo.net/ecommerce/magento-frontend-optimization/#comments Thu, 23 Oct 2014 10:47:54 +0000 http://inchoo.net/?p=23574 Speed is the most important feature of any online store. If it is too slow, people will just not use it. It may have great products, low prices, the most beautiful interface, but if it takes forever to load, people will just go away. In the offline world, we often have to wait. Line in...

The post Frontend performance: why should you care? appeared first on Inchoo.

]]>
Speed is the most important feature of any online store. If it is too slow, people will just not use it. It may have great products, low prices, the most beautiful interface, but if it takes forever to load, people will just go away.

In the offline world, we often have to wait. Line in the bank, security control at the airport, checkout line at the grocery store… pick your favorite. “I had such a great time waiting in the line for 3 hours” – said no one, ever…  People hate waiting.

On the other hand, in the online world, we have ability to serve thousands of customers in the same time. Immediate service. And that’s exactly what people expect. They expect websites to load in 2 seconds or less. Almost half of customers will abandon the website if it takes more than 3 seconds to load, and most of them will never come back. (Source: Kissmetrics)

Bad performance = Bad business

It is obvious that loading time has direct impact on customer satisfaction, conversions, and in the bottom line, on sales. It is proven that 1 second delay in loading time results in 7% less conversions. Amazon discovered that 100ms of latency costs them 1% less revenue. On the other hand, statistics show that websites are getting slower every year. It is almost like they are losing money on purpose. Why is that?

slow

Graphic credit: radware.com

Websites are getting fatter

Websites are getting more complex with every year. We also have bigger screens and bigger images, and that is the main reason why web pages are getting “fatter”. Namely, around 60% of page weight consists of images.

chart

It is a Mobile-first world

I already stated that screens are getting bigger. Well, kind of… They are getting smaller at the same time. Smartphone and tablet usage is climbing sky-high. We can now say that 55% of online traffic these days come from mobile. And mobile users expect sites to load as fast or even faster than on their desktops. And that’s hard to achieve due network constraints.

Although the mobile networks are getting faster, web browsing experience on 4G did not dramatically change compared to 3G. As it appears, bandwidth is not the biggest constraining factor – it’s the latency. Simply put, latency is time needed for a request to go from the browser to the server and back. Latency is important on wired connections, so you can imagine how critical it is on mobile networks.

The mobile web is a whole different game, and not one for the better. If you are lucky, your radio is on, and depending on your network, quality of signal, and time of day, then just traversing your way to the internet backbone can take anywhere from 50 to 200ms+. From there, add backbone time and multiply by two: we are looking at 100-1000ms RTT range on mobile.

 

What to do?

So far we stated:

  1. Speed is important
  2. Users expect 2 seconds
  3. Mobile traffic is dominant
  4. Mobile web is slow

We have a problem here… So, what can we do to avoid this pittfall of bad performance? Follow these rules, and you’ll be on the right track…

Speed is a feature, treat it as a part of UX

Speed is directly responsible for conversion rates. It is your competitive advantage. A feature of your store… And it is equally or even more important than your fancy newsletter subscribe form or one-step checkout. If your website is slow, no one will use it. Period.

Treat performance as a part of UX design, plan it in advance and make sure you secure a budget for it.

Speed is the most important feature. If your application is slow, people won’t use it. I see this more with mainstream users than I do with power users. I think that power users sometimes have a bit of sympathetic eye to the challenges of building really fast web apps, and maybe they’re willing to live with it, but when I look at my wife and kids, they’re my mainstream view of the world. If something is slow, they’re just gone.

Fred Wilson: “The 10 Golden Principles of Successful Web Apps”

Mobile first, content out workflow

Always start with the content. Structure it to create focus and hierarchy. Putting your content on a small screen, where your screen real estate is limited, will make you think twice what is most important and what deserves to be on the screen. It will give you an opportunity to reevaluate what content or functionality is necessary, and not just for the mobile version.
Performance will also benefit from mobile first approach. Think of it as building a house. You start with the foundation, and build on top of it.

Start with a baseline mobile experience and enhance with a media query cascade that starts from small screens and adapts design as screens get bigger.

mobile-first

Set a performance budget

We are not talking about money here. Performance budget is a clever idea introduced by Tim Kadlec.

A performance budget is just what it sounds like: you set a “budget” on your page and do not allow the page to exceed that.

Your “budget” may be number of requests, page weight, loading time…  It is important to declare your goals early in the planning phase. If you set a goal to have a homepage under 500Kb and then desing a content slider with 5 full screen images, it is going to be impossible to achieve your goals.

Once you achieved your goals, you should stick to them. Next time you want to add something to the page, you need to make sure it doesn’t exceed the budget. If it is does, there are three options:

  • Optimize an existing feature or asset on the page.
  • Remove an existing feature or asset from the page.
  • Don’t add the new feature or asset.

Optimize!

This is the part you need to be religious about it. Squeeze the last Kb out of your website. The main goals of optimization are:

  • Reduce http requests
  • Reduce file sizes
  • Reduce latency
  • Optimize critical rendering path

There are numerous techniques to achive this goals, and we will cover them in our future blog posts. Stay tuned!

The post Frontend performance: why should you care? appeared first on Inchoo.

]]>
http://inchoo.net/ecommerce/magento-frontend-optimization/feed/ 2
Book Review – Mastering Magento Theme Design by Andrea Sacca http://inchoo.net/magento/book-review-mastering-magento-theme-design/ http://inchoo.net/magento/book-review-mastering-magento-theme-design/#comments Thu, 09 Oct 2014 10:50:27 +0000 http://inchoo.net/?p=23466 It is widely assumed that knowledge is power and that it has to be built upon every day. It takes a lot of time to put a person’s knowledge into a book. For the past few months I was looking for a new book in order to expand on my frontend knowledge. Due to lack...

The post Book Review – Mastering Magento Theme Design by Andrea Sacca appeared first on Inchoo.

]]>
It is widely assumed that knowledge is power and that it has to be built upon every day. It takes a lot of time to put a person’s knowledge into a book. For the past few months I was looking for a new book in order to expand on my frontend knowledge.

Due to lack of free time I usually read only particular sections of some book or I surf the Internet if I need a specific piece of information.

Long story short, just at that time Andrea Sacca published his new book which covers one of my favorite topics – Creating responsive Magento theme. As one of the frontend developers at Inchoo, I had the opportunity to receive one of first copies of that book (and a copy for entire Inchoo). To return the favor to the author, we were asked to write a review of the book.

First impression

The cover page of the book shows something that I personally don’t like very much: “Responsive Magento themes using Bootstrap.” Nevertheless, I decided to give the author a chance.

The book is very useful for developers who have just started Magento career, such as myself. I have found some very practical information that will hopefully help me in my future career. I believe that many young developers will be able to find good tricks in the book. In addition to this, the book can also be a helpful reminder for developers who already have some experience in Magento development.

Book evaluation

As already mentioned, this book is focused on Bootstrap integration in Magento. To be honest, this approach doesn’t appeal to me in particular. It is adorable that frameworks will speed up development process but adding one more framework in the mix is not a clean and nice solution. Although it is nowadays all about speed and flexibility, we need to develop responsive, light and agile web stores. In such cases each part of the puzzle should be taken into consideration in order to make the users satisfied. And adding some frontend framework just seems as unnecessary added weight on already heavy Magento.

The book obviously has not changed my mind and I still believe that custom solutions are a better option, especially the ones based on SASS or LESS preprocessors which can speed your development process and provide good flexibility. If you have time to put a few mixings together, it can really help you keep your code on a “diet”. It is known that frameworks are not just CSS but if you need something else specific, you can always borrow a good solution from frameworks or internet without adding all the extra unnecessary code.

Moreover, if we take a look at new Magento 2 frontend ideas and solutions, it is clearly seen that new Magento frontend developers do not implement any frontend framework; instead they decide to go with custom solution established on LESS preprocessor.

Conclusion

To sum up, this book is very useful for fellow frontend developers who are starting to get their grips on Magento.

It contains several very useful chapters, and I’ll highlight three of them:

  • Adding Incredible Effects to Our Theme
  • Creating a Magento Widget
  • Creating a Theme Admin Panel

I would certainly recommend this book as an integral part of the learning process for junior frontend developer audience, but would also advise everyone to expand on this knowledge and try their own approaches when creating amazing responsive websites.

If you are interested, you can get your copy of this book here.

Regards and good luck with your responsive Magento projects!

The post Book Review – Mastering Magento Theme Design by Andrea Sacca appeared first on Inchoo.

]]>
http://inchoo.net/magento/book-review-mastering-magento-theme-design/feed/ 6
Magento without an IDE? Say hello to Sublime! http://inchoo.net/dev-talk/programming-magento-without-ide-say-hello-sublime/ http://inchoo.net/dev-talk/programming-magento-without-ide-say-hello-sublime/#comments Tue, 30 Sep 2014 09:45:00 +0000 http://inchoo.net/?p=23350 We are all aware of the Magento codebase size and its complexity. That is one of the reasons most people use full-fledged IDEs for Magento programming. Most answers regarding the “what IDE should I be using for Magento?” or “what is the best Magento development environment?” include big boys like Eclipse, NetBeans and PhpStorm. Since...

The post Magento without an IDE? Say hello to Sublime! appeared first on Inchoo.

]]>
We are all aware of the Magento codebase size and its complexity. That is one of the reasons most people use full-fledged IDEs for Magento programming. Most answers regarding the “what IDE should I be using for Magento?” or “what is the best Magento development environment?” include big boys like Eclipse, NetBeans and PhpStorm. Since you’ve already read the title, you may be wondering: “what can a text editor like Sublime Text offer me for my Magento development?”. Vanilla installation? Not much, but with the help of a few plugins, well… Keep reading and you just may be in for a treat.

Package manager

If you’ve ever used Sublime text you have probably stumbled upon its package manager. It is a great tool that lets you choose from hundreds of available plugins and install them in no time. This is the foundation for sublime customization, and you should install this immediately.

After you’ve installed it, you have access to hundreds of awesome plugins. But which ones should you choose? Which ones are good for Magento/PHP/general development? Here are the ones I use for my day-to-day work.

CTags

So, you’re accustomed to the “click to go to definition” functionality that most IDEs give you. Unfortunately Sublime text does not have this feature. But CTags is here to help. What it does is it generates an index file of language objects (eg. method names, class names etc.) and allows for them to be easily located. After you’ve installed it from the package manager (and read the installation instructions on github), you should build your tags. The way to do that is through “Find > CTags > Rebuild Tags” menu item, but I prefer a key bind. To add a key bind add this to your sublime-keymap file (Preferences > Key Bindings User):

[
  { "keys": ["alt+shift+c"], "command": "rebuild_tags" },
  { "keys": ["alt+shift+v"], "command": "navigate_to_definition"}
]

Now you can build your tags by pressing alt+shift+c, or any other key combination you want. After some time (usually ~30-40 seconds for Magento projects), CTags will build your tags and you can start to harness their power. How? Just place your text cursor on the class or method name, press the key combination for ‘navigate to definition’ command and the class will be automatically opened. As for the methods, if their name is unique, they will also be automatically opened, but if there are more methods with the same name, they will be shown in a list so you can choose where to go. CTags is a great package to have in order to improve your coding speed.

SublimeLinter

Don’t you hate it when you code for some time and refresh the browser only to find you have at least 5 syntax errors (missing semicolons or the infamous Paamayim Nekudotayim error)? “This would not happen if I was using Eclipse.”, you might say. Don’t give up yet, there is a cure for this problem too, and it’s called SublimeLinter. It supports many languages out of the box, including PHP so it is a great tool in our toolbelt. In order to make it work, you have to add a path to PHP installation in its configuration. In my case it was this:

"sublimelinter_executable_map":
{
  "php":"/usr/bin/php"
}

Try to make an error now, and sublime will greet you with a red error mark and the explanation of the error in the status bar.

sublime_2

XMLLinter

Magento uses a lot of XML. Before I found out about this plugin I used to spend a lot of time debugging even the silliest xml syntax errors in Sublime text.  It was really a pain. But fortunately we have a plugin for that too, XMLLinter. Missing the closing tag, misspelling tag name and other errors are a thing of the past, since we are now able to detect XML errors instantly.

So far so good.

PHP snippets

Sublime text already has a lot of PHP code snippets out of the box, but few more always come in handy: Additional PHP Snippets and PHP Completions Kit are good enough. One thing I should like to point is that I had some trouble with triggering the snippets in .phtml files. The solution was to change the “auto_complete_selector” default configuration value, ie. place this

"auto_complete_selector": "source, text"

into your ‘Preferences.sublime-settings’ file (Preferences > Settings – User), and it should work.
You want an “if” statement in your .phtml? No problem. Start typing.

sublime_4

Press enter and – poof:

sublime_5

XDebug

If you use a debugger while developing, you should look into packages that add support for xDebug. Possibly this or maybe this one.

Sublime-magento

This is a plugin that I have been contributing to, not really an original naming – sublime-magento, but it has some really nice features like:

  • inserting the proper class name on a simple key press (so you don’t have to write it yourself)
  • opening file path within quotes (especially useful for template paths in XML files)
  • creating a module (insert module name and it will be created with all the necessary files and configuration)
  • additional code snippets

 

With these packages in your arsenal, you are good to go. You have the power to tackle even the hardest Magento-related tasks. But let’s not wrap this up yet. We are developers, and we like to have a clean and neat code. Also, we like our development environment to look good (if not awesome), so here are few more plugins you should really look into because they provide for a much better programming experience in Sublime text.

AdvancedNewFile

AdvancedNewFile is a pretty neat plugin that allows you to easily create files within your project. No need to touch your mouse, just place a key bind. I have it like this:

{ "keys": ["shift+alt+n"], "command": "advanced_new_file_new"}

SidebarEnhancements

Out of the box sublime does not come with a lot of options in context menu of right click. In order to fix that, you should use SidebarEnhancements. It provides enhancements to the operations on sidebar of Files and Folders, and it’s really cool. Officially supported only for Sublime text 3, but there are some workarounds to make it work in ST2 also.

GoToDocumentation

If you are not really familiar with PHP standard library, or simply have trouble remembering the order of the parameters in a function call, you should consider using GoToDocumentation plugin. Place your cursor over the method name, press the designated key, and documentation page should open up in your browser, be it PHP, HTML, CSS or any other supported language.

Trailing Spaces

I really hate trailing spaces in code, and so should you.  With this plugin you will be able to detect and delete them in a matter of seconds. CAUTION: other developers in your team probably don’t care about this so you may start hating them too.

sublime_6

Code with trailing spaces

Git/Github packages

If you use git without a command line, you could use the Git and Github Tools plugins. Just search for them in package manager.

Syntax Highlight and themes

Even though the default syntax highlighting in Sublime is pretty good (monokai theme), you can always find something that suits you better. I really like the Phix color scheme and the SODA Dark theme.

There are lots of other tips and tricks for Sublime text and it would be really great to write about them too, but let’s leave that for some other time. You may have also noticed that these plugins (at least the ones from the beginning) were mostly backend related. Sublime has some nice plugins for frontend too like Emmet or jQuery. I recommend that you go through the list of packages in Package Manager and try some of them. It is really easy to add them, and remove, too.

NOTE: all plugins were tested and working on ST2, but they should work without problems in ST3 also.

The post Magento without an IDE? Say hello to Sublime! appeared first on Inchoo.

]]>
http://inchoo.net/dev-talk/programming-magento-without-ide-say-hello-sublime/feed/ 1
Magento Grid Serializer for Ajax Grids http://inchoo.net/magento/magento-grid-serializer-ajax-grids/ http://inchoo.net/magento/magento-grid-serializer-ajax-grids/#comments Mon, 22 Sep 2014 09:00:51 +0000 http://inchoo.net/?p=23268 In Magento selectable grids, each time after a grid operation is performed (whether it’s filtering the results, pagination or something else), the selected values get lost. In other words, Magento by default keeps only values selected after the grid is initialized in the beginning and loses them each time the grid gets reloaded by Ajax...

The post Magento Grid Serializer for Ajax Grids appeared first on Inchoo.

]]>
In Magento selectable grids, each time after a grid operation is performed (whether it’s filtering the results, pagination or something else), the selected values get lost. In other words, Magento by default keeps only values selected after the grid is initialized in the beginning and loses them each time the grid gets reloaded by Ajax call. Fortunately, Magento has a built-in library called Grid Serializer to solve this issue.

To make it simple, Grid Serializer is nothing more than a container made of two parts: a grid and a hidden input field that are distinctly separated. Hidden input is used for storing serialized selected id’s and is populated with Javascript each time a row is selected. As it’s separated from the grid, the hidden input content is preserved each time the grid is reloaded and is used for auto-selecting the rows each time the grid is reloaded.

Before we start with the code, as this is a bit more advanced grid, it is assumed that you’ve already mastered the basics of Magento grids since only differences will be shown how to create ajax-like grids.

Structure

It is already said that Grid Serializer splits the grid in two parts where only the grid gets reloaded, leaving the rest of the page intact as opposite to default action where the entire tab is reloaded. In order to make that happen, layout and controller has to distinguish those two parts. As it can be seen on the image below, we have to separate the entire tab content, which initially includes the a hidden input type and the grid itself, and the grid that will be replaced.

For this we’ll need to have two actions defined in both layout file and controller: customersTab and customersGrid (or the tab and grid content).

Admin Ajax Grid

Visible in the code below, the tab content defined by the <adminhtml_ajaxgrid_customerstab> handle includes both the grid content and the hidden input type blocks, while the grid-only content defined by the <adminhtml_ajaxgrid_customersgrid> handle is a stripped version of the tab content, containing only the grid block.

<?xml version="1.0"?>
<layout>
    <adminhtml_ajaxgrid_edit>
        <reference name="left">
            <block type="inchoo/adminhtml_edit_tabs" name="ajaxgrid.edit.tabs" />
        </reference>
        <reference name="content">
            <block type="inchoo/adminhtml_edit" name="ajaxgrid.index" />
        </reference>
    </adminhtml_ajaxgrid_edit>
 
    <adminhtml_ajaxgrid_customerstab>
        <block type="core/text_list" name="root" output="toHtml">
            <block type="inchoo/adminhtml_edit_tab_customers" name="inchoo.tab.customers"/>
            <block type="adminhtml/widget_grid_serializer" name="inchoo.serializer.customers">
                <action method="initSerializerBlock">
                    <grid_block_name>inchoo.tab.customers</grid_block_name>
                    <data_callback>getSelectedCustomers</data_callback>
                    <hidden_input_name>customer_ids</hidden_input_name>
                    <reload_param_name>customers</reload_param_name>
                </action>
            </block>
        </block>
    </adminhtml_ajaxgrid_customerstab>
 
    <adminhtml_ajaxgrid_customersgrid>
        <block type="core/text_list" name="root" output="toHtml">
            <block type="inchoo/adminhtml_edit_tab_customers" name="inchoo.tab.customers"/>
        </block>
    </adminhtml_ajaxgrid_customersgrid>
</layout>

In order to make Grid Serializer work, all those things have to be linked somehow, which is why the hidden input field has a few parameters left to be defined.

grid_block_name: name of the grid block
data_callback: method in the grid block that returns selected id’s on grid initialization
hidden_input_name: name of the hidden input type
reload_param_name: name of the parameter used in URL

Having the layout defined, controller has to add some actions to those handles. The tab action (customersTabAction) is supposed to collect the id’s, which is usually a collection that returns the id’s as array, to load the block defined in the layout and to set the selected id’s.

The grid action (customersGridAction) does the similar thing to the tab action, again, but the difference is that the selected id’s are read from the URL parameter which is defined in the layout (reload_param_name).

In the save action selected id’s are taken from the hidden input field from the parameter name defined in the layout (hidden_input_name). Grid Serializer comes with a handy method called decodeGridSerializedInput() which decodes a string into an array.

class Inchoo_Module_Adminhtml_AjaxgridController
extends Mage_Adminhtml_Controller_Action
{
    public function editAction()
    {
        $this->loadLayout()
            ->_title($this->__('Customer Grid'))
            ->renderLayout();
    }
    public function customersTabAction()
    {
        // used for selecting customers on tab load
        $saved_customer_ids = array(); // your load logic here
 
        $this->loadLayout()
            ->getLayout()
            ->getBlock('inchoo.tab.customers')
            ->setSelectedCustomers($saved_customer_ids);
 
        $this->renderLayout();
    }
    public function customersGridAction()
    {
        $this->loadLayout()
            ->getLayout()
            ->getBlock('inchoo.tab.customers')
            ->setSelectedCustomers($this->getRequest()->getPost('customers', null));
 
        $this->renderLayout();
    }
    public function saveAction()
    {
        if ($customersIds = $this->getRequest()->getParam('customer_ids', null)) {
            $customersIds = Mage::helper('adminhtml/js')->decodeGridSerializedInput($customersIds);
        }
 
        // your save logic here
    }
}

Blocks

Having the structure of the tab defined, there are only a few things left to change in the blocks. Let’s start with the Tab block first.

In the _beforeToHtml() method, where the tabs are usually defined, several parameters have to be changed. The first thing would be to omit the content parameter, that is usually set by default, and to replace it with the “url” parameter. That parameter has to define an url for the tab content. The other thing would be to add a class parameter with the value “ajax”.

protected function _beforeToHtml()
{
    $this->addTab('customers', array(
        'label'     => $this->__('Customers'),
        'title'     => $this->__('Customers'),
        'url'       => $this->getUrl('*/*/customerstab', array('_current' => true)),
        'class'     => 'ajax'
    ));
    return parent::_beforeToHtml();
}

The last file that has to be changed is the grid block itself. One thing would be to use a variable containing the selected id’s, which is set by the controller (getSelectedCustomers() in this example). The other thing would be to define an URL of the grid content, which is why a getGridUrl() method is defined. Make sure to have setUseAjax(true) variable defined as well.

class Inchoo_Module_Block_Adminhtml_Edit_Tab_Customers
extends Mage_Adminhtml_Block_Widget_Grid
{
    public function __construct()
    {
        parent::__construct();
        $this->setSaveParametersInSession(false);
        $this->setUseAjax(true);
        $this->setId('inchoo_customers');
    }
    protected function _prepareCollection()
    {
        $collection = Mage::getResourceModel('customer/customer_collection')
            ->addNameToSelect();
 
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }
    protected function _prepareColumns()
    {
        $this->addColumn('selected_customers', array(
            'header'    => $this->__('Popular'),
            'type'      => 'checkbox',
            'index'     => 'entity_id',
            'align'     => 'center',
            'field_name'=> 'selected_customers',
            'values'    => $this->getSelectedCustomers(),
        ));
 
        $this->addColumn('name', array(
            'header'    => $this->__('Name'),
            'index'     => 'name',
            'align'     => 'left',
        ));
 
        return parent::_prepareColumns();
    }
    public function getGridUrl()
    {
        return $this->getUrl('*/*/customersGrid', array('_current' => true));
    }
}

The result

Not a lot of changes are needed to create ajax-like grids in Magento by using Grid Serializer while a lot of things can have benefit of that. With this library selected id’s are preserved on grid reloads which is why pages can be changed, filters applied, sorting changed without losing the selection.

Additionally, the grid won’t be populated and collections won’t be run until the tab is selected. Having that in mind, it has to be extra careful when saving id’s since it has to distinguish whether none of the id’s are selected and when the tab is not loaded at all.

Ajax Admin Grid

There’s a small glitch that I have found when working on this. When viewing a grid on smaller screens where the grid can be scrolled up and down, each time a row is selected the page jumps to the top. It’s a bit irritating, but looking around in Magento I have found a few of the pages that use Grid Serializer natively and that share the same problem (some of the pages have custom solutions), but that’s really a tiny thing compared to what the library offers.

The post Magento Grid Serializer for Ajax Grids appeared first on Inchoo.

]]>
http://inchoo.net/magento/magento-grid-serializer-ajax-grids/feed/ 1
How We Built Sheeel iOS Application http://inchoo.net/dev-talk/ios-development/how-we-built-sheeel-ios-application/ http://inchoo.net/dev-talk/ios-development/how-we-built-sheeel-ios-application/#comments Thu, 18 Sep 2014 06:36:04 +0000 http://inchoo.net/?p=23294 A while ago, we released the first version of iOS application for Middle Eastern favorite, award-winning daily deal site – Sheeel.com Our goal was simple – to create natural environment for iOS users to receive notifications about new deals and breeze through the process of exploring and shopping. Even though Sheeel.com features an adaptive site...

The post How We Built Sheeel iOS Application appeared first on Inchoo.

]]>
A while ago, we released the first version of iOS application for Middle Eastern favorite, award-winning daily deal site – Sheeel.com

Our goal was simple – to create natural environment for iOS users to receive notifications about new deals and breeze through the process of exploring and shopping.

Even though Sheeel.com features an adaptive site which performs superb on a variety of devices, iOS handcrafted experience will help users stay in touch with notification system that helps them be the first to get their hands on the great deals from Sheeel.

With the launch of both iPhone and iPad versions you can download for free on iTunes, we are extending Sheeel’s already established identity as an innovative commerce company in the Middle East market.

For those of you unfamiliar with the project, Sheeel.com is a daily deal site based in Kuwait, operating in a number of Middle East countries, and you can learn more about our previous work with them in our portfolio.

Why iOS?

iOS powered devices are taking over mobile market in Middle East and to accommodate that growing market and fit users needs, developing an application for this platform was the next logical step in reaching out to ever growing base of new customers and the evolution of Sheeel brand.

How We Built The Application

Since Sheeel’s primary focus is on user needs and ease of access to everyone regardless of the platform and device, the app itself had to be designed and developed with same practice in mind – to allow every iOS user to quickly get notifications for new deals and browse through them without unnecessary clutter.

We researched, wireframed and iterated numerous versions of design. The process involved researching elements and UI patterns that are already in use by iOS applications, how users perceive and react to them and finally applying what we learned to create a great user experience for Sheeel users.

sketching

The main challenge to deal with was building an application that can serve both left-to-right and right-to-left layouts in order to fulfil the market needs while maintaining user flow and providing straightforward instructions and calls to actions. Having an experienced team at Inchoo who already worked on projects in the same market made the entire process a whole lot easier.

Repeating steps of research, wireframing and design while presenting and conducting tests with people who were not originally involved with the project resulted in where we are today. It took quite a few number of iterations, experimenting and ideas to get the things where we wanted them and where our test subjects felt comfortable while using the application itself and breezing through the process of exploring the deals and finally making a purchase.

prototyping

Prototyping the app’s design was a huge part of testing; it helped us to quickly iterate and resolve common problems while using the application and observing how people expected it to behave, which led to achieving the natural user flow in the final iteration. Ideas for features where piling up quickly and gave us insights into what we could include in the future, but we focused on keeping the core functionality of Sheeel as well as its simplicity, rather than spending too much time around nifty features in our user tests. Less is more.

Having color scheme and Sheeel’s strong identity already defined and widely recognized in the region through main website Sheeel.com, we faced quite a challenge blending it with iOS 7 design guidelines and best practices in mind, and the team tackled this with passion. Since we had the features and functionality sealed and tested in our early prototypes that gave us clear and precise answers to usability and behavioral questions, numerous design iterations, experiments and mashups were being thrown out until we iterated the right one that suited the needs of market and it’s users and represented Sheeel’s identity to the fullest.

screens

Mobile development and cooperation with Plava Tvornica

Our partners from Plava Tvornica (Blue Factory) were in charge of the mobile development aspect of the project. They are a Croatian company who have years of experience and excel at building mobile apps. Our design and ideas combined with the team of highly experienced professionals from Blue Factory resulted in a clear and concise workflow which allowed us to build and iterate prototypes quickly and efficiently to create a great user experience.

Conclusion

Nonetheless, versatility of the team involved, their knowledge and vast experience with previous projects in the same market pushed us in the right direction while building the application and adapting features and functionalities for iOS 7 powered devices to the ultimate satisfaction of its end users.

With the launch of the iPhone and iPad applications we have opened up a completely new frontier for Sheeel’s business to grow and attract new customers and serve existing ones using the iOS platform as their everyday tool of choice to get things done quickly and efficiently.

The post How We Built Sheeel iOS Application appeared first on Inchoo.

]]>
http://inchoo.net/dev-talk/ios-development/how-we-built-sheeel-ios-application/feed/ 4
UI/UX guidelines for different types of customers http://inchoo.net/ecommerce/ui-ux-guidelines-different-types-customers/ http://inchoo.net/ecommerce/ui-ux-guidelines-different-types-customers/#comments Tue, 02 Sep 2014 07:15:00 +0000 http://inchoo.net/?p=22749 There are plenty of things to consider when designing an eCommerce site, but the most important is to design with the customer in mind. Different shopper types have different goals and shopping strategies, and being aware of these helps us make decisions that improve store usability. While analysing some of our recent projects, and taking...

The post UI/UX guidelines for different types of customers appeared first on Inchoo.

]]>
There are plenty of things to consider when designing an eCommerce site, but the most important is to design with the customer in mind. Different shopper types have different goals and shopping strategies, and being aware of these helps us make decisions that improve store usability. While analysing some of our recent projects, and taking into account a number of articles around this topic, we decided to break down online customers into five main types.

The Bargain Hunter

customer-type-bargain-hunter What motivates the bargain hunter?

  • Coupons
  • Promotions
  • Discount codes
  • Sales
  • Free Shipping

Brand loyalty: Low Purchase probability: Medium Description: The Bargain Hunter will spend a lot of time to find the best deal. Usually they will start their search on some of the comparison shopping sites. They are less loyal to a brand and are just looking for the lowest price. Those with stronger brand loyalty will connect with your store via social media waiting for a coupon codes and sales. They often buy products on sale which they don‘t really require, as a excuse they would say that they are actually saving money in the long run by getting everything they need during the sale. UI/UX guidelines for Bargain Hunters:

  1. Products on sale must be clearly indicated – bargain hunters want to locate special deals as soon as they land on a page. Use labels, icons and other visual elements to make these items stand out. If possible, create On Sale category so they can see all sale-priced items listed on one page.
  2. List prices, discounts and savings – Cross out retail price and show discounted one, including amount saved.
  3. Make newsletter forms easy to find and appear in multiple places on your site – give them a clear value proposition for why they should join your email list (exclusive discounts).
  4. Easy coupon redemption – prominently display a coupon field on the cart or checkout page. Don’t forget to explain how to get codes (modal or tooltip) to avoid the risk of losing customers who don‘t have a code and will leave the site in search of a discount.
  5. Define a discount based on a specific criteria – e.g. free shipping for customers who spend at least $90.

Power Shopper (product focused)

customer-type-power-shopper What motivates the power shopper?

  • specific need (a replacement for something they have or something completely new)
  • necessity

Brand loyalty: Low / Medium Purchase probability: Medium Description: Power shopper has a specific product on mind and they want to find their desired product in as few steps as possible. If they don’t find it, they will leave right away. UI/UX guidelines for Power Shoppers:

  1. Place your search bar in a prominent place and keep it consistently placed throughout the rest of the site – Power shoppers will probably use search option to find a product so be sure to make it easily visible. If possible use search bar with auto-complete option and display results with prominent sort and filter options.
  2. Clear navigation – keep your main navigation well-structured and simple. With well-planned content and a natural organization structure Power shoppers will find their desired products in a few clicks.
  3. Use clear product descriptions, recognizable names and large images so they can confirm it’s the right product – They don’t want to waste time looking around, they will take a look and if it match their criteria they’ll buy it.
  4. Provide a convenient and quick checkout – power shoppers values their time so if they found what they were looking for, they want to checkout as quickly as possible.

The Researcher

customer-type-researcher What motivates the researcher?

  • specific goal but not a specific product or brand
  • to gather as much information as possible

Brand loyalty: Low / Medium Purchase probability: Medium Description: The researcher is browsing with the intention of collecting enough information about products and prices. Search can last for a few days or a few months. They will probably visit multiple sites, so provide them information they need to keep them on your site. UI/UX guidelines for Researches:

  1. detailed product description – if you provide them with enough helpful data you will become their source of information and therefore turn them into buyers on your site. If they are left with unanswered questions, they will try to find the answers on other sites.
  2. provide related or similar products links- researchers don’t have a specific product or brand on mind so show them all options you have available.
  3. ratings and reviews – they rely on ratings and reviews when deciding on a product, so motivate your customers to write reviews and make them prominent on a product page.
  4. if you have many similar products provide easy product comparison – buying is preceded by a decision and the researcher is undecided, he is comparing the products until he makes a final decision so make it easier for him to make a final decision by comparing products.
  5. use a persistent shopping cart – more than 20% of shoppers confess using the shopping cart as a wish list to save products for future consideration. Allow them to think about a purchase for a few days and save the information for their next visit.

The Wandering Shopper

customer-type-wandering What motivates the wandering shopper?

  • no specific need or desire

Brand loyalty: Low/Medium Purchase probability: Low Description: Wandering shoppers don’t have a specific product or goal in mind, so it is a challenge to convert them into buyers. They study products with no intentions to purchase anything. Usually this is the largest segment in terms of traffic but they make up the smallest percentage of revenue. Anyhow, this is not a reason to ignore them but we should minimize the efforts to attract these shoppers. UI/UX guidelines for Wandering Shoppers:

  1. Make your site more visually appealing – they have time to socialize and if they are passionate about your site or specific products they will very likely communicate to others on social networks and blogs. They are often the source of good marketing.
  2. Keep them engaged – wandering shoppers in a retail store would pace up and down every aisle. They act similar in online store, if they like your site they will study the items, rate them, write reviews etc. Clear navigation, cross-sell, up-sell and vivid images will keep them engaged.

The Loyal Shopper

customer-type-loyal What motivates the loyal shopper?

  • good experiences with the store
  • quality

Brand loyalty: High Purchase probability: Medium/High Description: This segment usually make up more than 50% of sales. Price is secondary for loyal shoppers, they value quality over price. UI/UX guidelines for Loyal Shoppers:

  1. Encourage them to repeat orders – Allow them to save shopping carts and create lists of frequent purchases.
  2. Reward loyalty – customers like feeling appreciated so thank them for being loyal and encourage them to keep coming back. Whether it’s a discount or reward points they will appreciate the effort.

How to please everyone?

When you know all types of shoppers, their shopping strategies and needs, you should put yourself in their position and see if you have all key approaches to satisfy their varied needs. Share of shopper types can vary greatly from one store to another, but almost each store has these main 5 types of customers. Knowing their behavior patterns will help us identify their problems and two main factors for successful store is understanding who your customers are and solve their problems before they arise.

The post UI/UX guidelines for different types of customers appeared first on Inchoo.

]]>
http://inchoo.net/ecommerce/ui-ux-guidelines-different-types-customers/feed/ 0
Adding Magento products to Google Merchant Center http://inchoo.net/magento/adding-magento-products-to-google-base/ http://inchoo.net/magento/adding-magento-products-to-google-base/#comments Tue, 19 Aug 2014 07:00:57 +0000 http://inchoo.net/?p=3764 This post describes how to add Magento products to Google Merchant Center (formerly known as Google Base) using a product feed and explains some of the issues you might encounter. This post used to be about adding Magento products to Google Base, but since Google swapped Google Base for Google Merchant Center, I revamped this...

The post Adding Magento products to Google Merchant Center appeared first on Inchoo.

]]>
This post describes how to add Magento products to Google Merchant Center (formerly known as Google Base) using a product feed and explains some of the issues you might encounter. This post used to be about adding Magento products to Google Base, but since Google swapped Google Base for Google Merchant Center, I revamped this article with up to date data.

Setting up the Google Merchant Center account

Head over to Google Merchant Center page to create a Google Merchant account. Important note: While you’re able to add additional users to one Google Merchant Center account through Users tab in Settings, every Google account can be associated with only one Google Merchant Center Account.

Generating a Magento product feed for Google Shopping Ads

While there are several solutions in form of extensions out there, so far we’ve only used our custom in-house developed code to generate the product feed.

If you can’t have the in-house solution, there is an official extension by Magento called Google Content API for shopping, but that extension seem to have a lot of issue judging by reviews. I’d probably try some of the other solutions.

If you’ll create a custom product feed generation solution, here are the full feed specifications that you should go through.

EDIT: Good mates left some nice options for feed generation in the comments bellow this post so make sure to check those out.

Test the new product feed

Once you’ve generated your product feed, it’s time to upload it into the Google Merchant Center and test it. Within GMC, go to the data feeds tab.

There are two different upload buttons: “New Data Feed” and “New Test Data Feed”. Use the test feed to test if your feed is generated correctly.

Add and schedule your product feed to GMC

Once you tested the feed and solved any issues, go to the data feeds tab once again and add your feed using a “New Data Feed” button.

You can create a schedule for Google Merchant Center to automatically fetch your data feed at certain time of the day every day, week or month. In most cases I’d recommend generating and fetching the feed daily.

Common reasons for Google Merchant Center to disapprove your products

The most common reason for Google to disapprove your products are your images.

For apparel products images must be at least 250x250px. Max image size for any category is 4MB.

If your products (apparel) come in several colors or even sizes, Google requires a different image for each variation.

Your images may not have a watermark on them.

Your images must be crawl-able by Google, make sure you didn’t block them by robots.txt disallow or if they are on CDN make sure your CDN is not blocking them with CDN’s robots.txt disallow.

Apparel products must have a color. It can’t be a pattern (you can’t put “chevron pattern” under color attribute). You must use the “dominant” color instead, and you may add additional colors. Also color can’t be #ffffff, it needs to be the actual name of the color “white” or “black” or “red” etc.

Your price might have changed between the time you generated the feed and the time someone reviewed the products in your feed. If price is different in the feed compared to the price on the live store, your products will get disapproved.

Product reviews in Google shopping ads

Google recently started including product reviews into their shopping ads. This is only available in the USA, but when it comes to features such as this one we can expect it to roll out into UK and some other countries soon as well.

To be able to display your product reviews (and star rating) within your Google shopping ads, you need to either use one of the authorized 3rd party review systems (fore full list scroll down in this article) or create a product review feed.

In any case, you’ll need to fill out this form.

If you use your own product review system, as I mentioned, you’ll need to create a review feed. There are two types of feed supported, a full feed and an incremental feed. Click here for feed specifications and here for a sample feed.

Google Trusted Stores Feeds

Besides accepting product feeds for your shopping ads, Google Merchant Center is also a place where you submit your shipment and cancellation feeds for Google trusted stores program.

If you don’t know what this program is, it’s Google’s amazing trust seal that boosts the trust of your customers into your store since Google has your shipment and order cancellation data and asks your customers for feedback. Within GTS, Google also offers your customers up to $1,000 purchase protection.

Unfortunately, for now Google Trusted Stores program is only available in the US.

The post Adding Magento products to Google Merchant Center appeared first on Inchoo.

]]>
http://inchoo.net/magento/adding-magento-products-to-google-base/feed/ 118
Custom shipping method in Magento http://inchoo.net/magento/custom-shipping-method-in-magento/ http://inchoo.net/magento/custom-shipping-method-in-magento/#comments Thu, 07 Aug 2014 11:30:39 +0000 http://inchoo.net/?p=2669 In this article, I will demonstrate how to write custom shipping in Magento, or to be more precise, two of them: standard shipping and express shipping, which is only available if none of your cart items exceeds specified weight threshold. Lets start by explaining how Magento handles shipping, and what would be needed to achieve...

The post Custom shipping method in Magento appeared first on Inchoo.

]]>
In this article, I will demonstrate how to write custom shipping in Magento, or to be more precise, two of them: standard shipping and express shipping, which is only available if none of your cart items exceeds specified weight threshold. Lets start by explaining how Magento handles shipping, and what would be needed to achieve our goal.


When looking for available shipping methods, Magento first gathers all available carriers. A “Carrier” represents a shipping carrier, just like in real world (ex. FedEx). Each carrier is represented with the class that extends Mage_Shipping_Model_Carrier_Abstract.

After list of carriers has been received, shipping information(implemented as Mage_Shipping_Model_Rate_Request) is sent to carrier in order to retrieve all available rates provided by given carrier, represented as Mage_Shipping_Model_Rate_Result.

This process happens in Mage_Shipping_Model_Shipping::collectRates() as seen in code below:

...
$carriers = Mage::getStoreConfig('carriers', $storeId);
 
foreach ($carriers as $carrierCode => $carrierConfig) {
    $this->collectCarrierRates($carrierCode, $request);
}
...

Function collectCarrierRates() is responsible for checking carrier availability (is carrier enabled in admin, is it available for requested country, etc.), and eventually triggers collectRates() function of your class, which we will implement later.

And that is general outline of what is going on behind the scenes. We are now ready to write some code that will fit nicely into everything explained above. First thing you will need to do, is create new module which depends on Mage_Shipping. Besides standard module configuration, you will have following inside your config.xml:

<config>
    ...
    <default>
        ...
        <carriers>
            <inchoo_shipping>
                <active>1</active>
                <model>inchoo_shipping/carrier</model>
                <title>Inchoo Shipping Carrier</title>
                <sort_order>10</sort_order>
                <sallowspecific>0</sallowspecific>
                <express_max_weight>1</express_max_weight>
            </inchoo_shipping>
        </carriers>
        ...
    </default>
    ...
</config>

Entries active, sallowspecific and express_max_items are config entries which will be used and explained later. We will start with model entry. You can see that our carrier will be represented with Inchoo_Shipping_Model_Carrier, so lets implement that class. As previously said, carrier needs to extend Mage_Shipping_Model_Carrier_Abstract and implement Mage_Shipping_Model_Carrier_Interface in order to ensure Magento can work with it. We will start by doing just that:

class Inchoo_Shipping_Model_Carrier
    extends Mage_Shipping_Model_Carrier_Abstract
    implements Mage_Shipping_Model_Carrier_Interface
{
    protected $_code = 'inchoo_shipping';

Next, our interface requires us to implement getAllowedMethods() which returns array of key-value pairs of all available methods, so let’s do that:

public function getAllowedMethods()
{
    return array(
        'standard'    =>  'Standard delivery',
        'express'     =>  'Express delivery',
    );
}

Finally, we said that rates are collected by calling collectRates(). This function takes shipping information as parameter, and returns all available rates. It is also responsible for determining which rate is available for given request:

public function collectRates(Mage_Shipping_Model_Rate_Request $request)
{
	/** @var Mage_Shipping_Model_Rate_Result $result */
	$result = Mage::getModel('shipping/rate_result');
 
	/** @var Inchoo_Shipping_Helper_Data $expressMaxProducts */
	$expressMaxWeight = Mage::helper('inchoo_shipping')->getExpressMaxWeight();
 
	$expressAvailable = true;
	foreach ($request->getAllItems() as $item) {
	    if ($item->getWeight() > $expressMaxWeight) {
		$expressAvailable = false;
	    }
	}
 
	if ($expressAvailable) {
	    $result->append($this->_getExpressRate());
	}
	$result->append($this->_getStandardRate());
 
	return $result;
}

As you can see, code is pretty straight forward: Weight of all products in cart are compared to value stored in config, which determines availability of our ‘express‘ rate. Our ‘standard‘ rate is always available. Each rate is added by passing Mage_Shipping_Model_Rate_Result_Method object to append() of our result object. And we get those rate objects by calling _getExpressRate() and _getStandardRate(), which are implemented as following:

protected function _getStandardRate()
{
    /** @var Mage_Shipping_Model_Rate_Result_Method $rate */
    $rate = Mage::getModel('shipping/rate_result_method');
 
    $rate->setCarrier($this->_code);
    $rate->setCarrierTitle($this->getConfigData('title'));
    $rate->setMethod('large');
    $rate->setMethodTitle('Standard delivery');
    $rate->setPrice(1.23);
    $rate->setCost(0);
 
    return $rate;
}

And that’s all that our class needs in order to work. We will finish up by adding admin configuration through system.xml. Here is an shortened version:

<config>
    <sections>
        <carriers>
            <groups>
                <inchoo_shipping translate="label">
                    ...
                    <fields>
                        <active translate="label">
                            ...
                        </active>
                        <title translate="label">
                            ...
                        </title>
                        <sallowspecific translate="label">
                            ...
                            <frontend_type>select</frontend_type>
                            <frontend_class>shipping-applicable-country</frontend_class>
                            <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
                            ...
                        </sallowspecific>
                        <specificcountry translate="label">
                            ...
                            <frontend_type>multiselect</frontend_type>
                            <source_model>adminhtml/system_config_source_country</source_model>
                            ...
                        </specificcountry>
                        <express_max_weight translate="label">
                            ...
                        </express_max_weight>
                    </fields>
                </inchoo_shipping>
            </groups>
        </carriers>
    </sections>
</config>

What is important to note here, is that active, title, sallowspecific and specificcountry are handled automatically by Magento, so besides adding this to admin, you aren’t required to do anything else. With others being self explanatory, there are only two options being interesting here. First one, sallowspecific, tells Magento should carrier be available for all countries, or only for once that are specified in specificcountry.

And that is all work required for our shipping method to appear on checkout step. This module has been written as an example for Magento CE 1.9.0.1 and can be downloaded here.

Note: This is a revamp of an article originally written in July 2009.

The post Custom shipping method in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/custom-shipping-method-in-magento/feed/ 10
State of Magento Solution Specialist Certification – July 2014 http://inchoo.net/ecommerce/state-of-magento-solution-specialist-certification-july-2014/ http://inchoo.net/ecommerce/state-of-magento-solution-specialist-certification-july-2014/#comments Thu, 31 Jul 2014 07:23:31 +0000 http://inchoo.net/?p=22827 We are now over two and a half months deep into Magento Solution Specialist certification that rolled out during this year’s Imagine eCommerce conference. So, it was about time to crunch some numbers – we bring you the first “State of Magento Solution Specialist Certification” report. Who leads the charts with Solution Specialist certification? Which continent, country, city...

The post State of Magento Solution Specialist Certification – July 2014 appeared first on Inchoo.

]]>
We are now over two and a half months deep into Magento Solution Specialist certification that rolled out during this year’s Imagine eCommerce conference. So, it was about time to crunch some numbers – we bring you the first “State of Magento Solution Specialist Certification” report.

Who leads the charts with Solution Specialist certification? Which continent, country, city and solution partner have the most people certified?

These and some other interesting stats can be drawn from the current list of certified solution specialists available from the official Magento certification directory.

Let’s start with some basics – there are 74 Magento Certified Solution Specialists worldwide at the time of writing this article (end of July 2014).

Global leaders – continents, countries, cities, partners

I’m happy to report that Europe actually leads the way in front of North America with 38:32 and only a handful of Solution Specialists coming from other continents (Central and South America with 3 and Asia with 1).

mcss-continent-2014-07

However, USA does lead the charts in the total number of MCSS per country with the total of 31, followed by Germany with 11, Netherlands and UK with 7 apiece and yours truly – Croatia with 5.

mcss-country-2014-07

 

Our hometown of Osijek can take the global bragging rights on city level for the time being with 5 MCSS badges shining right here in Croatia with some “smaller places” like Groningen (Netherlands), Springfield (MO, USA) and New York following suit with 4 each.

And if you were wondering how come some of these cities take the reins, here’s a quick explanation – our Classy Llama friends come from Springfield, Missouri and Inchoo, as you probably already know, is based in Osijek, Croatia. Our two companies are currently tied for the lead with 4 Solution Specialists each.

Finally certified

Interestingly enough, this is the only certificate for the majority of those who attained it, 45 people didn’t have any other Magento certificate (myself included) prior to this one – and it makes perfect sense, as Magento clearly announced this to be the first non-technical certification.That meant that many of us who were around the platform and know it reasonably well simply didn’t have the opportunity to verify this – now we were quick to jump on the bandwagon.

Who’s your MVD (Most Versatile Developer)?

There are some all-rounders in the mix, what you might call your Most Versatile Developers – I found 5 (five) people on the listing that have passed all currently available Magento certification exams – Frontend, Backend, Backend Plus and Solution Specialist achieving a Magento Certification Grand Slam – talk about wearing multiple hats, right?

And I’d say these folks deserve to be mentioned here (I’d leave it to you to find their contact details, though):

  • Adriano Aguiar
  • Jitze Bakker
  • Kris Brown
  • Phillip Jackson
  • Vladimir Kerkhoff

Eric(k)s and the ladies

And if we dig a little deeper, there are two people named Eric and one Erik on the list making this name the most common one in the Solution Specialists pool – not sure what you want to do with this particular piece of information, but here it is.

Women also play a part, albeit theirs is more of a supporting role as there’s the total of 7 Solution Specialist ladies out there (slightly below 10%) – and all of them come from USA – so what’s up with that, Europe?!

mcss-ladies-gentlemen-2014-07

 

And there you go – State of Magento Solution Specialist certification at the end of July 2014 – I plan to prepare updates and show some more interesting data in the months to come, so stay tuned.

Disclaimer:
This data is taken from the official public Magento certification directory and the “heavy crunching” required for this article was completed on July 29, 2014 – if you got certified and published in the meantime, you may want to wait for this post to be updated :)

There is also a possibility that not all people who got certified made their profiles public, so the actual number of certified developers may be a bit higher.

The post State of Magento Solution Specialist Certification – July 2014 appeared first on Inchoo.

]]>
http://inchoo.net/ecommerce/state-of-magento-solution-specialist-certification-july-2014/feed/ 5
Programmatically create bundle products in Magento http://inchoo.net/magento/programmatically-create-bundle-products-in-magento/ http://inchoo.net/magento/programmatically-create-bundle-products-in-magento/#comments Wed, 30 Jul 2014 13:00:14 +0000 http://inchoo.net/?p=22783 If this was a series of articles, this would be the third one. If you previously read my articles on simple and configurable product creation in Magento, you already know the basics. In this article we’ll expand our knowledge and learn how to create a bundle of products programmatically in Magento. A bundle of products...

The post Programmatically create bundle products in Magento appeared first on Inchoo.

]]>
If this was a series of articles, this would be the third one. If you previously read my articles on simple and configurable product creation in Magento, you already know the basics.

In this article we’ll expand our knowledge and learn how to create a bundle of products programmatically in Magento.

A bundle of products would be something you’d want to sell in batch. It might be easier to understand what a bundle product is in Magneto from the picture. We can take an example from Magento’s sample products.

Camera Travel Set.
bundle-sample1

We can see that this products has options. Each custom option’s value corresponds to one simple product that’s associated with the bundle.

For example, we have Camera option and Madison LX2200 and Madison RX3400 as it’s values. Each one of those cameras is a simple product (called ‘selection‘ in admin).

Now that we have gone through the basics, we’ll create the code necessary to create a bundle product with two options – each one containing two values to represent simple products.

    $bundleProduct = Mage::getModel('catalog/product');
    $bundleProduct
    ->setStoreId(Mage_Core_Model_App::ADMIN_STORE_ID) //you can set data in store scope
        ->setWebsiteIds(array(1)) //website ID the product is assigned to, as an array
        ->setAttributeSetId(20) //ID of a attribute set named 'default'
        ->setTypeId('bundle') //product type
        ->setCreatedAt(strtotime('now')) //product creation time
//    ->setUpdatedAt(strtotime('now')) //product update time
        ->setSkuType(0) //SKU type (0 - dynamic, 1 - fixed)
        ->setSku('bundlexx1') //SKU
        ->setName('test bundle product96') //product name
        ->setWeightType(0) //weight type (0 - dynamic, 1 - fixed)
//        ->setWeight(4.0000)
        ->setShipmentType(0) //shipment type (0 - together, 1 - separately)
        ->setStatus(1) //product status (1 - enabled, 2 - disabled)
        ->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) //catalog and search visibility
        ->setManufacturer(28) //manufacturer id
        ->setColor(24)
        ->setNewsFromDate('06/26/2014') //product set as new from
        ->setNewsToDate('06/30/2014') //product set as new to
        ->setCountryOfManufacture('AF') //country of manufacture (2-letter country code)
        ->setPriceType(0) //price type (0 - dynamic, 1 - fixed)
        ->setPriceView(0) //price view (0 - price range, 1 - as low as)
        ->setSpecialPrice(00.44) //special price in form 11.22
        ->setSpecialFromDate('06/1/2014') //special price from (MM-DD-YYYY)
        ->setSpecialToDate('06/30/2014') //special price to (MM-DD-YYYY)
        /*only available if price type is 'fixed'*/
//        ->setPrice(11.22) //price, works only if price type is fixed
//        ->setCost(22.33) //price in form 11.22
//        ->setMsrpEnabled(1) //enable MAP
//        ->setMsrpDisplayActualPriceType(1) //display actual price (1 - on gesture, 2 - in cart, 3 - before order confirmation, 4 - use config)
//        ->setMsrp(99.99) //Manufacturer's Suggested Retail Price
//        ->setTaxClassId(4) //tax class (0 - none, 1 - default, 2 - taxable, 4 - shipping)
        /*only available if price type is 'fixed'*/
        ->setMetaTitle('test meta title 2')
        ->setMetaKeyword('test meta keyword 2')
        ->setMetaDescription('test meta description 2')
        ->setDescription('This is a long description')
        ->setShortDescription('This is a short description')
        ->setMediaGallery(array('images' => array(), 'values' => array())) //media gallery initialization
        ->setStockData(array(
                'use_config_manage_stock' => 1, //'Use config settings' checkbox
                'manage_stock' => 1, //manage stock
                'is_in_stock' => 1, //Stock Availability
            )
        )
        ->setCategoryIds(array(4, 10)); //assign product to categories
 
    $bundleOptions = array();
    $bundleOptions = array(
        '0' => array( //option id (0, 1, 2, etc)
            'title' => 'item01', //option title
            'option_id' => '',
            'delete' => '',
            'type' => 'select', //option type
            'required' => '1', //is option required
            'position' => '1' //option position
        ),
        '1' => array(
            'title' => 'item02',
            'option_id' => '',
            'delete' => '',
            'type' => 'multi',
            'required' => '1',
            'position' => '1'
        )
    );
 
    $bundleSelections = array();
    $bundleSelections = array(
        '0' => array( //option ID
            '0' => array( //selection ID of the option (first product under this option (option ID) would have ID of 0, second an ID of 1, etc)
                'product_id' => '554', //if of a product in selection
                'delete' => '',
                'selection_price_value' => '10',
                'selection_price_type' => 0,
                'selection_qty' => 1,
                'selection_can_change_qty' => 0,
                'position' => 0,
                'is_default' => 1
            ),
 
            '1' => array(
                'product_id' => '553',
                'delete' => '',
                'selection_price_value' => '10',
                'selection_price_type' => 0,
                'selection_qty' => 1,
                'selection_can_change_qty' => 0,
                'position' => 0,
                'is_default' => 1
            )
        ),
        '1' => array( //option ID
            '0' => array(
                'product_id' => '552',
                'delete' => '',
                'selection_price_value' => '10',
                'selection_price_type' => 0,
                'selection_qty' => 1,
                'selection_can_change_qty' => 0,
                'position' => 0,
                'is_default' => 1
            ),
 
            '1' => array(
                'product_id' => '551',
                'delete' => '',
                'selection_price_value' => '10',
                'selection_price_type' => 0,
                'selection_qty' => 1,
                'selection_can_change_qty' => 0,
                'position' => 0,
                'is_default' => 1
            )
        )
    );
    //flags for saving custom options/selections
    $bundleProduct->setCanSaveCustomOptions(true);
    $bundleProduct->setCanSaveBundleSelections(true);
    $bundleProduct->setAffectBundleProductSelections(true);
 
    //registering a product because of Mage_Bundle_Model_Selection::_beforeSave
    Mage::register('product', $bundleProduct);
 
    //setting the bundle options and selection data
    $bundleProduct->setBundleOptionsData($bundleOptions);
    $bundleProduct->setBundleSelectionsData($bundleSelections);
 
    $bundleProduct->save();
    echo 'success';
} catch (Exception $e) {
    Mage::log($e->getMessage());
    echo $e->getMessage();
}

You can find comments next to each line of code below. I tried to comment as much of the code as possible, so you have some helpful hints as to what each of the lines represents.

The end result

bundle-example-2
bundle-example

It’s important to master the concept of a bundle product before running the code above, otherwise, you’ll probably end up setting values the wrong way.

I assume you already know how to figure out which data you can set for a bundle product. (Hint: read the articles in the links above).

As always, I’m here for any questions you might have.

The post Programmatically create bundle products in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/programmatically-create-bundle-products-in-magento/feed/ 0
Why should you look for Solution Specialists when planning your new Magento project? http://inchoo.net/ecommerce/why-should-you-look-for-solution-specialists-when-planning-your-new-magento-project/ http://inchoo.net/ecommerce/why-should-you-look-for-solution-specialists-when-planning-your-new-magento-project/#comments Tue, 29 Jul 2014 09:54:09 +0000 http://inchoo.net/?p=22803 If you have plans for a new eCommerce project on Magento, what better place to start than with a team of certified experts who can prepare you to make the most of this powerful platform? Magento Certified Solution Specialist is someone you’d want to have on your team early on. When you hear that someone is...

The post Why should you look for Solution Specialists when planning your new Magento project? appeared first on Inchoo.

]]>
If you have plans for a new eCommerce project on Magento, what better place to start than with a team of certified experts who can prepare you to make the most of this powerful platform? Magento Certified Solution Specialist is someone you’d want to have on your team early on.

When you hear that someone is a “specialist” at their job, or that they’re “certified”, it may not prove that they’re the perfect match for your project, of course, but these attributes can provide sufficient grounds for initial pre-selection of candidates, wouldn’t you agree?

What’s a Solution Specialist in Magento?

For those of you who are new to Magento as a platform, it may be worthy to know that Magento has several certification programs available and that its solution partners can verify their expertise by having their team members (be those frontend, backend developers or consultants) earn their badges.

For merchants who may already use Magento and know both its stronger and weaker points, the Solution Specialist certification is a rather new one, that came out only a couple of months back.

The Magento Solution Specialist is “an expert user of Magento platform who can efficiently align business objectives with Magento functionality, optimize use of native features and avoid unnecessary customization.”

Sounds good, right? So what do they actually do on a project? These people are here to first and foremost ensure that you can achieve your goals with Magento implementation – if needed, they’re also here to help you modify your goals and set clear expectations on what’s possible with standard set of Magento features and what may need further customizations.

These are the people who are an invaluable liaison between you, the creative and development team and they (at least in our case) help establish lasting partnerships rather than mere client-agency relationships.

I’ve already explained the certification process in one of my previous blog posts, and there I’ve announced that we’ll soon be adding some shiny new badges to our roster. So here we are, a couple of months later (actually, this post is overdue as we’ve passed the exam back in mid June) and we can proudly let you know that we at Inchoo have the total of 4 (four) Magento Certified Solution Specialists on board.

Ivan Weiler passing the MCSS exam at Imagine conference with Jacko approving.

Our Ivan Weiler passing the MCSS exam at Imagine conference with Jacko approving.

Who are these people and how can they help you?

Let’s check them out alphabetically:

Toni Anicic – in a nutshell, he’s your SEO go-to guy – Toni has extensive experience with eCommerce SEO in particular and knows how to make the most out of any store’s SEO configuration. He loves seeing eCommerce conversion rates improve after some of his suggestions and getting our clients on the right path to achieving their business goals.

Drazen Karacic-Soljic – with a bit more life and work experience in various industries than Toni (a polite way to say he’s much older), Drazen loves getting involved in all aspects of our clients’ businesses as he makes sure that any suggestions we make are keeping in mind the big picture and focus on our clients’ unique strengths.

Aron Stanic – yours truly, it’s a bit strange to write about myself, but there you go – you’ll most likely see me or someone else from our sales team as the first point of contact when discussing some of your projects. I’ve been around Magento, project management and requirements scoping for over 5 years now and can help you with making sure we align expectations early on and set up priorities based on your budget and timeline.

Ivan Weiler – as our technical lead and consultant, Ivan finds himself actively involved in many of our clients’ projects in early, architectural planning stage when he helps some of our teams set the solid technical foundation for simple and highly complex projects. You will want to have someone like him on board.

As you’ll also see by taking a look at these guys’ profiles, it’s quite an interesting mix.

What’s in it for you?

We have two of our eCommerce consultants, our technical lead and a chief sales officer all certified as Solution Specialists and this tells anyone planning for a new Magento project they can rely on us making sure we got them covered all the way through.

Starting with analyzing your requirements, all the way through technical implementation (don’t forget we also have 16 certified Magento developers on the roster) and optimizing the usability and overall performance of your store, we’re here for you.

If you’ve selected Magento as your platform of choice, who better to select to help you plan, deliver and optimize your eCommerce project then those certified to do exactly that?

Get in touch and let’s see how we can help you out!

The post Why should you look for Solution Specialists when planning your new Magento project? appeared first on Inchoo.

]]>
http://inchoo.net/ecommerce/why-should-you-look-for-solution-specialists-when-planning-your-new-magento-project/feed/ 0
Programmatically create a configurable Magento product http://inchoo.net/magento/programmatically-create-a-configurable-magento-product/ http://inchoo.net/magento/programmatically-create-a-configurable-magento-product/#comments Mon, 28 Jul 2014 12:00:43 +0000 http://inchoo.net/?p=22768 I already wrote about creating a simple product programmatically in Magento. For configurable product, however, things get a little bit complicated. As you already know, a configurable product is merely a product with simple products that differ in some option (attribute) assigned to itself. We can use this conclusion to extend our code for creation...

The post Programmatically create a configurable Magento product appeared first on Inchoo.

]]>
I already wrote about creating a simple product programmatically in Magento. For configurable product, however, things get a little bit complicated.

As you already know, a configurable product is merely a product with simple products that differ in some option (attribute) assigned to itself.

We can use this conclusion to extend our code for creation of simple products to work with configurable.

Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$simpleProduct = Mage::getModel('catalog/product');
try {
$simpleProduct
//    ->setStoreId(1) //you can set data in store scope
    ->setWebsiteIds(array(1)) //website ID the product is assigned to, as an array
    ->setAttributeSetId(20) //ID of a attribute set named 'default'
    ->setTypeId('simple') //product type
    ->setCreatedAt(strtotime('now')) //product creation time
//    ->setUpdatedAt(strtotime('now')) //product update time
    ->setSku('simple99y') //SKU
    ->setName('test simple product99') //product name
    ->setWeight(4.0000)
    ->setStatus(1) //product status (1 - enabled, 2 - disabled)
    ->setTaxClassId(4) //tax class (0 - none, 1 - default, 2 - taxable, 4 - shipping)
    ->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) //catalog and search visibility
    ->setManufacturer(28) //manufacturer id
    ->setColor(24)
    ->setNewsFromDate('06/26/2014') //product set as new from
    ->setNewsToDate('06/30/2014') //product set as new to
    ->setCountryOfManufacture('AF') //country of manufacture (2-letter country code)
    ->setPrice(11.22) //price in form 11.22
    ->setCost(22.33) //price in form 11.22
    ->setSpecialPrice(00.44) //special price in form 11.22
    ->setSpecialFromDate('06/1/2014') //special price from (MM-DD-YYYY)
    ->setSpecialToDate('06/30/2014') //special price to (MM-DD-YYYY)
    ->setMsrpEnabled(1) //enable MAP
    ->setMsrpDisplayActualPriceType(1) //display actual price (1 - on gesture, 2 - in cart, 3 - before order confirmation, 4 - use config)
    ->setMsrp(99.99) //Manufacturer's Suggested Retail Price
    ->setMetaTitle('test meta title 2')
    ->setMetaKeyword('test meta keyword 2')
    ->setMetaDescription('test meta description 2')
    ->setDescription('This is a long description')
    ->setShortDescription('This is a short description')
    ->setMediaGallery(array('images' => array(), 'values' => array())) //media gallery initialization
    ->setStockData(array(
            'use_config_manage_stock' => 0, //'Use config settings' checkbox
            'manage_stock' => 1, //manage stock
            'min_sale_qty' => 1, //Minimum Qty Allowed in Shopping Cart
            'max_sale_qty' => 2, //Maximum Qty Allowed in Shopping Cart
            'is_in_stock' => 1, //Stock Availability
            'qty' => 999 //qty
        )
    )
    ->setCategoryIds(array(3, 10)); //assign product to categories
    $simpleProduct->save();
} catch (Exception $e) {
    Mage::log($e->getMessage());
    echo $e->getMessage();
}

The code above would create a simple product. I won’t go into the details, but if you want, you can read more on my previous article by clicking on the link written at the top.

What we need to do now is create a configurable product and assign this simple product to it.

$configProduct = Mage::getModel('catalog/product');,
try {
$configProduct
//    ->setStoreId(1) //you can set data in store scope
        ->setWebsiteIds(array(1)) //website ID the product is assigned to, as an array
        ->setAttributeSetId(20) //ID of a attribute set named 'default'
        ->setTypeId('configurable') //product type
        ->setCreatedAt(strtotime('now')) //product creation time
//    ->setUpdatedAt(strtotime('now')) //product update time
        ->setSku('configurable96') //SKU
        ->setName('test config product96') //product name
        ->setWeight(4.0000)
        ->setStatus(1) //product status (1 - enabled, 2 - disabled)
        ->setTaxClassId(4) //tax class (0 - none, 1 - default, 2 - taxable, 4 - shipping)
        ->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) //catalog and search visibility
        ->setManufacturer(28) //manufacturer id
        ->setNewsFromDate('06/26/2014') //product set as new from
        ->setNewsToDate('06/30/2014') //product set as new to
        ->setCountryOfManufacture('AF') //country of manufacture (2-letter country code)
        ->setPrice(11.22) //price in form 11.22
        ->setCost(22.33) //price in form 11.22
        ->setSpecialPrice(00.44) //special price in form 11.22
        ->setSpecialFromDate('06/1/2014') //special price from (MM-DD-YYYY)
        ->setSpecialToDate('06/30/2014') //special price to (MM-DD-YYYY)
        ->setMsrpEnabled(1) //enable MAP
        ->setMsrpDisplayActualPriceType(1) //display actual price (1 - on gesture, 2 - in cart, 3 - before order confirmation, 4 - use config)
        ->setMsrp(99.99) //Manufacturer's Suggested Retail Price
        ->setMetaTitle('test meta title 2')
        ->setMetaKeyword('test meta keyword 2')
        ->setMetaDescription('test meta description 2')
        ->setDescription('This is a long description')
        ->setShortDescription('This is a short description')
        ->setMediaGallery(array('images' => array(), 'values' => array())) //media gallery initialization
        ->setStockData(array(
                'use_config_manage_stock' => 0, //'Use config settings' checkbox
                'manage_stock' => 1, //manage stock
                'is_in_stock' => 1, //Stock Availability
            )
        )
        ->setCategoryIds(array(3, 10)) //assign product to categories
    ;
    /**/
    /** assigning associated product to configurable */
    /**/
    $configProduct->getTypeInstance()->setUsedProductAttributeIds(array(92)); //attribute ID of attribute 'color' in my store
    $configurableAttributesData = $configProduct->getTypeInstance()->getConfigurableAttributesAsArray();
 
    $configProduct->setCanSaveConfigurableAttributes(true);
    $configProduct->setConfigurableAttributesData($configurableAttributesData);
 
    $configurableProductsData = array();
    $configurableProductsData['920'] = array( //['920'] = id of a simple product associated with this configurable
        '0' => array(
            'label' => 'Green', //attribute label
            'attribute_id' => '92', //attribute ID of attribute 'color' in my store
            'value_index' => '24', //value of 'Green' index of the attribute 'color'
            'is_percent' => '0', //fixed/percent price for this option
            'pricing_value' => '21' //value for the pricing
        )
    );
    $configProduct->setConfigurableProductsData($configurableProductsData);
    $configProduct->save();
 
    echo 'success';
} catch (Exception $e) {
    Mage::log($e->getMessage());
    echo $e->getMessage();
}

And that’s it! Your configurable product should be properly set up and visible on the frontend!

Note that you need to assign the same attribute set to both configurable product and it’s associated products.

In the code comments you’ll find a short comment for each of the methods used. As in previous article, you can log the saveAction of Mage_Adminhtml_Catalog_ProductController to get list of all the data keys you can add for the product.

Need help following the code in this article? Leave a comment below.

The post Programmatically create a configurable Magento product appeared first on Inchoo.

]]>
http://inchoo.net/magento/programmatically-create-a-configurable-magento-product/feed/ 4
Sort products by sold quantity in Magento http://inchoo.net/magento/magento-products/sort-show-products-by-sold-quantity-in-magento/ http://inchoo.net/magento/magento-products/sort-show-products-by-sold-quantity-in-magento/#comments Fri, 18 Jul 2014 11:14:26 +0000 http://inchoo.net/?p=10314 Magento by default comes with a few basic product sorting options such as sorting by product position, name or price. In this article you’ll learn how to sort products by how many times they have been sold. To do this we have to override some of Magento’s core files. Modifying core files is bad practice, so...

The post Sort products by sold quantity in Magento appeared first on Inchoo.

]]>
Magento by default comes with a few basic product sorting options such as sorting by product position, name or price. In this article you’ll learn how to sort products by how many times they have been sold.

To do this we have to override some of Magento’s core files.
Modifying core files is bad practice, so we’ll rather create our own module to accomplish the same functionality and remain upgradeable.

I’ll assume you know how to set-up your module and create its corresponding file (app/etc/modules/Inchoo_Catalog.xml) so Magento recognizes our module.

Now that we have our module ready and recognized by Magento, lets create our config.xml at Inchoo/Catalog/etc/config.xml:

<config>
    <modules>
        <Inchoo_Catalog>
            <version>0.1.0</version>
        </Inchoo_Catalog>
    </modules>
    <global>
        <blocks>
            <catalog>
                <rewrite>
                    <product_list_toolbar>Inchoo_Catalog_Block_Product_List_Toolbar</product_list_toolbar>
                </rewrite>
            </catalog>
        </blocks>
        <models>
            <catalog>
                <rewrite>
                    <config>Inchoo_Catalog_Model_Config</config>
                </rewrite>
            </catalog>
            <catalog_resource>
                <rewrite>
                    <product_collection>Inchoo_Catalog_Model_Resource_Product_Collection</product_collection>
                </rewrite>
            </catalog_resource>
        </models>
    </global>
</config>

As you might have noticed we are overriding the following three files:

  • Mage_Catalog_Block_Product_List_Toolbar
  • Mage_Catalog_Model_Config
  • Mage_Catalog_Model_Resource_Product_Collection

Our app/code/local/Inchoo_Catalog_Block_Product_List_Toolbar should look like this:

class Inchoo_Catalog_Block_Product_List_Toolbar extends Mage_Catalog_Block_Product_List_Toolbar
{
    public function setCollection($collection)
    {
        parent::setCollection($collection);
 
        if ($this->getCurrentOrder()) {
            if($this->getCurrentOrder() == 'qty_ordered') {
                $this->getCollection()->getSelect()
                     ->joinLeft(
                            array('sfoi' => $collection->getResource()->getTable('sales/order_item')),
                             'e.entity_id = sfoi.product_id',
                             array('qty_ordered' => 'SUM(sfoi.qty_ordered)')
                         )
                     ->group('e.entity_id')
                     ->order('qty_ordered ' . $this->getCurrentDirection());
            } else {
                $this->getCollection()
                     ->setOrder($this->getCurrentOrder(), $this->getCurrentDirection())->getSelect();
            }
        }
 
        return $this;
    }
}

We are extending all the Mage_Catalog_Block_Product_List_Toolbar properties and methods but we are overriding the setCollection() method with our own.

Our Inchoo_Catalog_Model_Config is actually very simple:

class Inchoo_Catalog_Model_Config extends Mage_Catalog_Model_Config
{
    public function getAttributeUsedForSortByArray()
    {
        return array_merge(
			parent::getAttributeUsedForSortByArray(),
			array('qty_ordered' => Mage::helper('catalog')->__('Sold quantity'))
		);
    }
}

By now the sorting on the category page should actually work, but we have a little problem with pagination as it doesn’t display the correct amount of records found. We can fix this with the following code in Inchoo/Catalog/Model/Resource/Product/Collection.php:

class Inchoo_Catalog_Model_Resource_Product_Collection extends Mage_Catalog_Model_Resource_Product_Collection
{
    protected function _getSelectCountSql($select = null, $resetLeftJoins = true)
    {
       $this->_renderFilters();
       $countSelect = (is_null($select)) ?
           $this->_getClearSelect() :
           $this->_buildClearSelect($select);
 
       if(count($countSelect->getPart(Zend_Db_Select::GROUP)) > 0) {
           $countSelect->reset(Zend_Db_Select::GROUP);
       }
 
       $countSelect->columns('COUNT(DISTINCT e.entity_id)');
       if ($resetLeftJoins) {
           $countSelect->resetJoinLeft();
       }
       return $countSelect;
    }
}

And that’s it. After taking 4 little steps our Magento site now is able to sort products by sold quantity.
Hope you enjoyed! :)

Note: This is a revamp of an article originally written in November 2008.

The post Sort products by sold quantity in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/magento-products/sort-show-products-by-sold-quantity-in-magento/feed/ 2
Out of the box Form Validation in Magento http://inchoo.net/magento/out-of-the-box-form-validation-in-magento/ http://inchoo.net/magento/out-of-the-box-form-validation-in-magento/#comments Wed, 16 Jul 2014 09:00:46 +0000 http://inchoo.net/?p=745 Magento uses Prototype library to manage form validation. This comes in handy, because all you need to do when writing custom form is to assign a valid class names to your input fields, and pass the form id to VarienForm object. Let’s look at this example of validation in Magento. <form name="test-form" id="my-custom-form" action="" method="post">...

The post Out of the box Form Validation in Magento appeared first on Inchoo.

]]>
Magento uses Prototype library to manage form validation. This comes in handy, because all you need to do when writing custom form is to assign a valid class names to your input fields, and pass the form id to VarienForm object.

Let’s look at this example of validation in Magento.

<form name="test-form" id="my-custom-form" action="" method="post">
    <label for="firstname"><?php echo $this->__('First name') ?> <span class="required">*</span></label><br />
    <input id="firstname" name="firstname" class="input-text required-entry" />
    <label for="lastname"><?php echo $this->__('Last name') ?> <span class="required">*</span></label><br />
    <input id="lastname" name="lastname" class="input-text required-entry" />
    <label for="useremail"><?php echo $this->__('Email') ?> <span class="required">*</span></label><br />
    <input type="text" name="useremail" id="useremail" class="input-text required-entry validate-email" />
    <input type="submit" name="submit" value="<?php echo $this-/>__('Submit') ?>" />
</form>
<script type="text/javascript">
    //< ![CDATA[
        var customForm = new VarienForm('my-custom-form');
    //]]>
</script>

I also took some time to extract all validation rules you can use, and their error message. You can find the methods yourself by looking in js/prototype/validation.js on line 414

'validate-no-html-tags'         => 'HTML tags are not allowed'
'validate-select'               => 'Please select an option.'
'required-entry'                => 'This is a required field.'
'validate-number'               => 'Please enter a valid number in this field.'
'validate-number-range'         => 'The value is not within the specified range.'
'validate-digits'               => 'Please use numbers only in this field. Please avoid spaces or other characters such as dots or commas.'
'validate-digits-range'         => 'The value is not within the specified range.'
'validate-alpha'                => 'Please use letters only (a-z or A-Z) in this field.'
'validate-code'                 => 'Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter.'
'validate-alphanum'             => 'Please use only letters (a-z or A-Z) or numbers (0-9) only in this field. No spaces or other characters are allowed.'
'validate-alphanum-with-spaces' => 'Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.'
'validate-street'               => 'Please use only letters (a-z or A-Z) or numbers (0-9) or spaces and # only in this field.'
'validate-phoneStrict'          => 'Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.'
'validate-phoneLax'             => 'Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.'
'validate-fax'                  => 'Please enter a valid fax number. For example (123) 456-7890 or 123-456-7890.'
'validate-date'                 => 'Please enter a valid date.'
'validate-date-range'           => 'The From Date value should be less than or equal to the To Date value.'
'validate-email'                => 'Please enter a valid email address. For example johndoe@domain.com.'
'validate-emailSender'          => 'Please use only visible characters and spaces.'
'validate-password'             => 'Please enter 6 or more characters. Leading or trailing spaces will be ignored.'
'validate-admin-password'       => 'Please enter 7 or more characters. Password should contain both numeric and alphabetic characters.'
'validate-both-passwords'       => 'Please make sure your passwords match.'
'validate-url'                  => 'Please enter a valid URL. Protocol is required (http://, https:// or ftp://)'
'validate-clean-url'            => 'Please enter a valid URL. For example http://www.example.com or www.example.com'
'validate-identifier'           => 'Please enter a valid URL Key. For example "example-page", "example-page.html" or "anotherlevel/example-page".'
'validate-xml-identifier'       => 'Please enter a valid XML-identifier. For example something_1, block5, id-4.'
'validate-ssn'                  => 'Please enter a valid social security number. For example 123-45-6789.'
'validate-zip'                  => 'Please enter a valid zip code. For example 90602 or 90602-1234.'
'validate-zip-international'    => 'Please enter a valid zip code.'
'validate-date-au'              => 'Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.'
'validate-currency-dollar'      => 'Please enter a valid $ amount. For example $100.00.'
'validate-one-required'         => 'Please select one of the above options.'
'validate-one-required-by-name' => 'Please select one of the options.'
'validate-not-negative-number'  => 'Please enter a number 0 or greater in this field.'
'validate-zero-or-greater'      => 'Please enter a number 0 or greater in this field.'
'validate-greater-than-zero'    => 'Please enter a number greater than 0 in this field.'
'validate-state'                => 'Please select State/Province.'
'validate-new-password'         => 'Please enter 6 or more characters. Leading or trailing spaces will be ignored.'
'validate-cc-number'            => 'Please enter a valid credit card number.'
'validate-cc-type'              => 'Credit card number does not match credit card type.'
'validate-cc-type-select'       => 'Card type does not match credit card number.'
'validate-cc-exp'               => 'Incorrect credit card expiration date.'
'validate-cc-cvn'               => 'Please enter a valid credit card verification number.'
'validate-ajax'                 => ''
'validate-data'                 => 'Please use only letters (a-z or A-Z), numbers (0-9) or underscore(_) in this field, first character should be a letter.'
'validate-css-length'           => 'Please input a valid CSS-length. For example 100px or 77pt or 20em or .5ex or 50%.'
'validate-length'               => 'Text length does not satisfy specified text range.'
'validate-percents'             => 'Please enter a number lower than 100.'
'validate-cc-ukss'              => 'Please enter issue number or start date for switch/solo card type.'

In case you would want to add your custom validation rule, just create a javascript file with any name you want in js folder of your Magento installation. I’ll show you how to do this in the following example.

Say you want to modify one of the validation rules, the best way would be to create a method with a same name and load it after the original method. This way, all calls would be redirected to your method instead of the original. Just be careful if you’re overriding validation rules like this, because javascript is only client-side validation. Your may have your server validate the field as well – which can cause some errors even though the value in field was entered the way you wanted.
So, be sure to test your validation rule.

For our purposes, we’ll override Magento’s validate-email method. We want it to accept only email addresses ending with @gmail.com.

Let’s create our javascript file and place it in the js folder.

js/inchoo.js

Validation.add('validate-email', 'Please enter a valid Gmail address. For example johndoe@gmail.com.', function(v) {
    return Validation.get('IsEmpty').test(v) || /^([a-zA-Z0-9]+[a-zA-Z0-9._%-]*@gmail\.com)$/i.test(v)
})

All we need to do now is include our validation script. We’ll do that using Magento’s addJs method which will add the javascript file to our website’s head.

<default>
    <reference name="head">
        <action method="addJs"><script>inchoo.js</script></action>
    </reference>
</default>

You can do this from any layout file (preferably your module’s layout file), Magento did it in their theme’s layout page called page.xml located in app/design/frontend/base/default/layout/page.xml

Hope this clears some things up for you. At the very least, you now have a list of all validation classes in Magento.

Note: This is a revamp of an article originally written in January 2009.

The post Out of the box Form Validation in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/out-of-the-box-form-validation-in-magento/feed/ 1
Adding custom credit card type to your payment method in Magento http://inchoo.net/magento/adding-custom-credit-card-type-to-your-payment-method-in-magento/ http://inchoo.net/magento/adding-custom-credit-card-type-to-your-payment-method-in-magento/#comments Thu, 03 Jul 2014 11:55:23 +0000 http://inchoo.net/?p=22592 At one point or another, you might want to implement a payment method with credit card types that are not implemented in Magento by default. Please note that in this article you’ll learn how to add a new credit card type and validate it only. This article does not cover the whole payment module creation....

The post Adding custom credit card type to your payment method in Magento appeared first on Inchoo.

]]>
At one point or another, you might want to implement a payment method with credit card types that are not implemented in Magento by default.
Please note that in this article you’ll learn how to add a new credit card type and validate it only. This article does not cover the whole payment module creation.


For the needs of this article, lets assume we have created a new payment module named NewModule and corresponding app/etc/modules/Inchoo_NewModule.xml file. We are going to implement Diners Club credit card type and validate it both server-side and client-side, the Magento way.

Steps

There are a few steps we have to go through in order to make the magic happen, the Magento way of course, and each one is equally important as the other.

  • Add credit card type to global payment node in our config.xml
  • Let Magento know which model are we using as our payment model
  • Implement server-side validation in our model
  • Implement client-side validation (Prototype)

Adding new credit card type

Lets hit it off by creating our etc/config.xml and adding credit card type to it:

<config>
    <modules>
        <Inchoo_NewModule>
            <version>1.0.0</version>
        </Inchoo_NewModule>
    </modules>
 
    <global>
        <payment>
            <cc>
                <types>
                    <DC>
                        <code>DC</code>
                        <name>Diners Club</name>
                        <order>60</order>
                    </DC>
                </types>
            </cc>
        </payment>
    </global>
</config>

This doesn’t do much yet, as we have just added a credit card type to Magento without telling it what model to use and how to validate it.

Creating payment model

Lets stay in our etc/config.xml for a lil’ bit. We have to configure our model and tell Magento what model to use by adding the following code:

<config>
...
    <global>
    ...
        <models>
            <newmodule>
                <class>Inchoo_NewModule_Model</class>
            </newmodule>
        </models>
    ...
    </global>
 
    <default>
        <payment>
            <newmodule>
                <model>newmodule/payment</model>
            </newmodule>
        </payment>
    </default>
</config>

Now we have to create a file Inchoo/NewModule/Model/Payment.php and it has to extend Mage_Payment_Model_Method_Cc.
This is the place where we’ll use our custom Regex pattern for credit card number and CVV.
If you’re implementing a different credit card type, you’ll need a Regex pattern that corresponds to it.

class Inchoo_NewModule_Model_Payment extends Mage_Payment_Model_Method_Cc
{
    protected $_code = 'inchoo_newmodule';
 
    public function getVerificationRegEx()
    {
        return array_merge(parent::getVerificationRegEx(), array(
            'DC' => '/^[0-9]{3}$/' // Diners Club CCV
       ));
    }
 
    public function OtherCcType($type)
    {
        return in_array($type, array('OT', 'DC'));
    }
 
    public function validate()
    {
        parent::validate();
        /* we call parent's validate() function!
         * if the code got this far, it means the cc type is none of the supported
         * ones implemented by Magento and now it gets interesting
         */
 
        $info = $this->getInfoInstance();
        $ccNumber = $info->getCcNumber();
        $availableTypes = explode(',',$this->getConfigData('cctypes'));
        // checks if Diners Club card is allowed
        if(!in_array($info->getCcType(), $availableTypes)){
            Mage::throwException($this->_getHelper()->__('Credit card type is not allowed for this payment method.'));
        }
        // validate credit card number against Luhn algorithm
        if(!$this->validateCcNum($info->getCcNumber())){
            Mage::throwException($this->_getHelper()->__('Invalid Credit Card Number'));
        }
 
        /* this is Diners Club regex pattern.
         * it's different for every cc type, so beware
         */
        if($info->getCcType()=='DC' && !preg_match('/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/', $ccNumber)){
            Mage::throwException($this->_getHelper()->__('Credit card number mismatch with credit card type.'));
        }
        // now we retrieve our CCV regex pattern and validate against it
        $verificationRegex = $this->getVerificationRegEx();
        if(!preg_match($verificationRegex[$info->getCcType()], $info->getCcCid()))
        {
            Mage::throwException($this->_getHelper()->__('Please enter a valid credit card verification number.'));
        }
 
        // further validation here (expiration month, expiration year etc.)
 
    }
 
}

Now that sums up our Diners Club card number and CCV number validation part. You’ll probably want to make additional checks in validate() method, such as credit card expiration date.

Client-side validation in Prototype

There are two ways we can tackle this issue.
The first one would be creating a .js file and including it via layout XML file where we need it. The second one would include modifying layout XML file only.
We’ll do it the second way here.

<config>
...
    <frontend>
        <layout>
            <updates>
                <newmodule>
                    <file>newmodule.xml</file>
                </newmodule>
            </updates>
        </layout>
    </frontend>
</config>

It’s time to modify the layout file and implement Javascript at checkout onepage. In case you haven’t created the layout xml file already, create it at design/frontend/base/default/layout/newmodule.xml with the following code:

<layout version="1.0.0">
    <checkout_onepage_index>
        <reference name="head">
            <block type="core/text" name="newmodule.diners.validation">
                <action method="setText">
                    <text>
                        <![CDATA[<script type="text/javascript">
                            Validation.creditCartTypes.set('DC', [new RegExp('^3(?:0[0-5]|[68][0-9])[0-9]{11}$'), new RegExp('^[0-9]{3}$'), true]);
                        </script>]]>
                    </text>
                </action>
            </block>
        </reference>
    </checkout_onepage_index>
</layout>

We should add Diners Club to the list of allowed credit card types in our NewModule config.xml likewise:

<config>
...
    <default>
        <payment>
            <newmodule>
                <model>newmodule/payment</model>
                <cctypes>VI,AE,DC</cctypes> //Visa (VI), American Express (AE) and Diners Club (DC)
            </newmodule>
        </payment>
    </default>
</config>

Show’s over folks! We have successfully implemented a custom credit card into Magento! :)

The post Adding custom credit card type to your payment method in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/adding-custom-credit-card-type-to-your-payment-method-in-magento/feed/ 1
Programmatically (manually) creating simple Magento product http://inchoo.net/magento/programming-magento/programatically-manually-creating-simple-magento-product/ http://inchoo.net/magento/programming-magento/programatically-manually-creating-simple-magento-product/#comments Fri, 27 Jun 2014 11:00:30 +0000 http://inchoo.net/?p=10210 In a development process, you often need some testing data you can use. Magento supplies you with it’s default Sample Data that contains some products. Thing is, they’re not of much use if you have some custom attributes added to your products, if you create your own attribute set, or a product type. For this,...

The post Programmatically (manually) creating simple Magento product appeared first on Inchoo.

]]>
In a development process, you often need some testing data you can use. Magento supplies you with it’s default Sample Data that contains some products. Thing is, they’re not of much use if you have some custom attributes added to your products, if you create your own attribute set, or a product type.

For this, you’ll need to add some products yourself.

Sometimes, doing so in the administration is enough, but it gets painful when you need to add multiple products, or if you’re still developing some feature, and making changes on the fly.

This problem can be easily solved by adding products programmatically.

I’ll show you an example on how to add a simple product this way. You could modify this code a bit, and make it work for other product types, or even use it as an import script.

You can call this through an observer, set is as a controller action, or cheat and call it from a view file, whichever you think is best. Just pay attention, that if you observe some event, don’t use the ones that get called on every controller action. Or, if you do, be sure to check if the product with the same SKU already exists, by loading product’s ID by SKU before the attempt to create it. For this, you can use the ‘if’ check on lines 4 and 49 in the code example below.

If you don’t do that, you’ll get SQL errors line the following one:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '193-1' for key 'UNQ_CATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_STOCK_ID'

Let’s get to the code I’m talking about.

<?php
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$product = Mage::getModel('catalog/product');
//    if(!$product->getIdBySku('testsku61')):
 
try{
$product
//    ->setStoreId(1) //you can set data in store scope
    ->setWebsiteIds(array(1)) //website ID the product is assigned to, as an array
    ->setAttributeSetId(9) //ID of a attribute set named 'default'
    ->setTypeId('simple') //product type
    ->setCreatedAt(strtotime('now')) //product creation time
//    ->setUpdatedAt(strtotime('now')) //product update time
 
    ->setSku('testsku61') //SKU
    ->setName('test product21') //product name
    ->setWeight(4.0000)
    ->setStatus(1) //product status (1 - enabled, 2 - disabled)
    ->setTaxClassId(4) //tax class (0 - none, 1 - default, 2 - taxable, 4 - shipping)
    ->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) //catalog and search visibility
    ->setManufacturer(28) //manufacturer id
    ->setColor(24)
    ->setNewsFromDate('06/26/2014') //product set as new from
    ->setNewsToDate('06/30/2014') //product set as new to
    ->setCountryOfManufacture('AF') //country of manufacture (2-letter country code)
 
    ->setPrice(11.22) //price in form 11.22
    ->setCost(22.33) //price in form 11.22
    ->setSpecialPrice(00.44) //special price in form 11.22
    ->setSpecialFromDate('06/1/2014') //special price from (MM-DD-YYYY)
    ->setSpecialToDate('06/30/2014') //special price to (MM-DD-YYYY)
    ->setMsrpEnabled(1) //enable MAP
    ->setMsrpDisplayActualPriceType(1) //display actual price (1 - on gesture, 2 - in cart, 3 - before order confirmation, 4 - use config)
    ->setMsrp(99.99) //Manufacturer's Suggested Retail Price
 
    ->setMetaTitle('test meta title 2')
    ->setMetaKeyword('test meta keyword 2')
    ->setMetaDescription('test meta description 2')
 
    ->setDescription('This is a long description')
    ->setShortDescription('This is a short description')
 
    ->setMediaGallery (array('images'=>array (), 'values'=>array ())) //media gallery initialization
    ->addImageToMediaGallery('media/catalog/product/1/0/10243-1.png', array('image','thumbnail','small_image'), false, false) //assigning image, thumb and small image to media gallery
 
    ->setStockData(array(
                       'use_config_manage_stock' => 0, //'Use config settings' checkbox
                       'manage_stock'=>1, //manage stock
                       'min_sale_qty'=>1, //Minimum Qty Allowed in Shopping Cart
                       'max_sale_qty'=>2, //Maximum Qty Allowed in Shopping Cart
                       'is_in_stock' => 1, //Stock Availability
                       'qty' => 999 //qty
                   )
    )
 
    ->setCategoryIds(array(3, 10)); //assign product to categories
$product->save();
//endif;
}catch(Exception $e){
Mage::log($e->getMessage());
}

We’re setting this product in ‘Default Values‘ scope. You can uncomment the code on line 5 to set product data for a specific store. For example, you could do this:

...
    ->setMetaTitle('test meta title 2')
    ->setMetaKeyword('test meta keyword 2')
    ->setMetaDescription('test meta description 2')
    ->setStoreId(2)
    ->setMetaTitle('Some other meta title')
    ->setMetaKeyword('Some other meta keyword')
    ->setMetaDescription('Some other meta description')
$product->save();

The code above will set different meta values in store with an ID of 2.

You can find all the values you are able to set for the product by logging the post data on product save. I’m thinking of saveAction in the ProductController that gets called from Magento administration when you’re saving a product.

app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php

public function saveAction()
{
    $storeId        = $this->getRequest()->getParam('store');
    $redirectBack   = $this->getRequest()->getParam('back', false);
    $productId      = $this->getRequest()->getParam('id');
    $isEdit         = (int)($this->getRequest()->getParam('id') != null);
 
    $data = $this->getRequest()->getPost();
    if ($data) {
        Mage::log($data);

Just add the last line to the file. Be sure to delete it when you’re done developing, though. This will get you an array of data you can save. For example for setting [meta_title], you’ll use a magic method setMetaTitle(‘somevalue’).

Note: This is a revamp of an article originally written in July 2009.

The post Programmatically (manually) creating simple Magento product appeared first on Inchoo.

]]>
http://inchoo.net/magento/programming-magento/programatically-manually-creating-simple-magento-product/feed/ 14
Get the current customer role in Magento http://inchoo.net/magento/get-the-current-customer-role-in-magento/ http://inchoo.net/magento/get-the-current-customer-role-in-magento/#comments Thu, 26 Jun 2014 10:00:46 +0000 http://inchoo.net/?p=10287 Looking at the old articles on our website that long for a rewrite, I sometimes stumble upon a gem that can be useful. One of those is the piece of code enables us to do custom stuff if our customer is assigned to a certain customer group. You can find customer groups in Magento Administration....

The post Get the current customer role in Magento appeared first on Inchoo.

]]>
Looking at the old articles on our website that long for a rewrite, I sometimes stumble upon a gem that can be useful. One of those is the piece of code enables us to do custom stuff if our customer is assigned to a certain customer group.

You can find customer groups in Magento Administration.

  • Customers->Customer Groups is where customer groups are managed.
  • Customers->Manage Customers->Click on a customer->Account Information tab->Customer group dropdown is where you would assign a customer to a group.

The way I see it, the possible implementation of this code would be using it in an observer to add custom javascript to our page’s head if a customer is assigned to the ‘Wholesale‘ group.

Let’s do that.

Begin by registering your module. I presume you’ll know how to do this, or have your existing module you can use.

Now, create a configuration file for your module. Since my example module is called ‘Customergroup‘ with namespace ‘Inchoo‘, the code below goes in

app/code/community/Inchoo/Customergroup/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Inchoo_Customergroup>
            <version>1.0.0</version>
        </Inchoo_Customergroup>
    </modules>
    <global>
        <events>
            <controller_action_layout_generate_blocks_after>
                <observers>
                    <inchoo_customergroup>
                        <type>singleton</type>
                        <class>Inchoo_Customergroup_Model_Observer</class>
                        <method>placeCustomJs</method>
                    </inchoo_customergroup>
                </observers>
            </controller_action_layout_generate_blocks_after>
        </events>
    </global>
</config>

Now, we’ll create our observer.
app/code/community/Inchoo/Customergroup/Model/Observer.php

<?php
class Inchoo_Customergroup_Model_Observer
{
    public function placeCustomJs()
    {
 
        $roleId = Mage::getSingleton('customer/session')->getCustomerGroupId();
        $role = Mage::getSingleton('customer/group')->load($roleId)->getData('customer_group_code');
        $role = strtolower($role);
 
        $headBlock = Mage::app()->getLayout()->getBlock('head');
        if($headBlock && $role=='wholesale')
        {
            $headBlock->addJs('test/myScript.js');
        }
        return $this;
    }
}

This isn’t the only use case. You can, of course, use this anywhere else you see fit. For example showing a message for certain customers, a different newsletter box, etc.

Just keep in mind that $role will equal ‘not logged in‘ if a customer isn’t logged in.

Note: This is a revamp of an article originally written in March 2009.

The post Get the current customer role in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/get-the-current-customer-role-in-magento/feed/ 2
Reusing Magento poll on any page or any block http://inchoo.net/magento/magento-frontend/reusing-magento-poll-on-any-page-or-any-block/ http://inchoo.net/magento/magento-frontend/reusing-magento-poll-on-any-page-or-any-block/#comments Mon, 23 Jun 2014 12:00:04 +0000 http://inchoo.net/?p=10250 On very rare occasions we get to work with polls. Most of our Magento projects end up with polls removed from the layout for one reason or the other – mostly because we rely on social networks instead for adding ‘social’ aspect to stores. Nonetheless, sometimes client wants to have polls on his site. Magento...

The post Reusing Magento poll on any page or any block appeared first on Inchoo.

]]>
On very rare occasions we get to work with polls. Most of our Magento projects end up with polls removed from the layout for one reason or the other – mostly because we rely on social networks instead for adding ‘social’ aspect to stores.

Nonetheless, sometimes client wants to have polls on his site. Magento has default poll system, which does it’s job. To an extent. What do we do if we want to add multiple polls to the site or choose which poll we want to display?

I’ll show you. The procedure is not complicated. It depends on what you want to achieve, and how you want to proceed.

CMS page

One option is adding polls through CMS page, by calling the poll block and loading a specific poll by it’s ID. Let’s do that on the ‘About Us’ page.

Open the CMS page in Magento administration (CMS->Pages->click on ‘About Us’), and under Design tab, paste this piece of code:

<reference name="content">
<block type="poll/activePoll" name="custom.poll">
<action method="setPollId"><pollId>1</pollId></action>
<action method="setPollTemplate"><template>poll/active.phtml</template><type>poll</type></action>
<action method="setPollTemplate"><template>poll/result.phtml</template><type>results</type></action>
</block>
<block type="poll/activePoll" name="custom.poll2">
<action method="setPollId"><pollId>2</pollId></action>
<action method="setPollTemplate"><template>poll/active.phtml</template><type>poll</type></action>
<action method="setPollTemplate"><template>poll/result.phtml</template><type>results</type></action>
</block>
</reference>

This layout uses Magento’s default poll block methods – firstly setting the PollId to an ID we have chosen (highlighted lines in this example), and sets appropriate poll templates. Just keep in mind that, if you want to add multiple polls, be sure to set an unique name attribute to each block.

You should see two polls printed in the ‘About Us’ page.

10250-2

Programatically

Let’s also cover adding polls programatically to any page we like. In this example, we’ll add a poll to left column of ‘3 columns layout’ template.

Open app/design/frontend/base/default/template/page/3columns.phtml

<?php
	$poll = $this->getLayout()->createBlock('poll/activePoll');
	$poll->setPollId(2);
	$poll->setPollTemplate('poll/active.phtml', 'poll');
	$poll->setPollTemplate('poll/result.phtml', 'results');
	echo $poll->toHtml();
?>

Add the code above just before

<?php echo $this->getChildHtml('left') ?>

The result:
10250-1

Note that adding the poll this way means that it would load regardless of poll start and end date.

This means that the poll would show, but you wouldn’t be able to place a vote, since when placing a vote, there is a piece of code in place that checks whether the poll is expired.

That’s it for today, a quick way of adding polls to any page in Magento.

Note: This is a revamp of an article originally written in May 2009.

The post Reusing Magento poll on any page or any block appeared first on Inchoo.

]]>
http://inchoo.net/magento/magento-frontend/reusing-magento-poll-on-any-page-or-any-block/feed/ 0
Programmatically adding new customers to the Magento store http://inchoo.net/magento/programming-magento/programmaticaly-adding-new-customers-to-the-magento-store/ http://inchoo.net/magento/programming-magento/programmaticaly-adding-new-customers-to-the-magento-store/#comments Wed, 18 Jun 2014 11:00:16 +0000 http://inchoo.net/?p=10243 Us developers love adding things programmatically. Even though you can create a new customer through a signup form, or via admin interface, in some cases, that might take too long. If you need a bunch of customers assigned to different groups, from different countries, you might be better off doing this from the code. For...

The post Programmatically adding new customers to the Magento store appeared first on Inchoo.

]]>
Us developers love adding things programmatically. Even though you can create a new customer through a signup form, or via admin interface, in some cases, that might take too long.

If you need a bunch of customers assigned to different groups, from different countries, you might be better off doing this from the code.

For starters, let’s add a customer with some basic information.

$websiteId = Mage::app()->getWebsite()->getId();
$store = Mage::app()->getStore();
 
$customer = Mage::getModel("customer/customer");
$customer   ->setWebsiteId($websiteId)
            ->setStore($store)
            ->setFirstname('John')
            ->setLastname('Doe')
            ->setEmail('jd1@ex.com')
            ->setPassword('somepassword');
 
try{
    $customer->save();
}
catch (Exception $e) {
    Zend_Debug::dump($e->getMessage());
}

Example 1

As we can see, the code above adds a customer with only first and last names, email and password set. This sometimes might be enough, but we can do more. You could add middle name, assign the customer to a specific customer group, even add prefixes and suffixes to his/hers name.

$customer   ->setWebsiteId($websiteId)
            ->setStore($store)
            ->setGroupId(2)
            ->setPrefix('Sir')
            ->setFirstname('John')
            ->setMiddleName('2')
            ->setLastname('Doe')
            ->setSuffix('II')
            ->setEmail('jd2@ex.com')
            ->setPassword('somepassword');

The result:
Example 3

For the customer to be a complete entity able to complete orders, we need to add an address and assign it to a customer. Let’s do just that.

$address = Mage::getModel("customer/address");
$address->setCustomerId($customer->getId())
        ->setFirstname($customer->getFirstname())
        ->setMiddleName($customer->getMiddlename())
        ->setLastname($customer->getLastname())
        ->setCountryId('HR')
		//->setRegionId('1') //state/province, only needed if the country is USA
        ->setPostcode('31000')
        ->setCity('Osijek')
        ->setTelephone('0038511223344')
        ->setFax('0038511223355')
        ->setCompany('Inchoo')
        ->setStreet('Kersov')
        ->setIsDefaultBilling('1')
        ->setIsDefaultShipping('1')
        ->setSaveInAddressBook('1');
 
try{
    $address->save();
}
catch (Exception $e) {
    Zend_Debug::dump($e->getMessage());
}

Example 2

Note that the setCountryId needs country code as value (you could find out these values by inspecting ‘Country’ input field on customer creation page in administration). Same goes for setGroupId method above, you need an ID of a customer group, which is easiest to find out by inspecting elements.

I hope most of the code is self-explanatory, since we know what fields are required when adding a customer from the administration. The thing you should keep an eye on are required fields.
In case you would need multiple customers to be added, you could place the part of the code in a loop, and create them within seconds.

Note: This is a revamp of an article originally written in July 2012.

The post Programmatically adding new customers to the Magento store appeared first on Inchoo.

]]>
http://inchoo.net/magento/programming-magento/programmaticaly-adding-new-customers-to-the-magento-store/feed/ 3
Get rewritten product url in a different store http://inchoo.net/magento/get-rewritten-product-url-in-a-different-store/ http://inchoo.net/magento/get-rewritten-product-url-in-a-different-store/#comments Thu, 29 May 2014 08:00:10 +0000 http://inchoo.net/?p=22357 It was pointed out to me in one of the preceding articles that getting a product’s URL in a different store can get pretty complicated if the URL you want to get is rewritten. I decided to come up with a solution to that problem, as it could be useful in the future. I came...

The post Get rewritten product url in a different store appeared first on Inchoo.

]]>
It was pointed out to me in one of the preceding articles that getting a product’s URL in a different store can get pretty complicated if the URL you want to get is rewritten. I decided to come up with a solution to that problem, as it could be useful in the future.

I came up with a method that returns a rewritten part (i.e. after rewrite) of a product’s URL. You’ll need to add base URL of a store to this return value to get a full URL (as shown on languages.phtml example).

Note that this is useful only if you have a scope of URL key attribute set to store view. This means that you could have different URL of a product in different stores – for example if you have a product with an URL key ‘nokia-blue‘ in english store view, and ‘nokia-blau‘ in german store view. This will work for rewrites that you when editing your product. If you decide to add URL rewrites yourself in ‘URL rewrite management’, you probably have a very good reason, but we won’t be covering that case.

I won’t be going through creating and registering a module this time.

Lets start by adding this method to your module’s helper.

app/code/community/Inchoo/Rewrites/Helper/Data.php

<?php
class Inchoo_Rewrites_Helper_Data extends Mage_Core_Helper_Abstract
{
    public function rewrittenProductUrl($productId, $categoryId, $storeId)
    {
        $coreUrl = Mage::getModel('core/url_rewrite');
        $idPath = sprintf('product/%d', $productId);
        if ($categoryId) {
            $idPath = sprintf('%s/%d', $idPath, $categoryId);
        }
        $coreUrl->setStoreId($storeId);
        $coreUrl->loadByIdPath($idPath);
 
        return $coreUrl->getRequestPath();
    }
}
?>

Now, we’ll do a little walk-through. As you can see, this method accepts $productId, $categoryId and $storeId as arguments. To understand how URLs look before they’re rewritten, let’s take a look at core_url_rewrite table in our database.

rewritten-product-url-example

A id_path of a product that’s not in any category appears in form of product/productId, and if it belongs to any of the categories, it’s appears as product/productId/categoryId.

Let’s get back to our method. We’ll recreate the structure of id_path using data we already have in Magento registry. Then, we need to setStoreId() to the model we’re using before we look for a rewrite. After that, we’ll use loadByIdPath() to load our rewritten URL row. All that’s left now is getting a request path, i.e. the rewritten part of a product’s URL. We’ll doing all that using Magento’s core/url_rewrite model.

One use case for this would be language (store) switcher that redirects you to the same product you’re looking at, but in different store. Just place the following code in your languages.phtml file.

app/design/base/default/template/page/html/switch/languages.phtml

<?php if(count($this->getStores())>1): ?>
    <?php
    $helper = Mage::helper('inchoo_rewrites');
    $prod = Mage::registry('current_product');
    $categ = Mage::registry('current_category');
    $categId = $categ ? $categ->getId() : null;
 
    ?>
    <div class="form-language">
        <label for="select-language"><?php echo $this->__('Your Language:') ?></label>
        <select id="select-language" title="<?php echo $this->__('Your Language') ?>" onchange="window.location.href=this.value">
            <?php foreach ($this->getStores() as $_lang): ?>
                <?php $_selected = ($_lang->getId() == $this->getCurrentStoreId()) ? ' selected="selected"' : '' ?>
                <option value="<?php
                    if($prod) {
                        echo $_lang->getBaseUrl() . $helper->rewrittenProductUrl($prod->getId(), $categId, $_lang->getId()) . '?___store=' . $_lang->getCode();
                    }else{
                        echo $_lang->getCurrentUrl(false);
                    }
                ?>"<?php echo $_selected ?>><?php echo $this->escapeHtml($_lang->getName()) ?></option>
            <?php endforeach; ?>
        </select>
    </div>
<?php endif; ?>

Your language switcher’s source code should look something like this (provided you have different URL set for your product in at least one store)
rewritten-product-url-example-2

Hope this will help some of you to master the concept of URL rewrites in multilingual stores.

The post Get rewritten product url in a different store appeared first on Inchoo.

]]>
http://inchoo.net/magento/get-rewritten-product-url-in-a-different-store/feed/ 7