<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SpinOneSolutions &#187; PHP</title>
	<atom:link href="http://www.spinonesolutions.com/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.spinonesolutions.com</link>
	<description>I waste my time so that you don't have to...</description>
	<lastBuildDate>Fri, 16 Apr 2010 21:39:11 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Magento Controller Override</title>
		<link>http://www.spinonesolutions.com/2010/03/magento-controller-override/</link>
		<comments>http://www.spinonesolutions.com/2010/03/magento-controller-override/#comments</comments>
		<pubDate>Fri, 19 Mar 2010 16:00:11 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[Custom]]></category>
		<category><![CDATA[Override]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=511</guid>
		<description><![CDATA[The Problem: You want to override a Controller&#8217;s action with a custom action. Magento Enterprise v1.6.0.0 The Solution: I ran into some issues trying to override a controller&#8217;s action yesterday. I think it mainly stems from a version change, but at any rate here&#8217;s the &#8220;new&#8221; way to do it. This comes from a helpful [...]]]></description>
			<content:encoded><![CDATA[<h3>The Problem:</h3>
<p>You want to override a Controller&#8217;s action with a custom action.</p>
<p>Magento Enterprise v1.6.0.0</p>
<h3>The Solution:</h3>
<p>I ran into some issues trying to override a controller&#8217;s action yesterday. I think it mainly stems from a version change, but at any rate here&#8217;s the &#8220;new&#8221; way to do it. This comes from a helpful <a href="http://www.magentocommerce.com/boards/viewthread/37244/">post</a> on the Magento forums.</p>
<p>In this example my custom modules&#8217;s namespace is &#8220;Spinonesolutions&#8221; and I&#8217;m going to override the <strong>createPostAction</strong> in <strong>Mage_Customer_AccountController.</strong></p>
<p>Start by creating a bare bones custom module; if you don&#8217;t know how to do this check out <a title="Magento – Part I : Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Magento – Part I : Custom Module</a>.</p>
<p>I&#8217;ve only got two files in my custom module; <strong>controllers/AccountController.php</strong> and <strong>etc/config.xml</strong>.</p>
<p>config.xml:</p>
<pre class="php" name="code">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;config&gt;
&lt;modules&gt;
  &lt;Spinonesolutions_Customer&gt;
    &lt;version&gt;0.0.1&lt;/version&gt;
  &lt;/Spinonesolutions_Customer&gt;
&lt;/modules&gt;
&lt;frontend&gt;
  &lt;routers&gt;
    &lt;!-- This is the frontname to override
    EG: http://mydomainname.com/customer/  --&gt;
    &lt;customer&gt;
      &lt;args&gt;
        &lt;modules&gt;
          &lt;!-- Use the module Spinonesolutions_Customer BEFORE Mage_Customer --&gt;
          &lt;Spinonesolutions_Customer before="Mage_Customer"&gt;Spinonesolutions_Customer&lt;/Spinonesolutions_Customer&gt;
        &lt;/modules&gt;
      &lt;/args&gt;
    &lt;/customer&gt;
  &lt;/routers&gt;
&lt;/frontend&gt;
&lt;/config&gt;</pre>
<p>AccountController.php:</p>
<pre class="php" name="code">&lt;?php
/**
* Override Customer AccountController
*
* Override the createPost action
*
* @category Mage
* @package Mage_Customer
* @author Will Wright
*/

/*Controllers aren't included automatically, so we include it here so that we can extend it*/
include_once("Mage/Customer/controllers/AccountController.php");
class Spinonesolutions_Customer_AccountController extends Mage_Customer_AccountController {
  public function createPostAction() {
    die('here');
  }
}</pre>
<p>That&#8217;s it!  You should now have a working override.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2010/03/magento-controller-override/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Magento Partial Refund Amount Correction for Authorize.net</title>
		<link>http://www.spinonesolutions.com/2010/01/partial-refund-amount-correction-for-authorize-net/</link>
		<comments>http://www.spinonesolutions.com/2010/01/partial-refund-amount-correction-for-authorize-net/#comments</comments>
		<pubDate>Wed, 20 Jan 2010 22:04:48 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Model]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/2009/11/partial-refund-amount-correction-for-authorize-net/</guid>
		<description><![CDATA[The Problem: You&#8217;ve enabled partial refunds in the Authorizenet Model, but it&#8217;s not refunding the proper amounts.  This is due to, in my view, a bug in the core which I&#8217;ll show you how to patch! The Solution: What you need to do is overload the refund method in the Authorizenet model. Create a new [...]]]></description>
			<content:encoded><![CDATA[<h3>The Problem:</h3>
<p>You&#8217;ve enabled partial refunds in the Authorizenet Model, but it&#8217;s not refunding the proper amounts.  This is due to, in my view, a bug in the core which I&#8217;ll show you how to patch!</p>
<h3>The Solution:</h3>
<p>What you need to do is overload the <strong>refund</strong> method in the Authorizenet model.</p>
<p>Create a new model that extends the original Authorizenet model.  You&#8217;ll need to follow the general instructions for how to <a title="Magento Custom Module Part I" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/" target="_blank">create a custom module</a> if you don&#8217;t already know how to do so.</p>
<pre class="php" name="code">
class Spinonesolutions_General_Model_Paygate_Authorizenet extends Mage_Paygate_Model_Authorizenet {
//Override Mage_Paygate_Model_Authorizenet

protected $_canRefund = true;

public function refund(Varien_Object $payment, $amount) {
  $error = false;
  if ($payment-&gt;getRefundTransactionId() &amp;&amp; $amount&gt;0) {
    $payment-&gt;setAnetTransType(self::REQUEST_TYPE_CREDIT);
    $request = $this-&gt;_buildRequest($payment, $amount);
    $request-&gt;setXTransId($payment-&gt;getRefundTransactionId());
    $result = $this-&gt;_postRequest($request);
    if ($result-&gt;getResponseCode()==self::RESPONSE_CODE_APPROVED) {
      $payment-&gt;setStatus(self::STATUS_SUCCESS);
    } else {
      $error = $result-&gt;getResponseReasonText();
    }
  } else {
    $error = Mage::helper('paygate')-&gt;__('Error in refunding the payment');
  }

  if ($error !== false) {
    Mage::throwException($error);
  }

  return $this;
}
}</pre>
<p>Now all we need to do is let Magento know that we&#8217;ve got a custom model.  In config.xml add</p>
<pre class="xml" name="code">
&lt;?xml version="1.0"?&gt;
&lt;config&gt;
  &lt;modules&gt;
    &lt;Spinonesolutions_General&gt;
      &lt;version&gt;0.0.1&lt;/version&gt;
    &lt;/Spinonesolutions_General&gt;
  &lt;/modules&gt;
  &lt;global&gt;
    &lt;models&gt;
      ...
      &lt;paygate&gt;
        &lt;rewrite&gt;
          &lt;authorizenet&gt;Imagistic_General_Model_Paygate_Authorizenet&lt;/authorizenet&gt;
        &lt;/rewrite&gt;
      &lt;/paygate&gt;
      ...
    &lt;/models&gt;
  &lt;/global&gt;
&lt;/config&gt;
</pre>
<p>That should do it!  Questions and comments welcome.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2010/01/partial-refund-amount-correction-for-authorize-net/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Custom Magento Grid with Ambiguous Column on Filter</title>
		<link>http://www.spinonesolutions.com/2010/01/custom-magento-grid-with-ambiguous-column-on-filter/</link>
		<comments>http://www.spinonesolutions.com/2010/01/custom-magento-grid-with-ambiguous-column-on-filter/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 17:14:58 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=486</guid>
		<description><![CDATA[The Problem You&#8217;ve built a custom grid and the source collection has an ambiguous column in the where clause when applying a filter. The Solution I recently ran into this problem while I was building a custom grid.  The situation should be familiar to many of you.  I have a custom table storing addresses; for the [...]]]></description>
			<content:encoded><![CDATA[<h3>The Problem</h3>
<p>You&#8217;ve built a custom grid and the source collection has an ambiguous column in the where clause when applying a filter.</p>
<h3>The Solution</h3>
<p>I recently ran into this problem while I was building a custom grid.  The situation should be familiar to many of you.  I have a custom table storing addresses; for the region and country I store a region_id and country_id, respectively.  For display of the grid however I want to do a lookup to show the user the friendly names for both region and country.  To accomplish that I do an inner join on the original collection which yields the full dataset.</p>
<p>However, this also introduces a douplicate column into the collection which was causeing an ambigous column error in my SQL statement whenever I applied a filter (a WHERE clause).  Lets take a look at an example.</p>
<p>My tables:</p>
<p><span style="text-decoration: underline;">retailer</span></p>
<ul>
<li>id</li>
<li>street</li>
<li>city</li>
<li>postcode</li>
<li>country_id</li>
<li>region_id</li>
</ul>
<p><span style="text-decoration: underline;">directory_country_region</span></p>
<ul>
<li>region_id</li>
<li>country_id</li>
<li>code</li>
<li>default_name</li>
</ul>
<p>Now lets take a look at my <strong>_prepareCollection</strong> method.</p>
<pre class="php" name="code">
protected function _prepareCollection() {
  $collection = Mage::getResourceModel('spinonesolutions_storelocator/retailer_collection');
  $collection-&gt;getSelect()
    -&gt;join(array('dcr' =&gt; 'directory_country_region'),'dcr.region_id = main_table.region_id')
    -&gt;reset(Zend_Db_Select::COLUMNS)
    -&gt;columns(array('id','name','street','city','country_id'))
    -&gt;columns(array('default_name'),'dcr');
  $this-&gt;setCollection($collection);
  return parent::_prepareCollection();
}</pre>
<p>This will result in a collection containing all the data that I need for a friendly presentation.  However, if I want to filter by country_id (which I do!) then I&#8217;m going to get an ambiguous column error in my SQL if I simply add &#8220;WHERE country_id = ?&#8221;.  That&#8217;s because country_id is in both tables.</p>
<p>The solution is the &#8220;filter_index&#8221; property.  Here&#8217;s a snippet from <strong>_prepareColumns</strong> which generates the country column and uses filter_index.</p>
<pre class="php" name="code">
$this-&gt;addColumn('country_id', array(
  'header' =&gt; Mage::helper('spinonesolutions_storelocator')-&gt;__('Country'),
  'width' =&gt; '100px',
  'index' =&gt; 'country_id',
  'filter_index' =&gt; 'main_table.country_id',
  'type' =&gt; 'country', ));
</pre>
<p>Now when the SQL statement is generated the ambiguous column error will be fixed since I&#8217;ve provided enough specificity.  Simple!</p>
<p>Questions and comments welcome!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2010/01/custom-magento-grid-with-ambiguous-column-on-filter/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Magento &#8211; Enable Paygate Refunds</title>
		<link>http://www.spinonesolutions.com/2009/10/magento-enable-paygate-refunds/</link>
		<comments>http://www.spinonesolutions.com/2009/10/magento-enable-paygate-refunds/#comments</comments>
		<pubDate>Wed, 28 Oct 2009 21:43:26 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Model]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=435</guid>
		<description><![CDATA[The Problem: You want to issue a refund, electronically, through your payment gateway from Magento. The Solution: By default, the community edition of Magento has refunds disabled in the Paygate Models.  In order to enable them you need to create a custom Model and overwrite a protected variable.  Refer to Part IV: Extending Models for [...]]]></description>
			<content:encoded><![CDATA[<h3>The Problem:</h3>
<p>You want to issue a refund, electronically, through your payment gateway from Magento.</p>
<h3>The Solution:</h3>
<p>By default, the community edition of Magento has refunds disabled in the Paygate Models.  In order to enable them you need to create a custom Model and overwrite a protected variable.  Refer to <a title="Part IV - Extending Models" href="http://www.spinonesolutions.com/2009/10/magento-part-i…tending-models/">Part IV: Extending Models</a> for more details on extending a Model.</p>
<p>First, lets take a look at the native Model so that you can understand what&#8217;s standing in the way.  I&#8217;m going to use the Authorize.net gateway as my example.</p>
<p>The top portion of <strong>Mage_Paygate_Model_Authorizenet</strong> (/Mage/Paygate/Model/Authorizenet.php)</p>
<pre class="php" name="code">class Mage_Paygate_Model_Authorizenet extends Mage_Payment_Model_Method_Cc
{
    const CGI_URL = 'https://secure.authorize.net/gateway/transact.dll';

    const REQUEST_METHOD_CC     = 'CC';
    const REQUEST_METHOD_ECHECK = 'ECHECK';

    const REQUEST_TYPE_AUTH_CAPTURE = 'AUTH_CAPTURE';
    const REQUEST_TYPE_AUTH_ONLY    = 'AUTH_ONLY';
    const REQUEST_TYPE_CAPTURE_ONLY = 'CAPTURE_ONLY';
    const REQUEST_TYPE_CREDIT       = 'CREDIT';
    const REQUEST_TYPE_VOID         = 'VOID';
    const REQUEST_TYPE_PRIOR_AUTH_CAPTURE = 'PRIOR_AUTH_CAPTURE';

    const ECHECK_ACCT_TYPE_CHECKING = 'CHECKING';
    const ECHECK_ACCT_TYPE_BUSINESS = 'BUSINESSCHECKING';
    const ECHECK_ACCT_TYPE_SAVINGS  = 'SAVINGS';

    const ECHECK_TRANS_TYPE_CCD = 'CCD';
    const ECHECK_TRANS_TYPE_PPD = 'PPD';
    const ECHECK_TRANS_TYPE_TEL = 'TEL';
    const ECHECK_TRANS_TYPE_WEB = 'WEB';

    const RESPONSE_DELIM_CHAR = ',';

    const RESPONSE_CODE_APPROVED = 1;
    const RESPONSE_CODE_DECLINED = 2;
    const RESPONSE_CODE_ERROR    = 3;
    const RESPONSE_CODE_HELD     = 4;

    protected $_code  = 'authorizenet';

    /**
     * Availability options
     */
    protected $_isGateway               = true;
    protected $_canAuthorize            = true;
    protected $_canCapture              = true;
    protected $_canCapturePartial       = false;
    protected $_canRefund               = false;
    protected $_canVoid                 = true;
    protected $_canUseInternal          = true;
    protected $_canUseCheckout          = true;
    protected $_canUseForMultishipping  = true;
    protected $_canSaveCc = false;

    protected $_allowCurrencyCode = array('USD');</pre>
<p>The variable that we&#8217;re interested in is <strong>_canRefund</strong>.  The game plan is to create a custom Model that overrides <strong>Mage_Paygate_Model_Authorizenet</strong> and in that Model, override <strong>_canRefund</strong>.  If you&#8217;re thinking, why not just change false to true right here, right now!  Well my friend, you certainly could, but you&#8217;d certainly break your upgrade path too, check out <a title="Part IV - Extending Models" href="../2009/10/magento-part-i%E2%80%A6tending-models/">Part IV: Extending Models</a> and <a title="Part I - Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Part I: Custom Module</a>.</p>
<p>I&#8217;m going to create the class: <strong>Spinonesolutions_Paygate_Model_Authorizenet</strong> (/local/Spineonesolutions/Paygate/Model/Authorizenet.php) and extend <strong>Mage_Paygate_Model_Authorizenet</strong>.  Now I just override <strong>_canRefund</strong> and we&#8217;re halfway there.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Paygate_Model_Authorizenet extends Mage_Paygate_Model_Authorizenet
{
 //Override Mage_Paygate_Model_Authorizenet
 protected $_canRefund = true;
}
?&gt;</pre>
<p>If you read <a title="Part IV - Extending Models" href="../2009/10/magento-part-i%E2%80%A6tending-models/">Part IV: Extending Models</a> you&#8217;ll know that there&#8217;s one last step, and that is to modify config.xml so that our custom Model overrides the native Model.</p>
<p>/Spinonesolutions/Paygate/etc/config.xml</p>
<pre class="xml" name="code">&lt;global&gt;
  &lt;models&gt;
  ...
    &lt;paygate&gt;
      &lt;rewrite&gt;
        &lt;authorizenet&gt;Spinonesolutions_Paygate_Model_Authorizenet&lt;/authorizenet&gt;
      &lt;/rewrite&gt;
    &lt;/paygate&gt;
  ...
  &lt;/models&gt;
&lt;/global&gt;</pre>
<p>That should do it!  If you&#8217;ve got everything else setup correctly this should enable the Model to post refunds electronically to your gateway.  Of course, if you want to enable more than one gateway, or perhaps a gateway that&#8217;s not Authorize.net you can do that by just following the same steps above with the Model for the gateway of your choice.</p>
<p>As always, questions and comments are welcome!</p>
<h3>Update: From Commenter Cindy</h3>
<p>Afer doing the above (and not forgetting to fill in app/etc/modules/MyNameSpace_All.xml to activate the new module), I still wasn’t getting authorize.net to really refund the cc.</p>
<p>I found that I needed to add the CC to the request, and then add the actual amount to be refunded. Now issuing a credit memo on the invoice will cause authorize.net to issue a refund to the CC.</p>
<p>Also, it appears that the ‘refund’ button only shows up if you click credit memo from an invoice – it doesn’t show up if you click credit memo on an order (only ‘refund offline’ shows up then).</p>
<p>Anyway, this is the code to make this work for me:</p>
<pre class="php" name="code">//
// Override Mage_Paygate_Model_Authorizenet
//    enable online refunds via credit memos
//    enable partial refunds
//
class Sbr_Paygate_Model_Authorizenet extends Mage_Paygate_Model_Authorizenet {
  protected $_canCapturePartial = true;
  protected $_canRefund = true;

  //
  // override _buildRequest to fill in the CC number as it is required yet not set peviously
  //
  protected function _buildRequest (Varien_Object $payment) {
    if (($payment-&gt;getAnetTransMethod() == self::REQUEST_METHOD_CC) &amp;&amp;($payment-&gt;getCcLast4()))
      $payment-&gt;setCcNumber($payment-&gt;getCcLast4());
   
      return parent::_buildRequest($payment);
  }

  // Override refund in order to fill out the amount
  //
  public function refund(Varien_Object $payment, $amount) {
    $error = false;
    if ($payment-&gt;getRefundTransactionId() &amp;&amp; $amount&gt;0) {
      $payment-&gt;setAnetTransType(self::REQUEST_TYPE_CREDIT);
      $request = $this-&gt;_buildRequest($payment);

      // set the amount (beginning of changes)
      $order = $payment-&gt;getOrder();
      $request-&gt;setXAmount($amount,2);
      $request-&gt;setXCurrencyCode($order-&gt;getBaseCurrencyCode());
      // (end of changes)

      $request-&gt;setXTransId($payment-&gt;getRefundTransactionId());
      $result = $this-&gt;_postRequest($request);

      if ($result-&gt;getResponseCode() == self::RESPONSE_CODE_APPROVED) {
        $payment-&gt;setStatus(self::STATUS_SUCCESS);
      } else {
        $error = $result-&gt;getResponseReasonText();
      }
    } else {
      $error = Mage::helper(’paygate’)-&gt;__(’Error in refunding the payment’);
    }

    if ($error !== false) {
      Mage::throwException($error);
    }

    return $this;
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2009/10/magento-enable-paygate-refunds/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Magento &#8211; Part IV : Extending Models</title>
		<link>http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/</link>
		<comments>http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/#comments</comments>
		<pubDate>Thu, 22 Oct 2009 15:09:55 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Models]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=413</guid>
		<description><![CDATA[The Problem: You want to expand upon or change some functionality within Magento. The Solution: Extend a Model!  I’m going to use a real world example here because I believe that examples are the best tools for teaching. I was faced with a request for additional functionality in regards to the Authorize.net payment method.  Basically, [...]]]></description>
			<content:encoded><![CDATA[<h2>The Problem:</h2>
<p>You want to expand upon or change some functionality within Magento.</p>
<h2>The Solution:</h2>
<p>Extend a Model!  I’m going to use a real world example here because I believe that examples are the best tools for teaching.</p>
<p>I was faced with a request for additional functionality in regards to the Authorize.net payment method.  Basically, the customer wanted an email to be sent to an administrator if the gateway experienced an error while trying to processes a payment; a very reasonable request.</p>
<p>To accomplish this I extended <strong>Mage_Paygate_Model_Authorizenet</strong>, the model responsible for Authorize.net.  There are two very important reasons why I went about it the way I did.</p>
<p>First, as I mentioned earlier, all custom code should go in the /local/ directory.  If I had made my changes directly to <strong>Mage_Paygate_Model_Authorizenet</strong> (/core/Mage/Paygate/Model/Authorizenet) then the modifications would be in serious danger if the core were ever updated, essentially breaking the upgrade path.</p>
<p>Secondly, by extending the core class I can make reference to anything contained in the parent class.  This is good OOP architecture; I’m just adorning the parent class with additional functionality, leveraging all the work that has come before and maintaining good compartmentalization.</p>
<p>There are two basic steps to extending a core class.  Firstly, you need to create your new (custom) class in a custom Module living in /local/.  I’ll continue expanding upon my previous examples and make a new Model within Spinonesolutions/Helloworld.  I’m going to put the new Model in its own folder, mirroring the structure of the core.</p>
<p>Parent Class Path:<br />
/Mage/Paygate/Model/Authorizenet.php</p>
<p>Custom Class Path:<br />
/Spinonesolutions/Helloworld/Model/Paygate/Authorizenet.php</p>
<p>Naming conventions dictate that my custom Class’s name be:<br />
<strong>Spinonesolutions_Helloworld_Model_Paygate_Authorizenet</strong></p>
<p>Let’s jump right into the source and dissect what’s going on.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Helloworld_Model_Paygate_Authorizenet extends Mage_Paygate_Model_Authorizenet {

  private $_order;

  function __construct() {
    parent::__construct();     
  }

  public function capture(Varien_Object $payment, $amount) {
    $this-&gt;_order = $payment-&gt;getOrder();
    try {
      parent::capture($payment,$amount);
    } catch (Exception $error) {
      $this-&gt;_sendEmail($error);
      throw $error;
    }           
    return $this;
  }
}</pre>
<p>The declaration is import to note because this is where we do the actual extending.  Although it’s not necessary for this example I’ve included a call to the constructor, and placed a call to the parent constructor within it.  This way, both the custom and parent model are instantiated correctly.</p>
<p>OK, onto the meat, the <strong>capture()</strong> function.  To fully understand this we need to understand what we’re trying to extend.  Looking at <strong>Mage_Paygate_Model_Authorizenet</strong> (/Mage/Paygate/Model/Authorizenet.php) will yield the answer.</p>
<p>As you can see <strong>Mage_Paygate_Model_Authorizenet</strong> contains a capture method with the exact same signature: <strong>public function capture(Varien_Object $payment, $amount)</strong>.  What I’m doing is overriding this method with my method.  I add my additional functionality and then call the parent method at the appropriate time to drop the system back into its default workflow.</p>
<p>Notice that all I’m doing is wrapping the parent method call in a try catch block so that if an Exception is thrown I can catch it and send an email.  I’ve successfully extended the core functionality without touching a line of the core code!</p>
<p>I’ve left out one small, but hugely important bit of information.  In order to get this to work we have to let Magento know that there’s a custom Model which overrides this particular Model.  To accomplish this we turn to the <strong>config.xml</strong>.</p>
<pre class="xml" name="code">&lt;?xml version=<em>"1.0"</em> encoding=<em>"UTF-8"</em> ?&gt;
&lt;config&gt;
      &lt;modules&gt;
            &lt;Spinonesolutions_Helloworld&gt;
                  &lt;version&gt;0.1.0&lt;/version&gt;
            &lt;/Spinonesolutions_Helloworld&gt;
      &lt;/modules&gt;
    &lt;frontend&gt;
        &lt;routers&gt;
            &lt;spinonesolutions&gt;      &lt;!-- I make this match my front name but I'm not sure it matters --&gt;
                &lt;use&gt;standard&lt;/use&gt; &lt;!-- Use standard routing as opposed to admin.  IE: frontend vs admin --&gt;
                &lt;args&gt;
                    &lt;module&gt;Spinonesolutions_Helloworld&lt;/module&gt;  &lt;!-- The module to search for controllers --&gt;
                    &lt;frontName&gt;spinonesolutions&lt;/frontName&gt; &lt;!-- The first descriminator in the path.  "spinonesolutions" in http://localhost/spinonesolutions/ --&gt;
                &lt;/args&gt;
            &lt;/spinonesolutions&gt;
        &lt;/routers&gt;
    &lt;/frontend&gt;
      &lt;global&gt;
            &lt;models&gt;
                  &lt;spinonesolutions_helloworld&gt; &lt;!-- This is used when istanting your Model EG: Mage::getModel("spinonesolutions_helloworld/hellworld") --&gt;
                        &lt;class&gt;Spinonesolutions_Helloworld_Model&lt;/class&gt;      &lt;!-- That class to use when isntanting objects of type above. --&gt;
                  &lt;/spinonesolutions_helloworld&gt;
              &lt;paygate&gt;
                  &lt;rewrite&gt;
                        &lt;authorizenet&gt;Spinonesolutions_Helloworld_Model_Paygate_Authorizenet&lt;/authorizenet&gt;
                  &lt;/rewrite&gt;
              &lt;/paygate&gt;
            &lt;/models&gt;
      &lt;/global&gt;
&lt;/config&gt;</pre>
<p>Notice line:24 and the <strong>paygate</strong> element.  This is the element that is telling Magento to now look in our custom class for the Model, and not the core.</p>
<p>That’s all there is to it!  You can now extend Magento’s core functionality to your heart’s content.</p>
<p>I bet you astute readers out there noticed that ALL of the models thus far have extended a core class.  My first Model <strong>Spinonesolutions_Helloworld_Model_Helloworld</strong> extends <strong>Varien_Object</strong>.  Even the controllers extend <strong>Mage_Core_Controller_Front_Action</strong>.</p>
<p>Oh Snap!</p>
<p>Download the source &#8211; <a href="http://www.spinonesolutions.com/wp-content/uploads/2009/10/Helloworld_v3.rar">Helloworld_v3</a></p>
<ol>
<li><a title="Part I : Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Part I : Custom Module</a></li>
<li><a title="Part II : Models" href="http://www.spinonesolutions.com/2009/10/magento-part-ii-models/">Part II : Models</a></li>
<li><a title="Part III : Controllers" href="http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/">Part III : Controllers</a></li>
<li><a title="Part IV : Extending Models" href="http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/">Part IV : Extending Models</a></li>
<li><a title="Part V: Data Layer" href="http://www.spinonesolutions.com/2009/10/magento-part-v-data-layer/">Part V : Data Layer</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Magento &#8211; Part III : Controllers</title>
		<link>http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/</link>
		<comments>http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/#comments</comments>
		<pubDate>Wed, 07 Oct 2009 16:38:05 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Controller]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=392</guid>
		<description><![CDATA[The Problem: I need to write Part III of the Magento series. The Solutions: This article&#8230; TADA! First, let’s talk about the controller.  The default controller for a module is always named IndexController.php.  If you haven’t requested a specific controller (in the URL), than the system is going to look for IndexController.php and load it.  [...]]]></description>
			<content:encoded><![CDATA[<h2>The Problem:</h2>
<p>I need to write Part III of the Magento series.</p>
<h2>The Solutions:</h2>
<p>This article&#8230; TADA!</p>
<p>First, let’s talk about the controller.  The default controller for a module is always named <strong>IndexController.php</strong>.  If you haven’t requested a specific controller (in the URL), than the system is going to look for <strong>IndexController.php</strong> and load it.  Similarly, the default Magento action is <strong>indexAction()</strong>.</p>
<p>Applying this logic to my custom Module; if I simply type {base}/spinonesolutions/ into the address bar, Magento is going to load up <strong>Spinonesolutions_Helloworld_IndexController</strong> and invoke <strong>indexAction()</strong>.  Remember that “spinonesolutions” is the frontname that I defined for this module in the <strong>config.xml</strong> (See <a title="Part II : Models" href="http://www.spinonesolutions.com/2009/10/magento-part-ii-models/">Part II : Models</a> for source).</p>
<pre class="php" name="code">&lt;?php
//IndexController is the default controller
//EG: http://localhost/spinonesolutions/
//Notice there's no parameters being passed as a parameter (Nothing after trailing "/")
//IndexController will be called since it's the default.
//"spinonesolutions" is the frontname as defined in confg.xml
class Spinonesolutions_Helloworld_IndexController extends Mage_Core_Controller_Front_Action {
      //indexAction is the default Action for any controller
      function indexAction() {
            echo "indexAction";
            $helloworld = Mage::getModel("spinonesolutions_helloworld/helloworld");
            $helloworld-&gt;helloworld("helloworld");
      }
}</pre>
<pre class="xml" name="code">&lt;?xml version=<em>"1.0"</em> encoding=<em>"UTF-8"</em> ?&gt;
&lt;config&gt;
      &lt;modules&gt;
            &lt;Spinonesolutions_Helloworld&gt;
                  &lt;version&gt;0.1.0&lt;/version&gt;
            &lt;/Spinonesolutions_Helloworld&gt;
      &lt;/modules&gt;
    &lt;frontend&gt;
        &lt;routers&gt;
            &lt;spinonesolutions&gt;      &lt;!-- I make this match my front name but I'm not sure it matters --&gt;
                &lt;use&gt;standard&lt;/use&gt; &lt;!-- Use standard routing as opposed to admin.  IE: frontend vs admin --&gt;
                &lt;args&gt;
                    &lt;module&gt;Spinonesolutions_Helloworld&lt;/module&gt;  &lt;!-- The module to search for controllers --&gt;
                    &lt;frontName&gt;spinonesolutions&lt;/frontName&gt; &lt;!-- The first descriminator in the path.  "spinonesolutions" in http://localhost/spinonesolutions/ --&gt;
                &lt;/args&gt;
            &lt;/spinonesolutions&gt;
        &lt;/routers&gt;
    &lt;/frontend&gt;
      &lt;global&gt;
            &lt;models&gt;
                  &lt;spinonesolutions_helloworld&gt; &lt;!-- This is used when istanting your Model EG: Mage::getModel("spinonesolutions_helloworld/hellworld") --&gt;
                        &lt;class&gt;Spinonesolutions_Helloworld_Model&lt;/class&gt;      &lt;!-- That class to use when isntanting objects of type above. --&gt;
                  &lt;/spinonesolutions_helloworld&gt;
            &lt;/models&gt;
      &lt;/global&gt;
&lt;/config&gt;</pre>
<p>Let’s start adding pieces onto our URL and explore what happens in Magento.  Adding one piece yields {base}/spinonesolutions/foo/.  This is asking Magento to invoke the <strong>indexAction()</strong> of the <strong>FooController</strong>, more specifically <strong>Spinonesolutions_Helloworld_FooController</strong>.</p>
<p>That Class doesn’t exist in our current code base so we need to create it.  Following Magento’s naming conventions and our choices in config.xml the filename and path for <strong>Spinonesolutions_Helloworld_FooController</strong> must be: <strong>/Spinonesolutions/Helloworld/controllers/FooController.php</strong>.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Helloworld_FooController extends Mage_Core_Controller_Front_Action {
      //indexAction is the default Action for any controller
      function indexAction() {
            echo "Spinonesolutions_Helloworld_FooController.indexAction()";
      }    
}</pre>
<p>Now we’ll take it to the next level and add another piece to the URL yielding {base}/spinonesolutions/foo/bar/.  This is asking Magento to invoke the <strong>barAction()</strong> of the <strong>FooController</strong> (<strong>Spinonesolutions_Helloworld_FooController</strong>). Let’s add that action to our growing controller.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Helloworld_FooController extends Mage_Core_Controller_Front_Action {
      //indexAction is the default Action for any controller
      function indexAction() {
            echo "Spinonesolutions_Helloworld_FooController.indexAction()";
      }

      //{base}/spinonesolutions/bar/
      function barAction() {
            echo "Spinonesolutions_Helloworld_FooController.barAction()";
      }
}</pre>
<p>That covers the basics for controllers.  Setup your route in the <strong>config.xml</strong> then follow the proper naming conventions when creating your classes and you should be all set.</p>
<p>Download the source &#8211; <a href="http://www.spinonesolutions.com/wp-content/uploads/2009/10/Helloworld_v2.rar">Helloworld_v2</a></p>
<ol>
<li><a title="Part I : Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Part I : Custom Module</a></li>
<li><a title="Part II : Models" href="http://www.spinonesolutions.com/2009/10/magento-part-ii-models/">Part II : Models</a></li>
<li><a title="Part III : Controllers" href="http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/">Part III : Controllers</a></li>
<li><a title="Part IV : Extending Models" href="http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/">Part IV : Extending Models</a></li>
<li><a title="Part V: Data Layer" href="http://www.spinonesolutions.com/2009/10/magento-part-v-data-layer/">Part V : Data Layer</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Magento &#8211; Part II : Models</title>
		<link>http://www.spinonesolutions.com/2009/10/magento-part-ii-models/</link>
		<comments>http://www.spinonesolutions.com/2009/10/magento-part-ii-models/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 16:18:49 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Model]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=362</guid>
		<description><![CDATA[The Problem: You need to work with Magento Models, how chic! The Solution: Well, let&#8217;s be honest&#8230; I don&#8217;t have THE solution, but I do have some useful snippets of information to pass along. Models are the meat of an MVC application.  They contain the definitions for your application&#8217;s components.  Stop for a moment and [...]]]></description>
			<content:encoded><![CDATA[<h2>The Problem:</h2>
<p>You need to work with Magento Models, how chic!</p>
<h2>The Solution:</h2>
<p>Well, let&#8217;s be honest&#8230; I don&#8217;t have THE solution, but I do have some useful snippets of information to pass along.</p>
<p>Models are the meat of an MVC application.  They contain the definitions for your application&#8217;s components.  Stop for a moment and think conceptually about your application.  Anything that is represented by a database table is most likely going to be a Model.  Anything that can be mentally packaged up and labeled could be a Model.  How are Models built, used, and made use of in Magento?  Let&#8217;s find out.</p>
<p>In the Previous <a title="Part I - Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">post</a> I showed you how to create a custom Module and one of the main pieces of that Module was the Model <strong>Helloworld</strong>.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Helloworld_Model_Helloworld extends Varien_Object {
  function __construct() {
    parent::__construct();
  }

  function helloworld($arg) {
    echo "&lt;br&gt;Hello World! My argument is : " . $arg;
  }
}</pre>
<p>I&#8217;m going to examine it now, in more detail, to flush out some of the intricacies of Magento.</p>
<p>Notice the class definition:</p>
<p><strong>Spinonesolutions_Helloworld_Model_Helloworld</strong></p>
<p>It&#8217;s important to maintain this naming convention since Magento uses an autoloader to load class files when they&#8217;re called for.  If they&#8217;re not named correctly it will look at the wrong place in the file system for the file which SHOULD contain the class it&#8217;s looking for, but alas it will not be there, and thus; it will not find it and you will receive an error.</p>
<p>Deconstructing the above:</p>
<p><strong>Spinonsolutions_Helloworld_Model_Helloworld</strong><br />
{Namespace}_{Module}_Model_{Model}.php</p>
<p>Let’s look at some permutations to get a deeper understanding:</p>
<p><strong>Spinonesolutions_Helloworld_Model_Worldhello</strong><br />
\Spinonesolutions\Helloworld\Model\Worldhello.php</p>
<p><strong>Spinonesolutions_Helloworld_Model_Foo_Bar<br />
</strong>\Spinonesolutions\Helloworld\Model\Foo\Bar.php</p>
<p><strong>Spinonesolutions_Helloworld_Model_Foo_Helloworld<br />
</strong>\Spinonesolutions\Helloworld\Model\Foo\Helloworld.php</p>
<p>Picking up the pattern?  Good!</p>
<p> The _<strong>_consruct</strong> method is called when Magento builds an instance of this Model.  My constructors always contain, at least, a call to the parent constructor and sometimes more depending on the Model.</p>
<p> To instantiate an instance of a Model use <strong>Mage::getModel($modelClass,$arguments)</strong> or <strong>Mage::getSingleton($modelClass,$arguments)</strong>.  The parameter <strong>$modelClass</strong> is a string, the construction of which is partially determined by the <strong>config.xml</strong> and partly by the Model’s class definition.  Let’s look at <strong>IndexController</strong> from <a title="Part I: Magento Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Part I</a>, line 23.</p>
<pre class="php" name="code">&lt;?php
//IndexController is the default controller
//EG: http://localhost/spinonesolutions/
//Notice there's no parameters being passed as a parameter (Nothing after trailing "/")
//IndexController will be called since it's the default.
//"spinonesolutions" is the frontname as defined in confg.xml
class Spinonesolutions_Helloworld_IndexController extends Mage_Core_Controller_Front_Action {

  //indexAction is the default Action for any controller
  function indexAction() {
    echo "indexAction";
    $helloworld = Mage::getModel("spinonesolutions_helloworld/helloworld");
    $helloworld-&gt;helloworld("helloworld");
  }
}</pre>
<p>The piece of <strong>$modelClass</strong> that comes before the backslash (spinonesolutions_helloworld) is defined in the <strong>config.xml</strong> file included in the <strong>etc</strong> directory of your Module.</p>
<pre class="xml" name="code">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;config&gt;
&lt;modules&gt;
&lt;Spinonesolutions_Helloworld&gt;
&lt;version&gt;0.1.0&lt;/version&gt;
&lt;/Spinonesolutions_Helloworld&gt;
&lt;/modules&gt;
&lt;frontend&gt;
&lt;routers&gt;
&lt;spinonesolutions&gt; &lt;!-- I make this match my front name but I'm not sure it matters --&gt;
&lt;use&gt;standard&lt;/use&gt; &lt;!-- Use standard routing as opposed to admin. IE: frontend vs admin --&gt;
&lt;args&gt;
&lt;module&gt;Spinonesolutions_Helloworld&lt;/module&gt; &lt;!-- The module to search for controllers --&gt;
&lt;frontName&gt;spinonesolutions&lt;/frontName&gt; &lt;!-- The first descriminator in the path. "spinonesolutions" in http://localhost/spinonesolutions/ --&gt;
&lt;/args&gt;
&lt;/spinonesolutions&gt;
&lt;/routers&gt;
&lt;/frontend&gt;
&lt;global&gt;
&lt;models&gt;
&lt;spinonesolutions_helloworld&gt; &lt;!-- This is used when istanting your Model EG: Mage::getModel("spinonesolutions_helloworld/hellworld") --&gt;
&lt;class&gt;Spinonesolutions_Helloworld_Model&lt;/class&gt; &lt;!-- That class to use when isntanting objects of type above. --&gt;
&lt;/spinonesolutions_helloworld&gt;
&lt;/models&gt;
&lt;/global&gt;
&lt;/config&gt;</pre>
<p>Notice the <strong>models</strong> element.  It’s defining a label for the class (and folder) <strong>Spinonesolutions_Helloworld_Model</strong>.  Said in another way; given <strong>spinonesolutions_helloworld</strong>, the <strong>models</strong> element tells Magento what class and, by naming convention, what folder to look in for the second piece; that which comes after the backslash.</p>
<p>The piece that comes after the backslash (helloworld) tells Magento what particular Model you’re looking for.  In my example, it’s simply <strong>helloworld</strong>, so the autoloader is going to be looking for a file named <strong>Helloworld.php</strong> that contains a class definition <strong>Spinonesolutions_Helloworld_Model_Helloworld</strong>.  Lets extend the permutations that we used earlier adding how to instantiate each.</p>
<p><strong>Spinonesolutions_Helloworld_Model_Worldhello</strong><br />
\Spinonesolutions\Helloworld\Model\Worldhello.php<br />
$objModel = Mage::getModel(‘spinonesolutions_helloworld/worldhello’);</p>
<p><strong>Spinonesolutions_Helloworld_Model_Foo_Bar<br />
</strong>\Spinonesolutions\Helloworld\Model\Foo\Bar.php<br />
$objModel = Mage::getModel(‘spinonesolutions_helloworld/foo_bar’);</p>
<p><strong>Spinonesolutions_Helloworld_Model_Foo_Helloworld<br />
</strong>\Spinonesolutions\Helloworld\Model\Foo\Helloworld.php<br />
$objModel = Mage::getModel(‘spinonesolutions_helloworld/foo_helloworld’);</p>
<p>A great way to get a handle on how the naming conventions work is to meander through the <strong>core</strong> and note of name of the file and the folder structure it’s been placed in; then compare that to the class definition contained within.</p>
<p>One more time; since it took me a while to figure this out!</p>
<p><span style="color: #ff0000;">\Spinonesolutions\Helloworld\Model</span>\<span style="color: #3366ff;">Helloworld.php</span></p>
<p><span style="color: #ff0000;">Spinonesolutions_Helloworld_Model</span>_<span style="color: #3366ff;">Helloworld</span></p>
<p><span style="color: #339966;">&lt;spinonesolutions_helloworld&gt;</span><br />
    &lt;class&gt;<span style="color: #ff0000;">Spinonesolutions_Helloworld_Model</span>&lt;/class&gt;<br />
<span style="color: #339966;">&lt;/spinonesolutions_helloworld&gt;<strong></strong></span></p>
<p>$objModel = Mage::getModel(‘<span style="color: #339966;">spinonesolutions_helloworld</span>/<span style="color: #3366ff;">helloworld</span>);</p>
<p>Do you see it!  Do you!?</p>
<p>One awesome, and very useful, property of Models is that they can extend any other Model you want.  This is, in general, how you extend <strong>core</strong> Models with your own functionality.</p>
<p>This is absolutely essential when you want to add on to the Magento code base.  If you just fire up your text editor and make your changes to the <strong>core,</strong> the next time you upgrade Magento, there’s a relatively good chance you’ll lose all your work.</p>
<p>That’s all for Models! Not really though, I’ve just covered the basics here, read on for more as I cover extending!</p>
<p>Download the source &#8211; <a href="http://www.spinonesolutions.com/wp-content/uploads/2009/09/Helloworld.rar">Helloworld</a></p>
<ol>
<li><a title="Part I : Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Part I : Custom Module</a></li>
<li><a title="Part II : Models" href="http://www.spinonesolutions.com/2009/10/magento-part-ii-models/">Part II : Models</a></li>
<li><a title="Part III : Controllers" href="http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/">Part III : Controllers</a></li>
<li><a title="Part IV : Extending Models" href="http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/">Part IV : Extending Models</a></li>
<li><a title="Part V: Data Layer" href="http://www.spinonesolutions.com/2009/10/magento-part-v-data-layer/">Part V : Data Layer</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2009/10/magento-part-ii-models/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Magento &#8211; Part I : Custom Module</title>
		<link>http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/</link>
		<comments>http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 04:00:01 +0000</pubDate>
		<dc:creator>Will Wright</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Module]]></category>

		<guid isPermaLink="false">http://www.spinonesolutions.com/?p=336</guid>
		<description><![CDATA[This article was written while using Magento v1.3.2.3. Greetings!  This is installment number one of the the exiting Magento Custom Module series.  There&#8217;s basically two paths to the end of the fairytale: Path 1: You follow this series and get down and dirty with Magento, learning a lot along the way. Path 2: You go [...]]]></description>
			<content:encoded><![CDATA[<p>This article was written while using Magento v1.3.2.3.</p>
<p>Greetings!  This is installment number one of the the exiting <em>Magento Custom Module</em> series.  There&#8217;s basically two paths to the end of the fairytale:</p>
<p>Path 1: You follow this series and get down and dirty with Magento, learning a lot along the way.</p>
<p>Path 2: You go out and download the Module Creator (<a href="http://www.magentocommerce.com/wiki/custom_module_with_custom_database_table">http://www.magentocommerce.com/wiki/custom_module_with_custom_database_table</a>) and head straight to GO!</p>
<p>Your choice!</p>
<p>Note: Magento is an MVC application so I&#8217;m going to assume you have a basic understanding of <a title="MVC" href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" target="_blank">MVC</a>.  Also, Magento is built (I believe) on the <a title="Zend Framework" href="http://framework.zend.com/" target="_blank">Zend framework</a>, so there&#8217;s some Zend specific syntax that I&#8217;m also not going to dive into.  Huzzzah!</p>
<h2>The Problem:</h2>
<p>You need to create a custom Module in Magento!  Why&#8230;?  Hell, I don&#8217;t know; you just do.  That&#8217;s enough of that, let&#8217;s get down to it.</p>
<h2>The Solution:</h2>
<p>All of your customizations need to go into the [root]\app\code\local folder.  This is where Magento expects to find them and it&#8217;s also a factor in maintaining your upgrade path.  When the next version of Magento is released you&#8217;ll be able to backup your &#8220;local&#8221; folder and blow away everything else.</p>
<p>Before we get too far into this let’s setup our development environment, go to:<br />
Admin -&gt; System -&gt; Cache Management: Cache Control – All Cache = Disable</p>
<p>This will save you many hours of self-torture while you’re developing.</p>
<p>A couple of other developer settings that I’ve found helpful:<br />
Admin -&gt; System -&gt; Configuration -&gt; Developer: Debug &#8211; Profiler = yes<br />
Log Settings &#8211; Enabled = yes</p>
<p>Now open up [root]\index.php and at the very end you&#8217;ll see three lines that are commented out; un-comment them and save.</p>
<p>Finally, turn SEO friendly URLs on:<br />
Admin -&gt; System -&gt; Configuration -&gt; Web: Search Engine Optimization &#8211; Use Web Server Rewrites = Yes</p>
<p>All of the URLs that I’ll be presenting as examples below assume that SEO friendly URLs are enabled.</p>
<p>Magento is now correctly configured for development so let’s dive into building a custom module.</p>
<p>Sample file structure:<br />
[root]\app\code\local\{Namespace}\{Modulename}<br />
[root]\app\code\local\{Namespace}\{Modulename}\controllers<br />
[root]\app\code\local\{Namespace}\{Modulename}\etc<br />
[root]\app\code\local\{Namespace}\{Modulename}\etc\config.xml<br />
[root]\app\code\local\{Namespace}\{Modulename}\Helper<br />
[root]\app\code\local\{Namespace}\{Modulename}\Helper\Data.php<br />
[root]\app\code\local\{Namespace}\{Modulename}\Model<br />
[root]\app\code\local\{Namespace}\{Modulename}\{Modulename}.php</p>
<p><strong>{Namespace}</strong> is a user defined variable.  Basically it&#8217;s just a mechanism that allows the user to create disparate classes that would otherwise have the same names.<br />
<strong>{Modulename}</strong> this is the name of your module.</p>
<p>For my examples I&#8217;m going to use &#8220;Spinonesolutions&#8221; as my namespace and &#8220;Helloworld&#8221; as my Module.  I&#8217;m not entirely sure if it matters or not, but as a general rule I always capitalize the first letter and leave the rest lower case.  Don&#8217;t use underscores in your names either, we&#8217;ll see why later.</p>
<p><strong>controllers</strong> is where all the controllers go, crazy right?  I’ll get into the controller structure in another part of this series.  For now a sample will suffice.</p>
<pre class="php" name="code">&lt;?php
//IndexController is the default controller
//EG: http://localhost/spinonesolutions/
//Notice there's no parameters being passed as a parameter (Nothing after trailing "/")
//IndexController will be called since it's the default.
//"spinonesolutions" is the frontname as defined in confg.xml
class Spinonesolutions_Helloworld_IndexController extends Mage_Core_Controller_Front_Action {
  //indexAction is the default Action for any controller
  function indexAction() {
    echo "indexAction";
    $helloworld = Mage::getModel("spinonesolutions_helloworld/helloworld");
    $helloworld-&gt;helloworld("helloworld");
  }
}</pre>
<p><strong>Data.php</strong> is just a helper file for the Magento internals, I&#8217;m not exactly sure what it does but it appears to be exactly the same in every module except for its class definition.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Helloworld_Helper_Data extends Mage_Core_Helper_Abstract {

}</pre>
<p><strong>Model</strong> holds all of the models for your module.  It&#8217;s customary to name your main model after your {Modulename}.  You’ll most likely have at least one model and if you browse through the Magento core you&#8217;ll notice that most modules contain many models.</p>
<pre class="php" name="code">&lt;?php
class Spinonesolutions_Helloworld_Model_Helloworld extends Varien_Object {
  function __construct() {
    parent::__construct();
  }

  function helloworld($arg) {
    echo "&lt;br&gt;Hello World! My argument is : " . $arg;
  }
}</pre>
<p><strong>config.xml</strong> is where you connect all your code up to Magento.  There&#8217;s a lot of voodoo going on in here and I&#8217;m not entirely sure how it all works.  I might even write a full post on the <strong>config.xml</strong> as a forthcoming part of this series.</p>
<pre class="xml" name="code">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;config&gt;
 &lt;modules&gt;
   &lt;Spinonesolutions_Helloworld&gt;
   &lt;version&gt;0.1.0&lt;/version&gt;
   &lt;/Spinonesolutions_Helloworld&gt;
 &lt;/modules&gt;
 &lt;frontend&gt;
   &lt;routers&gt;
     &lt;spinonesolutions&gt;    &lt;!-- I make this match my front name but I'm not sure it matters --&gt;
       &lt;use&gt;standard&lt;/use&gt;    &lt;!-- Use standard routing as opposed to admin.  IE: frontend vs admin --&gt;
       &lt;args&gt;
         &lt;module&gt;Spinonesolutions_Helloworld&lt;/module&gt;    &lt;!-- The module to search for controllers --&gt;
         &lt;frontName&gt;spinonesolutions&lt;/frontName&gt;    &lt;!-- The first descriminator in the path.  "spinonesolutions" in http://localhost/spinonesolutions/ --&gt;
       &lt;/args&gt;
     &lt;/spinonesolutions&gt;
   &lt;/routers&gt;
 &lt;/frontend&gt;
 &lt;global&gt;
   &lt;models&gt;
     &lt;spinonesolutions_helloworld&gt;    &lt;!-- This is used when istanting your Model EG: Mage::getModel("spinonesolutions_helloworld/hellworld") --&gt;
       &lt;class&gt;Spinonesolutions_Helloworld_Model&lt;/class&gt;    &lt;!-- That class to use when isntanting objects of type above. --&gt;
     &lt;/spinonesolutions_helloworld&gt;
   &lt;/models&gt;
 &lt;/global&gt;
&lt;/config&gt;</pre>
<p>That takes care of all the code needed in the module directory. There’s one more configuration file we need to create and it’s an exception to the local folder rule:<br />
[root]\app\etc\modules\{Namespace}_{Modulename}.xml</p>
<pre class="xml" name="code">&lt;?xml version="1.0"?&gt;
&lt;config&gt;
 &lt;modules&gt;
   &lt;Spinonesolutions_Helloworld&gt;
     &lt;active&gt;true&lt;/active&gt;
     &lt;codePool&gt;local&lt;/codePool&gt;
   &lt;/Spinonesolutions_Helloworld&gt;
 &lt;/modules&gt;
&lt;/config&gt;</pre>
<p>This file tells Magento that there&#8217;s a new module out there that it needs to load.  Alternatively, you can name your file [root]\app\etc\modules\{Namespace}_All.xml to tell Magento to load up all the modules in that namespace.</p>
<p>Once you have this file in place you can navigate to Admin-&gt;System -&gt; Configuration -&gt; Advanced and you should see your module enabled here.</p>
<p>You now have a functioning custom module, hurray!</p>
<p>Download the source &#8211; <a href="http://www.spinonesolutions.com/wp-content/uploads/2009/09/Helloworld.rar">Helloworld</a></p>
<ol>
<li><a title="Part I : Custom Module" href="http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/">Part I : Custom Module</a></li>
<li><a title="Part II : Models" href="http://www.spinonesolutions.com/2009/10/magento-part-ii-models/">Part II : Models</a></li>
<li><a title="Part III : Controllers" href="http://www.spinonesolutions.com/2009/10/magento-part-iii-controllers/">Part III : Controllers</a></li>
<li><a title="Part IV : Extending Models" href="http://www.spinonesolutions.com/2009/10/magento-part-iv-extending-models/">Part IV : Extending Models</a></li>
<li><a title="Part V: Data Layer" href="http://www.spinonesolutions.com/2009/10/magento-part-v-data-layer/">Part V : Data Layer</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.spinonesolutions.com/2009/09/magento-custom-module-part-i/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
