Howto: Repackageable custom extension development in Magento – Part 9 – Frontend – List
Author: gaweee | Filed under: development, howto Frontend – List
Let us revisit our frontend controller. Surely by now you’ve gotten a better grasp of the controller and models. So we’ll be revisiting those concepts here. So lets say you want to allow users to view you current Twits as well as create a new Twit.
- Some new file structure loving
app/ design/ frontend/ default/ default/ template/ twit/ - twits_list.phtml etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ Admin/ - Main.php Main/ - Grid.php - Edit.php Edit/ - Form.php - New.php New/ - Form.php - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php Model/ - Twit.php Mysql4/ - Twit.php Twit/ - Collection.php sql/ twit_setup/ - mysql4-install-0.2.0.php - mysql4-upgrade-0.1.0-0.2.0.phpWe’ve added a whole nasty branch of subdirectories under
app/designs. Read on to understand what its all for… - Let us return to our
IndexController.php<?php class SavantDegrees_Twit_IndexController extends Mage_Core_Controller_Front_Action { public function indexAction() { $this->loadLayout(); $this->getLayout() ->getBlock('content')->append( $this->getLayout()->createBlock('twit/index') ); $this->renderLayout(); } }
Hold your horses, this code is EXACTLY the same as the Part 1. Its just a revision. However, that being said, we see that in Part 1, we made the system create a
Blockcalledindex. That’s an opening… - So let us modify our
index.phpblock:<?php class SavantDegrees_Twit_Block_Index extends Mage_Core_Block_Template { public function __construct() { parent::__construct(); $this->setTemplate('twit/twits_list.phtml'); } public function getTwits() { $model = Mage::getModel('twit/twit'); $collection = $model ->getCollection() ->load(); return $collection->getItems(); } }
What the difference here? We made the block load the template from
twits/twit_list.phtml. We can create/find this file in/app/design/frontend/default/default/template/twits/twit_list.phtml. Some explanation is due here:- When you say
setTemplatein the block, it means, use this file as the presentation/view - This file exists in the some subdirectory of
/app/design/frontend/default/default/template. That is the path to your default template. Even though you may have other templates, this is the default 1. So when Magento cant find your template file in the other template directories, it always reverts back to this folder. Its a directory form of inheritance/ancestory/precedence.
- When you say
- Lastly, the
twit_list.phtmlfile:<?php $_twits = $this->getTwits(); if ($_twits) { $count = 0; foreach ($_twits as $i=>$twit) { ?> <div class="twit"> <div class='name'><?= $twit->getName() ?></div> <div class='summary'><?= $twit->getSummary() ?></div> </div> <?php } } ?>Since the whole purpose of
twits_list.phpis to prepare the view for the list of twits, it obviously needs to get the data from somewhere. It turns out, theTemplateis the extension of theBlock. So by calling$this->getTwits(), we’re calling theBlock'sgetTwits()method. - There you have it! check your twits list at http://127.0.0.1/magento/index.php/twit
Hi all its us again, In the past few months we’ve been asked many a times to help improve the progressbar code. We’ve also been referenced by several websites as THE progressbar to use. In all, we felt we owed it to you guys to make it better.
So here we are, after all those emails and bug reports, we finally got down to it. Always good to take a couple of months to have a fresh prespective of things. We ripped out the old code and made it much better and more extensible than its predecessors. So much that we decided it was enough to qualify as a major revision.
Let see whats been done…
- Cleaned up the code, yes its lighter, cleaner faster. (Still lacks documentation though)
- Callbacks! Everyone’s favourite
- Max values, you can now set it to be 150/2000 instead of just a percentage
- Text formats. Show 75/100 or 75% by toggling the
textFormataccordingly - Steps, how many steps to get to your target value
- Step Duration, how long each step lasts
- Webkit (Chrome/Safari) compatibility. More like, Webkit Hacks
Once again, many a thanks for all those who have so generously provided the feature requests as well as bug fixes every now and then. And thanks for all your patience in waiting for this new release. ![]()
If its working for you, drop us a comment and tell us where we can see your stuff!
Download the new jQuery progressbar here: jQuery progressbar
or view the demo here
In our work we like to create Google Maps links for our client’s (offices, stores, etc). However, the larger our clients presence, the more random links there are bound to be. When it comes to using Google Map’s javascript API to control the map, thats still perfectly fine. But how do I create a google maps link that only shows 1 entry? Turns out, there are several ways to do this:
- Manipulate the request such that it only shows your entryHow? For example, searching for “Holland, Singapore” will lead us to about 1,356 results. See here. So lets tweak it a little:
- Append
&mrt=ypto the url – That returns us all the business listings. (852 results) - Append
&start=16to the url – That skips the first 16 results (Shows results 17-26 of 852) - Append
&num=2to the url – That returns us only 2 results (Only shows 2 results)
For a more complete listing of Google Maps Parameters, consult mapki.com. Kudos to those guys for compiling that list. Really useful stuff
So there you have it, a simple way to link your business such that its the only entry there. However this method is relatively dangerous. Why? Cause you’ve got no idea of the exact ordering that Google maps may return. Today you could be entry 17, surely not in a year (hopefully for the better). As such, this is not the recommended solution. - Append
- Use the address with your long/lat coordinates to generate your entry.
- First get your GPS coordinates, there are a number of ways to do this. Check our this link
- Then create your link via the following structure:
http://www.google.com/maps?&ie=UTF8&hl=en&q=[urlescaped address]&ll=[GPS lat,long]&z=[Zoom]&iwloc=A - Still too troublesome? We’ve created a simple tool for our clients to use. Save yourself some time. Try it!
Done! Whats the downside to this? You can only show the address for the business. Because searching by business names returns us way too many results. So instead, we have to search for an address, Google interprets this as an address and only returns 1 result (which is the whole point of geocoding). At this point its easy to the map to what we need. We’d use this typically for this nifty little “Find us on a map” links (instead of embedding the actual map on your site).
Once again, if you’d rather embed Google maps directly into your site via the JS, then your options are much more open.
Good luck! Let us know if it works for you, of if you find a better way of doing things!

Howto: Repackageable custom extension development in Magento – Part 8 – CRUD – Update
Author: gaweee | Filed under: development, howto CRUD – Update
Very similar to the Create phase, this phase basically requires the Form and Form Container blocks, and the controller to save them. Thats all!
- Repeat the file structure goodness:
app/ etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ Admin/ - Main.php Main/ - Grid.php - Edit.php Edit/ - Form.php - New.php New/ - Form.php - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php Model/ - Twit.php Mysql4/ - Twit.php Twit/ - Collection.php sql/ twit_setup/ - mysql4-install-0.2.0.php - mysql4-upgrade-0.1.0-0.2.0.phpHere we’ve added the block for the
Editcontainer andForm - Looking at the
Edit.phpcode:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<?php class SavantDegrees_Twit_Block_Admin_Edit extends Mage_Adminhtml_Block_Widget_Form_Container { public function __construct() { parent::__construct(); $this->_blockGroup = 'twit'; $this->_mode = 'edit'; $this->_controller = 'admin'; if( $this->getRequest()->getParam($this->_objectId) ) { $twitData = Mage::getModel('twit/twit') ->load($this->getRequest()->getParam($this->_objectId)); Mage::register('frozen_twit', $twitData); } } public function getHeaderText() { return Mage::helper('twit')->__("Edit Twit'%s'", $this->htmlEscape(Mage::registry('frozen_twit')->getName())); } }
The additional code loads the right
Twitdata based on theRequestparameter, then stores that data in theMageregistry. We’ll be using that data on theFormpage again later to populate the form.
Notice that the saving code isMage::registerand the retrieving code isMage::registry - Then the accompanying
Edit/Form.phpcode1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
<?php class SavantDegrees_Twit_Block_Admin_Edit_Form extends Mage_Adminhtml_Block_Widget_Form { protected function _prepareForm() { $form = new Varien_Data_Form(array( 'id' => 'edit_form', 'action' => $this->getUrl('*/*/save', array('id' => $this->getRequest()->getParam('id'))), 'method' => 'post' )); $fieldset = $form->addFieldset('edit_twit', array('legend' => Mage::helper('twit')->__('Twit Details'))); $fieldset->addField('name', 'text', array( 'name' => 'name', 'title' => Mage::helper('twit')->__('Name'), 'label' => Mage::helper('twit')->__('Name'), 'maxlength' => '50', 'required' => true, )); $fieldset->addField('tags', 'text', array( 'name' => 'tags', 'title' => Mage::helper('twit')->__('Tags'), 'label' => Mage::helper('twit')->__('Tags'), 'maxlength' => '255', 'required' => true, )); $fieldset->addField('summary', 'textarea', array( 'name' => 'summary', 'title' => Mage::helper('twit')->__('Summary'), 'label' => Mage::helper('twit')->__('Summary'), 'style' => 'width: 98%; height: 200px;', 'required' => true, )); $form->setUseContainer(true); $form->setValues(Mage::registry('frozen_twit')->getData()); $this->setForm($form); return parent::_prepareForm(); } }
-
Then lastly the
adminControllerwith itseditActionandsaveActionto load and save the form respectively1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
<?php class SavantDegrees_Twit_AdminController extends Mage_Adminhtml_Controller_Action { public function indexAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/admin_main')) ->renderLayout(); } public function deleteAction() { $twitId = $this->getRequest()->getParam('id', false); try { Mage::getModel('twit/twit')->setId($twitId)->delete(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('twit')->__('Page successfully deleted')); $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } catch (Exception $e){ Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } $this->_redirectReferer(); } public function newAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/admin_new')) ->renderLayout(); } public function postAction() { if ($data = $this->getRequest()->getPost()) { $twit = Mage::getModel('twit/twit')->setData($data); try { $twit->save(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('twit')->__('Twit was successfully saved')); $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } catch (Exception $e){ Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } } $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } public function editAction() { $this->loadLayout(); $this->_addContent($this->getLayout()->createBlock('twit/admin_edit')); $this->renderLayout(); } public function saveAction() { $twitId = $this->getRequest()->getParam('id', false); if ($data = $this->getRequest()->getPost()) { $twit = Mage::getModel('twit/twit')->load($twitId)->addData($data); try { $twit->setId($twitId)->save(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('twit')->__('Twit was saved successfully')); $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } catch (Exception $e){ Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } } $this->_redirectReferer(); } }
-
Ok i admit the code is a tad inefficient. We have 2 separate functions that both does save. In fact,
postActionandsaveActiononly differ that saveAction loads the right record via the->load($twitId)function. Thats separates aCREATEfrom anUPDATErequest.And we’re finally finally finally done! To test out the edit function, simply click on any row from the
Grid! Thats assuming of course that you actually have some data.
Otherwise, if it works for you, leave a comment! =)
Just to be sure, download the Magento tutorial files to see if you’ve made any mistakes here: Magento Tutorial
Howto: Repackageable custom extension development in Magento – Part 7 – CRUD – Create
Author: gaweee | Filed under: development, howtoCRUD – Create
- More file structure goodness:
app/ etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ Admin/ - Main.php Main/ - Grid.php - New.php New/ - Form.php - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php Model/ - Twit.php Mysql4/ - Twit.php Twit/ - Collection.php sql/ twit_setup/ - mysql4-install-0.2.0.php - mysql4-upgrade-0.1.0-0.2.0.phpHere we’ve added the block for the
Newcontainer andForm - Looking at the
New.phpcode:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<?php class SavantDegrees_Twit_Block_Admin_New extends Mage_Adminhtml_Block_Widget_Form_Container { public function __construct() { parent::__construct(); $this->_blockGroup = 'twit'; $this->_mode = 'new'; $this->_controller = 'admin'; } public function getHeaderText() { return Mage::helper('twit')->__('Add New Twit'); } }
- Then the
New/Form.phpcode1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
<?php class SavantDegrees_Twit_Block_Admin_New_Form extends Mage_Adminhtml_Block_Widget_Form { protected function _prepareForm() { $form = new Varien_Data_Form(); $fieldset = $form->addFieldset('new_twit', array('legend' => Mage::helper('twit')->__('Twit Details'))); $fieldset->addField('name', 'text', array( 'name' => 'name', 'title' => Mage::helper('twit')->__('Name'), 'label' => Mage::helper('twit')->__('Name'), 'maxlength' => '50', 'required' => true, )); $fieldset->addField('tags', 'text', array( 'name' => 'tags', 'title' => Mage::helper('twit')->__('Tags'), 'label' => Mage::helper('twit')->__('Tags'), 'maxlength' => '255', 'required' => true, )); $fieldset->addField('summary', 'textarea', array( 'name' => 'summary', 'title' => Mage::helper('twit')->__('Summary'), 'label' => Mage::helper('twit')->__('Summary'), 'style' => 'width: 98%; height: 200px;', 'required' => true, )); $form->setMethod('post'); $form->setUseContainer(true); $form->setId('edit_form'); $form->setAction($this->getUrl('*/*/post')); $this->setForm($form); } }
Just like your listings/grid page, the
New.phpandForm.phpclasses are theForm ContainerandFormrespectively. So theForm Containerautomagically creates the form. Did I mention why containers are good? They come with nice pretty buttons! (Save and Back). -
Then lastly the
adminControllerwith itsnewActionandpostActionto load and save the form respectively1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
class SavantDegrees_Twit_AdminController extends Mage_Adminhtml_Controller_Action { public function indexAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/admin_main')) ->renderLayout(); } public function deleteAction() { $twitId = $this->getRequest()->getParam('id', false); try { Mage::getModel('twit/twit')->setId($twitId)->delete(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('twit')->__('Page successfully deleted')); $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } catch (Exception $e){ Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } $this->_redirectReferer(); } public function newAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/admin_new')) ->renderLayout(); } public function postAction() { if ($data = $this->getRequest()->getPost()) { $twit = Mage::getModel('twit/twit')->setData($data); try { $twit->save(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('twit')->__('Twit was successfully saved')); $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } catch (Exception $e){ Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } } $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } }
-
Reward yourself for your lightning fast copy-and-paste kung foo! Whats happening though?
When the script receives a callnewAction, it’ll show the form to enter data. The data is posted to thepostActionmethod. That method creates an instance of the model, sets the post data into the model, saves it then redirects it back to theindexAction. Did i mention automagically? Yes, thats why we had to learn so much of the Magento code just to write this simple tutorial. The sql is automagically generated and run and the form is automagically created! *sheds a tear*Again, your well deserved create form can be found by clicking on the “Add New Twit” button from the
Grid!
Howto: Repackageable custom extension development in Magento – Part 6 – CRUD – Delete
Author: gaweee | Filed under: development, howtoCRUD – Delete
- The simplest step thus far, add a new function to your
adminController.php1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
<?php class SavantDegrees_Twit_AdminController extends Mage_Adminhtml_Controller_Action { public function indexAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/admin_main')) ->renderLayout(); } public function deleteAction() { $twitId = $this->getRequest()->getParam('id', false); try { Mage::getModel('twit/twit')->setId($twitId)->delete(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('twit')->__('Page successfully deleted')); $this->getResponse()->setRedirect($this->getUrl('*/*/')); return; } catch (Exception $e){ Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } $this->_redirectReferer(); } }
Howto: Repackageable custom extension development in Magento – Part 5 – CRUD – Retrieve
Author: gaweee | Filed under: development, howtoCRUD – Retrieve
- Lets add a new block to the file structure
app/ etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ Admin/ - Main.php Main/ - Grid.php - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php Model/ - Twit.php Mysql4/ - Twit.php Twit/ - Collection.php sql/ twit_setup/ - mysql4-install-0.2.0.php - mysql4-upgrade-0.1.0-0.2.0We’ll create folder called
Adminto store all the admin related blocks. Inside we’ll have a Main.php file that contains theGrid Container. Why do we need a grid container? To put the title andCreatebutton! -
The
Main.phpcode1 2 3 4 5 6 7 8 9 10 11 12 13
<?php class SavantDegrees_Twit_Block_Admin_Main extends Mage_Adminhtml_Block_Widget_Grid_Container { public function __construct() { $this->_addButtonLabel = Mage::helper('twit')->__('Add New Twit'); parent::__construct(); $this->_blockGroup = 'twit'; $this->_controller = 'admin_main'; $this->_headerText = Mage::helper('twit')->__('Twit(s)'); } }
Here you see a first use of the helper, which will assist in translation
-
The
Grid.phpitself1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
<?php class SavantDegrees_Twit_Block_Admin_Main_Grid extends Mage_Adminhtml_Block_Widget_Grid { public function __construct() { parent::__construct(); $this->setId('twitGrid'); $this->_controller = 'twit'; } protected function _prepareCollection() { $model = Mage::getModel('twit/twit'); $collection = $model->getCollection(); $this->setCollection($collection); return parent::_prepareCollection(); } protected function _prepareColumns() { $this->addColumn('twit_id', array( 'header' => Mage::helper('twit')->__('ID'), 'align' => 'right', 'width' => '50px', 'filter_index' => 'dt.twit_id', 'index' => 'twit_id', )); $this->addColumn('name', array( 'header' => Mage::helper('twit')->__('Name'), 'align' => 'left', 'width' => '150px', 'filter_index' => 'dt.name', 'index' => 'name', 'type' => 'text', 'truncate' => 50, 'escape' => true, )); $this->addColumn('summary', array( 'header' => Mage::helper('twit')->__('Summary'), 'align' => 'left', 'filter_index' => 'dt.summary', 'index' => 'summary', 'type' => 'text', 'escape' => false, )); $this->addColumn('tags', array( 'header' => Mage::helper('twit')->__('Tags'), 'align' => 'left', 'filter_index' => 'dt.tags', 'index' => 'tags', 'type' => 'text', 'escape' => true, )); $this->addColumn('action', array( 'header' => Mage::helper('twit')->__('Action'), 'width' => '150px', 'type' => 'action', 'getter' => 'getTwitId', 'actions' => array( array( 'caption' => Mage::helper('twit')->__('Edit'), 'url' => array( 'base'=>'*/*/edit' ), 'field' => 'id' ), array( 'caption' => Mage::helper('twit')->__('Delete'), 'url' => array( 'base'=>'*/*/delete' ), 'field' => 'id' ) ), 'filter' => false, 'sortable' => false )); return parent::_prepareColumns(); } public function getRowUrl($row) { return $this->getUrl('*/*/edit', array( 'id' => $row->getTwitId(), )); } }
If you scrutinize the code (which i’d advise against), you’ll see most of the code is geared towards the setup of the grid columns and the edit/delete urls i’ll eventually call to perform the other
CRUDfunctions.
But whats going on? Where do we use this code? The main code (being aGrid_Containerautomatically adds the childGridcode associated to it. By child we’re talking about theGrid.phpfile in theMainfolder. The folder’s name follows the file. So if you changed the folder’s name toMain2, it wouldnt work. That being said, you can just put any block code in the folder and expect it to run. The reason why this all works now is cause the geniuses at Magento already did alot of work to call the Grid child to run. They have no super time travel powers to tell what else you’re gonna put in there. So, it simply wont work (yet). - Now lets make our
adminController.phpuse that block instead of Hello world.1 2 3 4 5 6 7 8 9
class SavantDegrees_Twit_AdminController extends Mage_Adminhtml_Controller_Action { public function indexAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/admin_main')) ->renderLayout(); } }
Well it says
createBlockdoesnt it? What do you think it does?twit/admin_mainas always refers to the twit namespace and theadmin/main.phpblock file. - Viola! Your very very very hard earned grid!
Howto: Repackageable custom extension development in Magento – Part 4 – Model
Author: gaweee | Filed under: development, howtoPrepare the model
- Back to the file structure drawing board
app/ etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php Model/ - Twit.php Mysql4/ - Twit.php Twit/ - Collection.php sql/ twit_setup/ - mysql4-install-0.2.0.php - mysql4-upgrade-0.1.0-0.2.0Notice the new folder
Modeland the associated files inside. Lets clarify:
Model/Twit.phpis the logical model that holds code regarding business logic and relations.
Model/Mysql4/Twit.phpis the data implementation model that associates the model with the table and its primary key.
Model/Mysql4/Twit/Collection.phpis the data implementation model of a collection ofTwit. Similar to arecordset/resultsetin.Net/Java. This will be used with a Grid later. - Even more
config.xmlmagic. Yay! More hairloss!<?xml version="1.0"?> <config> <modules> <SavantDegrees_Twit> <version>0.2.0</version> </SavantDegrees_Twit> </modules> <global> <models> <twit> <class>SavantDegrees_Twit_Model</class> <resourceModel>twit_mysql4</resourceModel> </twit> <twit_mysql4> <class>SavantDegrees_Twit_Model_Mysql4</class> <entities> <twit> <table>twit</table> </twit> </entities> </twit_mysql4> </models> <blocks> <twit> <class>SavantDegrees_Twit_Block</class> </twit> </blocks> <helpers> <twit><class>SavantDegrees_Twit_Helper</class></twit> </helpers> <resources> <twit_setup> <setup> <module>SavantDegrees_Twit</module> </setup> <connection> <use>core_setup</use> </connection> </twit_setup> <twit_write> <connection> <use>core_write</use> </connection> </twit_write> <twit_read> <connection> <use>core_read</use> </connection> </twit_read> </resources> </global> <adminhtml> <menu> <twit translate="title" module="twit"> <title>Twits</title> <sort_order>100</sort_order> <action>twit/admin</action> </twit> </menu> </adminhtml> <frontend> <routers> <SavantDegrees_Twit> <use>standard</use> <args> <module>SavantDegrees_Twit</module> <frontName>twit</frontName> </args> </SavantDegrees_Twit> </routers> </frontend> </config>
The
global > modelsfragment define thetwittable, thetwit modeland itsresourceModelthat we will be using shortly. - Now for the
model/Twit.phpcode:<?php class SavantDegrees_Twit_Model_Twit extends Mage_Core_Model_Abstract { protected function _construct() { $this->_init('twit/twit'); } }
Whats the point you ask!? The code does nothing but binds the model to the Twit namespace under the model twit. In Java that line would have been
package Twit;. *i assume/think/foretold*
(quickly moving on…) - Then
Model/Mysql4/Twit.php<?php class SavantDegrees_Twit_Model_Mysql4_Twit extends Mage_Core_Model_Mysql4_Abstract { protected function _construct() { $this->_init('twit/twit', 'twit_id'); } }
The code above tells Magento to hunt for the twit application’s twit table in the configuration. Yes i know the names are confusing now. Lets say instead of
twit/twitit istwit/foo. In that case the system will find the configuration file associated with twit (which is our app/code/local/SavantDegrees/Twit/etc/config.xml), and look underconfig > global > models > twit_mysql4 > entities > footo figure out its configuration instructions. You’ll see next that we’ll request for the table from that same configuration file. Capiche?
As aresourceModel, the code binds the model to a underlyingMysql4 table. - Lastly,
Model/Mysql4/Twit/Collection.php<?php class SavantDegrees_Twit_Model_Mysql4_Twit_Collection extends Varien_Data_Collection_Db { protected $_twitTable; public function __construct() { $resources = Mage::getSingleton('core/resource'); parent::__construct($resources->getConnection('twit_read')); $this->_twitTable = $resources->getTableName('twit/twit'); $this->_select->from( array('twit'=>$this->_twitTable), array('*') ); $this->setItemObjectClass(Mage::getConfig()->getModelClassName('twit/twit')); } }
Basically the model files created at this phase are simply trying to tie the model (singular and collection) to the underlying
Mysql resource. The entire mechanism is half tied in theconfig.xmltable declaration, the model individual instructions (from identifying the namespace to actually declaring the primary key of the table) - Viola. Models are ready
Howto: Repackageable custom extension development in Magento – Part 3 – Database
Author: gaweee | Filed under: development, howtoPrepare the database
Next we’ll create the installation script that installs the twit table into the database.
- Once again, we begin with updating the file structure
app/ etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php sql/ twit_setup/ - mysql4-install-0.2.0.php - mysql4-upgrade-0.1.0-0.2.0.phpWe simply included a
sqlfolder that will house the eventual table creation sql code. - Next up, updating the
config.xmlagain. This time to add the setup declarations.<?xml version="1.0"?> <config> <modules> <SavantDegrees_Twit> <version>0.2.0</version> </SavantDegrees_Twit> </modules> <global> <blocks> <twit> <class>SavantDegrees_Twit_Block</class> </twit> </blocks> <helpers> <twit><class>SavantDegrees_Twit_Helper</class></twit> </helpers> <resources> <twit_setup> <setup> <module>SavantDegrees_Twit</module> </setup> <connection> <use>core_setup</use> </connection> </twit_setup> <twit_write> <connection> <use>core_write</use> </connection> </twit_write> <twit_read> <connection> <use>core_read</use> </connection> </twit_read> </resources> </global> <adminhtml> <menu> <twit translate="title" module="twit"> <title>Twits</title> <sort_order>100</sort_order> <action>twit/admin</action> </twit> </menu> </adminhtml> <frontend> <routers> <SavantDegrees_Twit> <use>standard</use> <args> <module>SavantDegrees_Twit</module> <frontName>twit</frontName> </args> </SavantDegrees_Twit> </routers> </frontend> </config>
Here we’ve added a
global > resourcessection that tells the setup script the folder for installation. We’ve also changed the version number to0.2.0. When the page is initiated and Magento detects that the version has been upgraded, it will run the appropriate install script. - Lastly the install script itself:
mysql4-install-0.2.0.php<?php $installer = $this; $installer->startSetup(); $installer->run(" CREATE TABLE {$this->getTable('twit')} ( `twit_id` int(10) unsigned NOT NULL auto_increment, `name` varchar(250) NOT NULL default '', `tags` varchar(250) default NULL, `summary` text, PRIMARY KEY (`twit_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; "); $installer->endSetup();
- Copy the same file contents to
mysql4-upgrade-0.1.0-0.2.0.php. Why? because the code above is correct. But since this HOWTO is going through the phases in version 0.1, 0.2, and etc. Magento has already a record oftwit_setupin itscore_resourcetable bound to the previous version of 0.1 (its first run). So what we need now is a upgrading script instead of an installer. But if/when you do package your extension, you shouldn’t need the upgrade code. Just install straight to version 0.2 (or higher) - Once again, wake up the Magento installer by logging into the
adminbackend,System > Configuration > Advanced. Click onSave Config. Clear your cache at/var/cachetoo just to be sure. - Check to see if your table is created.
- Once your table is created, you can delete the
mysql4-upgrade-0.1.0-0.2.0.phpfile. As explained above, you dont need it anymore!
Howto: Repackageable custom extension development in Magento – Part 2 – Admin Controller
Author: gaweee | Filed under: development, howtoCreate the administrative complement
Next we’ll create the administration controller and configuration to achieve the https://127.0.0.1/magento/index.php/twit/admin reservation. This took me a long time to figure out how to do nicely.
- We’ll begin by updating the adding 2 new files (controller and helper) for the purpose of the Hello world in the admin site
app/ etc/ modules/ - SavantDegrees_All.xml (Or what ever your company name might be) code/ local/ SavantDegrees/ (Or what ever your company name might be) Twit/ (Or whatever your module name might be) Block/ - Index.php controllers/ - AdminController.php - IndexController.php etc/ - config.xml Helper/ - Data.php - Then we obviously create the
adminController.php
We’ll make the admin page reuse the sameHello world blockthat we’ve created in Phase 1.<?php class SavantDegrees_Twit_AdminController extends Mage_Adminhtml_Controller_Action { public function indexAction() { $this->loadLayout() ->_addContent($this->getLayout()->createBlock('twit/index')) ->renderLayout(); } }
Note that this controller extends
Mage_Adminhtml_Controller_Actioninstead. This enforces security and loads the admin layout for this controller instead of the frontend version. With this done, you can now visit https://127.0.0.1/magento/index.php/twit/admin to see the page in action. Again, this produces our ubiquitous Hello world. You’d probably figured out by now that/twitrefers to our frontend controller reservation by ourconfig.xmland then/adminrefers to theadminControllerthat we’ve just created. Now you can try https://127.0.0.1/magento/index.php/twit/admin/index to see theindexActionmethod being called. Try it with other action names! - Next we’ll do the
config.xmlmagic that allows us to use https://127.0.0.1/magento/index.php/admin/twit for the administration.<?xml version="1.0"?> <config> <modules> <SavantDegrees_Twit> <version>0.1.0</version> </SavantDegrees_Twit> </modules> <global> <helpers> <twit><class>SavantDegrees_Twit_Helper</class></twit> </helpers> <blocks> <twit> <class>SavantDegrees_Twit_Block</class> </twit> </blocks> </global> <adminhtml> <menu> <twit translate="title" module="twit"> <title>Twits</title> <sort_order>100</sort_order> <action>twit/admin</action> </twit> </menu> </adminhtml> <frontend> <routers> <SavantDegrees_Twit> <use>standard</use> <args> <module>SavantDegrees_Twit</module> <frontName>twit</frontName> </args> </SavantDegrees_Twit> </routers> </frontend> </config>
What’ changed?
- The
global > helper > [module]fragment defines the helper class that will help our future translation. We’ll be seeing the code for that shortly. - The
adminhtml > menu > [module]fragment allows us to create the menu to access the twit/admin page. This is vital for version 1.3 onwards as the menu generates the necessary url key to access the page.
- The
- And finally the helper
Data.phpfile<?php class SavantDegrees_Twit_Helper_Data extends Mage_Core_Helper_Abstract { }
Why bother? Its a quirk of Magento to try to translate the menu. So we NEED this helper. Otherwise Magento goes looking for a Mage_Twit_Helper_Data class. Which obviously doesnt exist! You could use the helper from existing Magento classes, but then you could use ALOT of stuff from Magento classes. And if you’re reading this tutorial, you’re obviously arent ready.
Most Popular
- HOWTO: PHP and jQuery upload progress bar (47)
- JQuery Progress Bar 1.1 (41)
- Howto: Repackageable custom extension development in Magento - Part 2 - Admin Controller (24)
- Howto: Repackageable custom extension development in Magento - Part 8 - CRUD - Update (13)
- HOWTO: struts 2 i18n (12)
- JQuery Progress Bar 2.0 (11)
- JQuery Progress Bar 1.2 (10)
- Howto: Repackageable custom extension development in Magento - Part 3 - Database (9)
- Howto: Repackageable custom extension development in Magento (8)
- Howto: Repackageable custom extension development in Magento - Part 5 - CRUD - Retrieve (6)
Recent Comments
- donald: Hi Wen, I have checked out
- donald: Hi Ashley, Currently, I simply use
- donald: Hi Noemi, there are many
- Serge: Thanks a lot. It's the simplest
- Zoran: Excellent posts you got here...
- Craig: Brilliant tutorial! There's NOTHING on
- Will: Thnak you! I was
- Margots: Thank you a lot for
- Aswin S: Wow great dude.. For the
- Noemi Heier: This is great! How did
Latest Entries
- jQuery Progress Bar Configuration
- Extracting email addresses from inbox
- 10 Good (Free and Legal) Source for Photos and Images
- Howto: Backup Microsoft SQL Server Database, as in Dump it to a SQL Script (like MYSQL's sqldump)
- Managing client's expectation with wireframe software
- Howto: Repackageable custom extension development in Magento - Part 9 - Frontend - List
- JQuery Progress Bar 2.0
- HOWTO: Find icons for your new prototype system
- Google Maps Helper
- laying the cornerstones
