Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save marinsagovac/e4c5f215b1d1323e01a1a7abefafe5bf to your computer and use it in GitHub Desktop.
Save marinsagovac/e4c5f215b1d1323e01a1a7abefafe5bf to your computer and use it in GitHub Desktop.
Magento - 2014 - tutorials
=======================
ONLINE RESOURCES:
=======================
SECTION 1 - OVERVIEW - COURSE INTRODUCTION
========================================
- understand the Magento architecture and modules
- efficiently and effectively customize and extend Magento
- enable the best upgrade path to new versions
COURSE FOCUS
- Core --> Admin --> Enterprise --> Other --> Sales --> Catalog
MAGENTO QUICK FACTS
220+ employees
$25B in transactions
2.5 Million downloads to date
350.000 Community members
Over 3,000 extensions developed on Magento
Open source eCommerce software platform
Varien was founded in 2001 and Magento project started in December 2006
Global presence with headquarters in Los Angeles, CA
65,000+ live merchants on Magento all over the world
SECTION 2 - MAGENTO BASICS - OVERVIEW
====================================
GoF Design Pattern
GRASP design pattern (information export, creator, low coupling, high cohesion, polymorphism)
MVC (business logic)
Pros: modular, flexible, standard structure, comparatmentalized, better maintenance, easy migration, reusable model code
Cos: performance degradation, requires lots of files, solves problems that may not occur, 3 layers not always enough
Views: Layout XML -> Block -> Template
Magento - MVC application with a configuration-based representaton model
Controller - receives input and initiates a response
Model - manages behavior and data of the application domain, reponds to requests for information about its state, responds to instructions to instructions to change state
View - renders the model into a form suitable for interaction
SECTION 2 - MAGENTO BASICS - EVENT-DRIVEN ARCHITECTURE
======================================================
Learning objectives
Event-Driven Architecture - arch which events are used to exchange messages in a real time between applications or between parts of single application (process)
EDA - prog paradigm in which the flow of the program is determined by events - ie sensor outputs, user actions, messages from oth prog threads
Event-Observer
Pipe-and-Filter
Intercepting Filters
EDA Advantages<->Disadvantages:
easy dev proc <> flow is less logical and obvious
easy to enhance app parts <> hard to control app interactions
application is flexible <> time consuming to event handling running
Why It's used in Magento:
- simplifying system expansibility
Script exec: event --> observer1, observer2, ..., observerN
Exercise: EDA - simulate event-driven programming paradigm
SECTION 2 - MAGENTO BASICS - MODULE-BASED ARCHITECTURE
======================================================
MODULE-BASED ARCHITECTURE (& modularity)
- describing module arch
- listing steps to add new modules
- designing a modular website
Modular programming - a software design technique that increases the extent to which softw is composed of separate parts, called modules
Modularity is a general concept which applies to the development of software in a fashion which allows individual modules to be developed, often with a standardised interface to allow modules to communicate. In fact, the kind of separation of concerns between objects in an OO language is much the same concept as for modules, except on a larger scale.
Guideline - modules should: (modular architecture)
- be opaque to the rest of the system andinitialized through a well-known interface
- not directly reference one another of the app that loaded them
- use services to comm. with the app or with other modules
- not be responsible for managing the dependencies
- not rel on static methods that can inhibit testability
- support being added and removed from the system in a pluggable fashion
Known issues and limitations:
- Mage_Core dependecy
- Modules initialization
- Code intersection
- Adminhtml Module
- Design integration
Tested. Thoroughly. Enterprise-ready - Zend Framework 1.11 (Ajax support through JSON, Search, Syndication, Web Services):
Thoroughly-tested, enterprise-ready and built with agile methods, Zend Framework has been unit-tested from the start, with stringent code coverage requirements to ensure that all code contributed has not only been thoroughly unit-tested, but also remains stable and easy for you to extend, re-test with your extensions and further maintain.
http://files.zend.com/help/previous-version/Zend-Server-5-Community-Edition/zend_framework.htm
Components usage:
Customizable ZEND Framework:
Zend_Cache, _Db, _Filter, _Http_Client, _Acl, _Controller, _View, _Currency, _Data, _Mail, _Mime, _Service, _Log, _XmlRpc, _Validate,..
ZEND Framework - MVC-Core-Web api...
Review - Guidelines for Modules, Reasons for using Modularity, Module Limitations, Zend Framework
SECTION 2 - MAGENTO BASICS - MAGENTO DIRECTORY STRUCTURE
========================================================
- locating various files structure
- conf fils, stylesheets, images, js, code
Directory structure:
- the high-level dir hierarchy is baed on keyword-based logic
- app,js,lib,media,skin,var,.htaccess,index.php,cron.php
Home >> Keyword >> ..
>> Keyword >> ..
app [root path]
app - directory where the application resides
- code - contains the Magento code separated into code pools
- design - contains the Magento design templates and custom design templates
- locale - contains localizations for different languages
js - contains all Javascript libraries used in Magento
- calendar - calendar JS widget library
- enterprise - JS libraries contributed withing enterprise modules
- ExtJS - ExtJS library code base
- jquery - custom javascript
- lib - a set of 3rd parties JavaScript libraries
- mage - JavaScrip developed by Magento* team
- prototype - ProtoType JavaScript library
- scriptaculous - Scriptaculous JavaScript library
- tiny_mce - TinyMCE JavaScript library
- varien - "old" Magento* libraries namespace
lib - contains Varien and Mage libraries which are developed by Magento team and all third-party PHP libraries used in Magento, including PEAR and Zend
- 3Dsecure - centinel 3D secure validation library
- Apache - Apache Solr library
- flex - file uploader library (Flex) by Magento*
- googlecheckout - Google Checkout support files
- LinLibertineFont - Libertine Open Fonts Project library
- Mage - Magento* libraries namespace
- PEAR - PEAR library
- phpseclib - The PHP Secure Communications Library
- Varien - "another" Magento* libraries namespace
- Zend - Zend Framework
media - storage for all images and media files that used in a Magento store (product images, pdf documents, etc..)
- catalog - Mage_Catalog media files stored here
- customer - Mage_Customer media files stored here
- downloadable - Mage_Downloadable media files stored here
- import - Media files to be imported stored here
skin - contains all available Magento design skins along with images and CSS files used by the corresponding themes
- adminhtml - Admin area skin here
- frontend - front area skin files
- install - installation wizard skin files
var - contains various dynamically-created and temporary files, like cache, locks, session storage, etc...
- cache - both system and user cache
- session - PHP session files
- tmp - place for temporary files
Module Directory Structure:
Mage_Catalog
- block
- controllers
- etc
- helper
- model
- sql
Mage_Catalog
- Block - is an additional layer between controllers and view representation of the application
- Category
- Layer
- Product
- Seo
- Widget
- Breadcrumb.php
- Navigation.php
- Product.php
example:
Mage_Catalog_Block_Product_View >> createBlock('catalog/product_view');
Catalog/Block/Product/View
Mage_Catalog_Block -- Product_View
important -> use "slash", "/" into createBlock() function for calling models.
- controllers - provided by module, and action (or case) controller processes web server requests
- Product
- Seo
CategoryController.php
IndexController.php
ProductController.php
- etc - contains module configuration and system files
- adminhtml.xml
- api.xml
- config.xml
- connect.xml
- system.xml
- widget.xml
- wsdl.xml
- Helper - is an auxiliary class that encapsulates some commonly used module functionality and makes it publicly available to other modules
- Category
- Product
- Category.php
- Data.php
- Image.php
- Map.php
- Output.php
- Product.php
- Model - object models provided by module. Model is a class that implements business logic of a certain Magento entity
- Api
- Attribute
- Category
- Convert
- Entity
- Indexer
- Layer
- Product
- sql - contains module Data Base setup and upgrade files
- catalog_setup
- mysql4-install/upgrade-version.. .php
Code Pools
(Programming Code)
- local
- community
- core
Talking:
First loads Dispatcher app/Mage.php - steps for including functions and directory structures,
than registering autoloads of class and libraries.
Loading Varien/Autoload and registering classes using spl_autoload_register to prepare and load class after calling class.
It will be easy for calling class example Catalog/Block/Product/View.php
Code Pool - unique directory that consists of grouped code. As ussual it identifies a high levl of code organization.
The Magento team, who write the Magento itself, always use: app/code/core
Community developers, who implements extenson and work outside the company, must use: app/code/community
End-user developers, who write local customization code for the particular Magento installation, must use: app/code/local
app/code/core
Holds modules that are distributd with the base Magento and make up the core functionality.
app/code/community
Holds modules that are developed by third party.
app/code/local
Holds custom modules you developed, include Mage overrides.
PHP INCLUDE PATH ORDER (priority!)
Local > Community > Core
// == Mage.php ==
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'local';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'community';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'core';
$paths[] = BP . DS . 'lib';
$appPath = implode(PS, $paths);
set_include_path($appPath . PS . Mage::registry('original_include_path'));
include_once "Mage/Core/functions.php";
include_once "Varien/Autoload.php";
Local checks IP address with local IP address.
Namespace - (as package ofted referred), first folder
- unique name that usually identifies a company or an organization
Each contributing member of the Magento community should use their own Namespace name when creating modules in order to avoid colliding with another user's code.
A namespace is an abstract container which contains a logical grouping of unique identifiers (i.e., names). An identifier defined in a namespace is associated with that namespace. It is possible to define the same identifier independently in multiple namespaces. That is, the meaning associated with an identifier defined in one namespace may or may not have the same meaning as the same identifier defined in another namespace. Languages that support namespaces specify the rules that determine to which namespace an identifier (i.e., not its definition) belongs.
http://en.wikipedia.org/wiki/Namespace_%28computer_science%29
Namespace: app/code/local/Training/Foo
app/code/local/Training/Catalog
Locating templates
app
design
+[AREA_NAME]
+[PACKAGE_NAME]
+[THEME_NAME]
+template
+[MODULE_NAME]
+...
Locating Layout XML
app
+design
+[AREA_NAME]+[PACKAGE_NAME]
+[THEME_NAME]
+layout
+[NAMESPACE_NAME]
+...
Locating Skins
skin
+[AREA_NAME]
+[PACKAGE_NAME]
+[THEME_NAME]
+js/css/images
+[NAMESPACE_NAME]
+...
Locating JavaScript
js
+[NAMESPACE_NAME]
+[MODULE_NAME]
+...
Locating Temporary Directories
var
+import
+[MODULE_NAME]
+export
+[MODULE_NAME]
+tmp
+[MODULE_NAME]
Exercise:
Locate Varien_Data_Collection_Db
lib/Varien/Data/Collection/Db
...
SECTION 2 - MAGENTO BASICS - CONFIGURATION XML
==============================================
Create and register a module
Specify option in the config file
Operate website/stores/stores views
Add different values for different stories
Configuration
Magento configuration is a bunch of XML files that are merged together during app initialization in one tree.
app/etc
+etc
+modules
+Enterprise_AdminGws.xml
+Enterprise_Banner.xml
...
Mage_All.xml
Mage_XmlConnect.xml
config.xml
enterprise.xml
local.xml
Inside module you have namespace and modules.
Example:
app/code/core/Enterprise/Banner/etc/config.xml
Files are loaded strictly in alphabetic order, except for module dependencies.
Global nodes:
<config>
<modules>...</modules> <!-- declarations modules (names, statuses,..) -->
<global>...</global>
<default>...</default> <!-- stores configurations -->
<frontend>...</frontend>
<catalog>...</catalog> <!-- Mage_Catalog module config -->
</config>
The global node defines:
- Main database settings, such as host, database name, user name, passwordand some system values.
- connection types [read/write]
- database adapter
- core module class names
The Frontend node defines:
- Routers
- Translation files
- Layout XML files
- Observers declarations
The catalog node defines:
- specific a certain module
How to access to configuration value:
To get part of config:
$store->getConfig($path);
To get part of config:
Mage::getStoreConfig($path [, $store]);
To check store flag:
Mage::getStoreConfigFlag($path [, $store]);
To access by absolute path:
Mage::getConfig()->getNode($path [, $scope]);
Store's Area Responsibilities
What could be configured for each scope individually
Website -> Store Group -> Store
Website:
- design defaults
- locale defaults
Store group:
- root category
Store:
- design
- locale
etc/modules/*.xml - modules configurations
Create module:
etc/modules/First_Module.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<First_Module>
<active>true</active>
<codePool>local</codePool>
</First_Module>
</modules>
</config>
Now to System > Advanced > First_Module > Enabled
Create directory:
app/code/local/First/Module/etc
Create XML:
app/code/local/First/Module/etc/config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<default>
<some>
<random>
<xpath>Here is some value.</xpath>
</random>
</some>
</default>
</config>
Now how to accessing to module:
(indexAction() in a controller)
Main module is Mage/Core/Cms/controllers/IndexController.php [Mage_Cms_IndexController] on indexAction().
echo Mage::getStoreConfig('some/random/xpath');
Result: /
Here is some value.
For specific languages change on config.xml after </default>:
<stores>
<french>
<some>
<random>
<xpath>Here is some value french.</xpath>
</random>
</some>
</french>
</stores>
Switch to French language on frontend
Result: /
Here is some value french.
Result: /
echo Mage::getConfig()->getNode('default/some/random/xpath');
Here is some value french.
SECTION 2 - MAGENTO BASICS - FUNCTIONAL AND FACTORY CLASS GROUPS
================================================================
- instantiate main Magento objects
- get different instances from different places
GoF Abstract Factory
Realization in Magento
Mage::getModel($modelClass = '', $arguments = array())
class Mage
getModel($modelClass = '', $arguments = array())
class Mage_Core_Model_Config
getModelInstance($modelClass = '', $constructArguments = array())
-->
getModelClassName($modelClass);
-->
getGroupedClassName($groupType, $classId, $groupRootNode=null)
Realization for retrieving model:
// instance Mage_Catalog_Model_Product class
Mage::getModel('catalog/product');
Models declaration in the config.xml:
<config>
<global>
<models>
<catalog>
<class>Mage_Catalog_Model</class>
<resourceModel>catalog_resource_eav_mysql4</resourceModel>
</catalog>
</models>
</global>
</config>
getResourceModel( <name_of_modul> )
How to call helpers in config:
helper('sales') = sales/dates
<config>
<global>
<helpers>
<sales>
<class>Mage_Sales_Helper</class>
Instance model on Mage_Cms_IndexController
Mage/Core/Cms/controllers/IndexController.php
Put on indexAction():
Zend_Debug::dump(Mage::getModel('catalog/product'));
Result: /
dumped models data...
Models example of Catalog:
etc/config.xml
<global>
<models>
<catalog>
<class>Mage_Catalog_Model</class>
<resourceModel>catalog_resource</resourceModel> <!-- resourceModel calls next sibling in XML -->
</catalog>
<catalog_resource> <!-- called from resourceModel -->
How to instance catalog_resource ?
echo get_class(Mage::getResourceModel('catalog/product'));
Result: /
Mage_Catalog_Model_Resource_Product
<catalog_resource>
<product>
<table>catalog_product_entity</table>
</product>
Model is stored under:
app/code/core/Mage/Catalog/Model/Resource/Product/
How to get class helper:
-- It's called "getHelperClassName($helperName)"
Controller:
Mage/Core/Cms/controllers/IndexController.php > indexAction()
echo get_class(Mage::helper('sales/data'));
Result: /
Mage_Sales_Helper_Data
Get layout:
Controller:
Mage/Core/Cms/controllers/IndexController.php > indexAction()
echo get_class(Mage::app()->getLayout()->createBlock('core/template'));
Result: /
Mage_Core_Block_Template
SECTION 2 - MAGENTO BASICS - CLASS OVERRIDES IN MAGENTO
=======================================================
- replace Model, helper and Block classes
- override a class
Class Overrides
- Model
- Resource Model
- Block
- Helpers
Example: Model
Configuration file:
<global>
<models>
<sales>
<rewrite>
<qoute_address>Training_Sales_Model_Qoute_Address</qoute_address>
</rewrite>
</sales>
</models>
</global>
Magento uses configurations so you can access to object. You can passing a class from sales model.
Instance a model:
Mage::getModel('sales/quote_address');
Explanation of instance model:
sales --> Mage_Sales_Model
Mage_Sales_Model <-- class prefix
global/models/sales/class
To 'quote_address':
Mage_Sales_Model_Quote_Address
Structure of explanation for calling model:
global/models/sales/<class>
global/models/sales/rewrite
global/models/sales/rewrite/qoute_address
Explanation using getResourceModel():
Mage::getResourceModel('cms/page');
Configuration model of cms/page:
globals/models/cms/resourceModel = 'cms_resource'
or config.xml:
<config>
<global>
<models>
<cms>
<class>Mage_Cms_Model</class>
<resourceModel>cms_resource</resourceModel>
</cms>
<cms_resource>
<class>Mage_Cms_Model_Resource</class>
<rewrite><page>...</page></rewrite>
Example of config: Block
<global>
<blocks>
<checkout>
<rewrite>
<onepage_success>Training_Checkout_Block_Success</onepage_success>
</rewrite>
</checkout>
</blocks>
</global>
Example of config: Helper
<global>
<helpers>
<checkout>
<rewrite>
<onepage_success>Training_Checkout_Block_Success</onepage_success>
</rewrite>
</checkout>
</helpers>
</global>
Learning from bootstrap Magento:
Create app/test.php and point .htaccess for Index page to 'test.php'
app/test.php
<?php
include 'app/Mage.php';
Mage::app();
$object = Mage::getModel('catalog/product');
echo get_class($object);
Result:
Mage_Catalog_Model_Product
Calling 'name' from catalog/product:
/app/code/core/Mage/Catalog/Model/Product.php
You found getName() in a model file. Call from main page:
app/test.php
$object = Mage::getModel('catalog/product')->load(165);
var_dump($object->getName());
Result:
string 'My Computer' (length=11)
Calling model using config.xml:
Change config.xml in a 'First_Module':
app/code/local/First/Module/etc/config.xml
<config>
<global>
<models>
<catalog>
<rewrite>
<product>First_Module_Model_Product</product>
</rewrite>
</catalog>
</models>
</global>
</config>
Create folder 'Model' under 'app/code/local/First/Module/'.
Create 'Product.php' under 'app/code/local/First/Module/Model'.
include 'app/Mage.php';
Mage::app();
$object = Mage::getModel('catalog/product')->load(165);
echo get_class($object);
------------------------------
local/First/Module/etc/config.xml
<config>
<global>
<models>
<catalog>
<rewrite>
<product>First_Module_Model_Product</product>
</rewrite>
</catalog>
</models>
</global>
</config>
------------------------------
local/First/Module/Model/Product.php
<?php
class First_Module_Model_Product extends Mage_Catalog_Model_Product {
}
------------------------------
Result:
First_Module_Model_Product
Put methods and function into class:
class First_Module_Model_Product extends Mage_Catalog_Model_Product {
public function getName() {
$name = parent::getName(); // from Catalog/Product
return strtoupper($name);
}
}
Creating view block for catalog:
/app/test.php
<?php
include 'app/Mage.php';
Mage::app();
$object = Mage::app()
->getLayout()
->createBlock('catalog/product_view'); // block of product view, create blocks in XML config
echo get_class($object);
Result:
Mage_Catalog_Block_Product_View
------------------------------
app/code/local/First/Module/etc/config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global> <!-- move to global -->
<blocks>
<catalog>
<rewrite>
<product_view>First_Module_Block_Product_View</product_view>
</rewrite>
</catalog>
</blocks>
<models>....</models>
</global>
</config>
Create view using blocks:
Create view path: 'Block/Product' with file 'View.php'.
View file: app/code/local/First/Module/Block/Product/View.php
Resource model:
Instance of resource model:
/app/test.php
$object = Mage::getResourceModel('catalog/product');
echo get_class($object);
It will call under <resourceModel> xml file.
Change config for resource model use:
app/code/local/First/Module/etc/config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
<blocks>
<catalog>
<rewrite>
<product_view>First_Module_Block_Product_View</product_view>
</rewrite>
</catalog>
</blocks>
<models>
<catalog>
<rewrite>
<product>First_Module_Model_Product</product>
</rewrite>
<!-- resource model -->
<catalog_resource> <!-- Magento <v1.8 use: <catalog_catalog_resource_eav_mysql4> -->
<rewrite>
<product>First_Module_Model_Product_Resource_Model</product>
</rewrite>
</catalog_resource>
<!-- resource model end -->
</catalog>
</models>
</global>
</config>
Create under 'Model' folder 'Product/Resource' resource folder with file 'Model.php'.
Create Resource file and folders under 'app/code/local/First/Module/Model/Product/Resource/Model.php'
On resource model 'app/code/local/First/Module/Model/Product/Resource/Model.php' put:
class First_Module_Model_Product_Resource_Model extends Mage_Catalog_Model_Resource
{
//.. resources data
}
On older Magento <1.8 version use change class 'Mage_Catalog_Model_Resource' to 'Mage_Catalog_Model_Resource_Eav_Mysql4_Product'.
Result:
Mage_Catalog_Model_Resource_Product
Working with Helpers:
Helpers:
app/test.php
---------------
include 'app/Mage.php';
Mage::app();
$object = Mage::helper('sales/data');
echo get_class($object);
-------------------
Create folder 'Helper' under 'app/code/local/First/Module/' with filename 'Data.php'.
Example:
app/code/local/First/Module/Helper/Data.php
<?php
class First_Module_Helper_Data extends Mage_Sales_Helper_Data
{
}
config.xml
---------------
<config>
<global>
<helpers>
<customer>
<rewrite>
<address>First_Module_Helper_Address</address>
</rewrite>
</customer>
<sales>
<rewrite>
<data>First_Module_Helper_Data</data>
</rewrite>
</sales>
</helpers>
</config>
</global>
Result:
--------------
First_Module_Helper_Data
Create 'Address.php' under 'helper' folder. 'app/code/local/First/Module/Helper/Address.php'
Address.php
Another helper way:
app/test.php
---------------
include 'app/Mage.php';
Mage::app();
$object = Mage::helper('customer/address');
echo get_class($object);
Helper/Address.php
---------------
<?php
class First_Module_Helper_Address extends Mage_Customer_Helper_Address
{
}
config.xml
---------------
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
<blocks>
<catalog>
<rewrite>
<product_view>First_Module_Block_Product_View</product_view>
</rewrite>
</catalog>
</blocks>
<helpers>
<!-- CUSTOMER HELPER -->
<customer>
<rewrite>
<data>First_Module_Helper_Address</data>
</rewrite>
</customer>
<!-- CUSTOMER HELPER -->
<sales>
<rewrite>
<data>First_Module_Helper_Data</data>
</rewrite>
</sales>
</helpers>
<models>
<catalog>
<rewrite>
<product>First_Module_Model_Product</product>
</rewrite>
<catalog_resource>
<rewrite>
<product>First_Module_Model_Product_Resource_Model</product>
</rewrite>
</catalog_resource>
</catalog>
</models>
</global>
</config>
Result:
Mage_Customer_Helper_Address
SECTION 2 - MAGENTO BASICS - EVENT OBSERVER
===========================================
- registering an Observer
- configuring Observer to work in only front or backend
- configuring Observer to work in both front end backend
- setting up a cron job
Firing an event
Mage::dispatchEvent('catalog_product_collection_load_after', array('collection' => $this))
dispatchEvent() iterates through observers:
Script execution: It will get type of config, model of getClassName, method from '#eventConfig'.
Observer's Invocation
dispatchEvent() do next steps during iteration:
Create event's andobserver's objects
$event = new Varien_Event($args);
$event->setName($eventName);
$observer = new Varien_Event_Observer();
Assign event to observer
$observer->setData(array('event'=>$event));
Call observer's method
$method = $obs['method'];
$observer->addData($args);
$object = Mage::getModel($obs['model']);
$object->$method($observer);
Auto-Fired Events
Some eventsare are dispatched by default:
if ($this->_eventPrefix && $this->_eventObject)
Mage::dispatchEvent($this->_eventPrefix . '_load_before', array(
));
)
Mage::dispatchEvent($this->_eventPrefix . '_delete_before',
$this->_getEventData());
Auto-Fired Events
*_load_before
*_load_after
*_save_before
*_save_after
*_save_commit_after
*_delete_before
*_delete_after
*_delete_commit_after
Event Object
Encapsulates all data needed for observer method
Varien_Event --> Varien_Object
Varien_Event <-- Varien_Event_Observer
Varien_Event
#observers
+addObserver(Observer)
+dispatch()
+getName()
Varien_Object
Varien_Event_Observer
Observers
One class per module
Class is a collection observer methods
Sales
Model
Mysql4
Billing
Entity
Payment
Observer.php <--- OBSERVER
Order.php
Qoute.php
Example: public function SOMEMETHOD(Varien_Event_Observer $observer)
Observer Interface
Must be public
Get Varien_Event_Observer as parameter
Modifying Parameters
Mage::dispatchEvent('catalog_product_collection_load_before',
array('colleton'=>$this));
public function productCollectionLoadBefore(Varien_Event_Observer $observer)
{
...
$collection = $observer->getData('collection');
$collection->addToFilter('entity_id', array('min' => $productId));
}
Doing Utility Actions
/app/code/core/Mage/Catalog/Model/Observer.php
/**
* Process catalog data after category move
*
* @param Varien_Event_Observer $observer
* @return Mage_Catalog_Model_Observer
*/
public function categoryMove(Varien_Event_Observer $observer)
{
$categoryId = $observer->getEvent()->getCategoryId();
$prevParentId = $observer->getEvent()->getPrevParentId();
$parentId = $observer->getEvent()->getParentId();
/** @var $categoryFlatHelper Mage_Catalog_Helper_Category_Flat */
$categoryFlatHelper = Mage::helper('catalog/category_flat');
if ($categoryFlatHelper->isAvailable() && $categoryFlatHelper->isBuilt()) {
Mage::getResourceModel('catalog/category_flat')->move($categoryId, $prevParentId, $parentId);
}
return $this;
}
Configuration
<frontend>
...
<events>
<customer_login>
<observers>
<training>
<type>model</type>
<class>training/observer</class>
<method>bindCustomerLogin</method>
</training>
</observers>
</customer_login>
...
</frontend>
Calling SINGLETON:
Mage::getSingleton();
Examples of Singleton() usage in Magento:
Mage::getSingleton('admin/session');
Mage::getSingleton('adminhtml/url')->getUrl('adminhtml');
Mage::getSingleton('core/session')->setLastUrl(Mage::getUrl('*/*/*', array('_current'=>true)));
Mage::getSingleton('sales/order_config')->getStatuses();
...
Instructors hints:
Calling Singleton from Config example:
Mage::getSingleton('training/observer');
Cron job
<crontab>
<jobs>
<catalog_product_index_price_reindex_all>
<schedule><cron_expr>0 2 * * *</cron_expr></schedule>
<run><model>catalog/product_indexer_price::reindexAll</model></run>
</catalog_product_index_price_reindex_all>
</jobs>
</crontab>
Exercise:
Create module config under 'app/etc/modules/Foo_Bar.xml'.
<?xml version="1.0" encoding="UTF-8"?>
<!-- app/etc/modules/Foo_Bar.xml -->
<config>
<modules>
<Foo_Bar>
<active>true</active>
<codePool>local</codePool>
</Foo_Bar>
</modules>
</config>
Create module in folder 'local':
/app/local/Foo/Bar/etc
Crete 'config.xml':
/app/local/Foo/Bar/etc/config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- app/code/local/Foo/Bar/etc/config.xml -->
<config>
<global>
<models>
<foo_bar>
<class>Foo_Bar_Model</class>
</foo_bar>
</models>
</global>
<frontend>
<events>
<catalog_product_load_after><!-- event -->
<observers>
<foo_bar><!-- unique for event -->
<type>model</type>
<!-- signleton | disabled -->
<class>foo_bar/observer</class>
<method>CatalogProductLoadAfter</method>
</foo_bar>
</observers>
</catalog_product_load_after>
</events>
</frontend>
</config>
Create folder 'Model' under 'app/local/Foo/Bar/Model'.
Create 'Observer.php' file under Model. 'app/local/Foo/Bar/Model/Observer.php'.
<?php
// app/code/local/Foo/Bar/Model/Observer.php
class Foo_Bar_Model_Observer
{
public function catalogProductLoadAfter(Varien_Event_Observer $observer)
{
$product->setName($product->getName() . ' is cool');
}
}
Now on Magento page "/electronics/computers/computer.html" you see changed title to:
"Acer Ferrari... is cool"
You can change VIEW from 'frontend' to 'adminhtml' to change events on admin page.
For working events on ALL PAGES use <global>.
SECTION 3 - REQUEST FLOW - OVERVIEW
===================================
Captions covered:
- application initializations
- front controller
- URL rewrites
- request routing
- modules initializations
- design and layout initialization
- structure block templates
- flushing output
APPLICATION INITIALIZATION
==========================
- knowing the steps for application initialization
- changing website from within index.php
Application Initialization Diagram
user:index.php -> :Mage -> :App -> :config -> :Resource_Setup -> :FrontController
Example:
user:index.php -> :Mage ----> run('', 'store')
:Mage-> :App ----> run('', 'store', array())
:App -> :Config ----> initiBaseConfig(), loadBase()
:config -> :Resource_Setup -> :FrontController
config (init config)
request (Request/Stores Initialization)
routing (Routing and Action Initialization)
Server - App diagram:
App <- Website visitor -> Internet -> Hosting, Registrar, DNS Server
Mage::run() --> App
Mage::run() <-- index.php <-- Web Server
Initialization:
php level <-- php.ini, .htaccess, index.php
Magento Level <-- Mage::run()
Initialization Demo:
1. Open index.php
2. Setting up autoloader
3. Using 'developer mode'
4. Modify/correct store parameters
5. Start Mage::run()
On .htaccess put:
SetEnv MAGE_RUN_CODE german
On administration you can see under 'System/Manage Stores/' > 'View store names' code on 'german' input field.
It will setup default language.
SECTION 3 - REQUEST FLOW - FRONT CONTROLLER
===========================================
- locating front controller class
- listing all events hat front controller fires
- explaining front controller responsibilities
Step 1. covered index.php setup
Step 2. Mage:app() initializes a Magento environment and then delegate responsibilities to the Front Controller. The main responsibility is matching exact routers.
Front Controller has the following responsibilities:
1. gathering all routes (init method)
2. apply database url rewrite
3. apply configuration url rewrite
4. matching exact router
5. sending output generated by controller
FRONT CONTROLLER DIAGRAM
Actor ->
-> index.php
-> Mage.php (Mage::run())
-> Mage_Core_Model_App (Mage_Core_Model_App::run())
-> Mage_Core_Controller_Varien_Front
-> Router (router::match())
FRONT CONTROLLER INITIALIZATION
- Mage_Core_Model_App Initializes environment
- Delegates the rest of responsibilities
public function run($params)
{
if ($this->_cache->processRequest()) {
...
} else {
..
$this->getFrontController()->dispatch();
}
return $this;
}
FRONT CONTROLLER RESPONSIBILITY #1
- Mage_Core_Controller_Varien_Front
It collecting routes from class and status codes of routes. (redirects and loaders)
public function init()...
FRONT CONTROLLER RESPONSIBILITY #2
- apply database url rewrites
dispatch() -->
getRequest() ... Mage::getModel('core/url_rewrite')->rewrite();
FRONT CONTROLLER RESPONSIBILITY #3
- apply configuration url rewrite
- based on configuration information we apply URL rewrite as from->to
public function dispatch()
{ ... $this->rewrite(); ... }
FRONT CONTROLLER RESPONSIBILITY #4
- matching exact router
- while cycling we check with each router - process request
$router->match($this->getRequest())
FRONT CONTROLLER RESPONSIBILITY #5
- saved output generated by action controller
$this->getResponse()->sendResponse();
Exercise:
- locate front controller class
- list all events that front controller fires
- list possible uses
Locate:
Mage_Core_Controller_Varien_Front
/app/code/core/Mage/Core/Controller/Varien/Front.php
init()
@todo
SECTION 3 - REQUEST FLOW - URL REWRITES
=======================================
- understand operating with URL rewrite structure
- rewrite a catalog/product view to different view
MAGENTO URL STRUCTURE
- allows to specify the URL key (known as URL identifier) on every static/content/product category page
Example web form for changng general settings:
GENERAL TAB | DISPLAY SETTINGS TAB | ...
Name Something
Is Active Yes/No
URL Key designer-something
Descrption example desc
You can choose a keyword and add to the particular page's URL independently from the Magento page name.
Database Rewrites Diagram
front Front > RewriteList > database table: core_url_rewrite > request
KEY OF REWRITES ON TABLE 'core_url_rewrite':
request_path
target_path
URL REWRITE
Type
Store
ID Path
Request Path
Target Path
Redirect - Yes/No
Description
Admin Panel Fields Reference
DIFFERENT TABLE IN VERSION 1.8 and <1.8!
Go to Magento ACP > Catalog > URL Rewrite Management.
Check a table 'core_url_rewrite' after on ACP adding URL Rewrite.
Exercise:
- go to admin
- create a product/category
- specify url for product/category
- go to frontend
- load product/category view page using url
- go to core_url_rewrite table, find record
Catalog > Manage products > Add Product (Default Attribute Set with Simple Product Type).
Put info under General/Prices/Description and Categories Tab.
Go to frontend and view product.
HINT! > Stock Availability -> In a stock select.
If didn't show clear cache.
SECTION 3 - REQUEST FLOW - REQUEST ROUTING
==========================================
- creating own module with controller
- overrriding existing controller
REQUEST FLOW
The Magento application is instantiated > It instantiates a Front Controller object > Front Controller instantiates routers > Routers check the request URL to match pattern > If a match is found, the action controller instantiates and the action name calls > The controller action instantiates a layout object > The layout object generates the nested blocks tree > layout starts rendering > Blocks refer directly to the models to obtain their data
Admin
- process Admin scope Requests
- Mage_Core_Controller_Varien_Router_Admin
Standard
- process Front-end scope Requests
- Mage_Core_Controller_Varien_Router_Standard
Custom
- .. _Custom
Default
- Process no-route action, 404 page
- Mage_Core_Controller_Varien_Router_Default
Mage_Core_Controller_Varien_Router_Standard::match()
public function match(Zend_Controller_Request_Http $request)
{
// check current module for this router
// default routes
// get module name
// router arguments
// check exact name if not in a array lists modules
foreach ($modules as $realModule)
{
// controller, action, secure page, instance controller, action controller
}
}
SYSTEM ENDPOINT
Index.php do:
- all requests >> index.php
- action will produce response
- routes sends routing request to controller
- add new routers
MATCHING APPROPRIATE ROUTER
index.php > Mage::run() > Mage_Core_Controller_Varien_Front::dispatch()
> Custom_Router
> Router_Admin
> Router_Standard
> Cms_Router
CHECKING ROUTER FOR COMPATIBILITY
Each router has match() thats:
- Check - current request routed by the router?
- If Yes: create instance controller + action
Request > Router::match() > false
Request > Router::match() > true > Controller::dispatch($action)
EXECUTION OF CONTROLLER'S ACTION
Steps:
- change requests object
- call controller instance
// set values
setModuleName()
setControllerName()
setActionName()
setControllerModule()
// dispatch
setDispatched(true)->dispatch($action)
ROUTER REGISTERING
- router could be registered using system configuration (XML)
<config>
<stores>
<default>
<web>
<routers>
<admin>
<area>admin</admin>
<class>Mage_Core....</class>
<standard>
...
RULES OF URL'S COMPOSITION
Standard Router: Mage_Core_Controller_Varien_Router_Standard
$baseUrl / $frontName / $controllerName / $actionName / $otherParams
cms/install/createuser
module/controller/action
STANDARD ROUTER
if no module specified in URL (base URL) >> it use 'default' from config.
Mage::getStoreConfig('web/default/front');
Magento ACP > System > Web > Default Pages .. here is default pages of pages
SYSTEM TOOLS FOR URL'S
In actions:
$this->_redirect('checkout/cart');
In other places:
Mage::getUrl($path, $arguments);
Mage_Core_Model_Url:
$route = Mage::getModel('core/url')
->setRouteName('checkout')
->setControllerName('cart')
->setActionName('index')
->getActionPath();
You can: $this->_redirect('http://...');
REQUEST FLOW - describes how works and when is redirecting...
TYPICAL ADMINHTML CONTROLLER
See class Mage_Admnhtml_Cms_Page, it's shows rendering titles, layouts...
CONTROLLER:
Controller does:
- processing requests parameters
- init blocks
- set layout
- prep response objects
- exceptions
- rendering layout
It's on example 'indexAction()' method for example: '$this->getLayout()->getBlock('content')->append(...)', '_initLayoutMessages'....
HINT!
Controllers doesn't contain business logic. Should be into models/helpers.
Example config:
<config>
..
<frontend>
<routers>
<cms>
<use>standard</use>
<args>
<module>Mage_Cms</module>
<frontName>cms</frontName>
</args>
REWRITE ABILITIES:
Rewrite of existing controlller:
/app/Mage/core/Catalog/etc/config.xml
<frontend>
<routers>
<catalog>
<use>standard</use>
<args>
<module>Mage_Catalog</module>
<frontName>catalog</frontName>
</args>
</catalog>
</routers>
</frontend>
My/Module/controllers/Catalog/____Controller.php::____Action()
<modules>
<My_Module before="Mage_Catalog">My_Module_Catalog</My_Module>
</modules>
Exercise:
- create own module+controller with hello world
app/code/local/Foo/Bar/etc/config.xml
----------------
<?xml version="1.0" encoding="UTF-8"?>
<!-- app/code/local/Foo/Bar/etc/config.xml -->
<config>
<frontend>
<routers>
<foo_bar><!-- unique -->
<use>standard</use> <!-- admin -->
<args>
<module>Foo_Bar</module> <!-- module name -->
<frontName>foo</frontName> <!-- name on front page -->
</args>
</foo_bar>
</routers>
</frontend>
site.com / foo / index / index
becomes -->
mainpage / frontName (always configured) / Controller / Action
frontName >> to call controllers: Foo / Bar / controllers / ...
Controller >> Controller first call IndexController.php, Directly: Foo_Bar_IndexController
Action >> indexAction()
Create controller folder app/code/local/Foo/Bar/controllers and controller 'Pasta' on /data3/web/www/YOURPROJECT/mag1.8.0.0/app/code/local/Foo/Bar/controllers/PastaController.php
PastaController.php
-------------------
<?php
class Foo_Bar_PastaController extends Mage_Core_Controller_Front_Action {
public function sleepAction()
{
echo '<h1>I\'m so tired after eating yummy pasta.</h1>';
die();
}
}
Go to browser:
--------------
http://WEBROOT/foo/pasta/sleep
Result:
<h1>I\'m so tired after eating yummy pasta.</h1>
Overrides a class name:
Change config.xml under Foo/Bar/etc/config.xml
<config>
<routers>
<foo_bar>
...
</foo_bar>
<catalog>
<args>
<modules>
<foo_bar before="Mage_Catalog">Foo_Bar_Catalog</foo_bar>
</modules>
</args>
</catalog>
Create folder 'Catalog' under 'Foo/Bar/controllers'.
Under 'Foo/Bar/controllers/Catalog' create 'CategoryController.php' file.
Foo/Bar/controllers/Catalog/CategoryController.php
--------------------------------
<?php
require_once 'Mage'.DS.'Catalog'.DS.'controllers'.DS.'CategoryController.php';
class Foo_Bar_Catalog_CategoryController extends Mage_Catalog_CategoryController
{
public function viewAction()
{
die('View action');
}
}
Go to page of any product catalog:
----------------------------------
http://YOUR_HOSTNAME/index.php/electronics/cell-phones.html
Result:
View action
SECTION 3 - REQUEST FLOW - MODULES INITIALIZATION
=================================================
- create and register a new module
- create a controller in your module
- enable/disable module
DECLARATION
Each modules must be declared by putting an XML file for that module into 'app/etc/modules' directory.
Example XML config on 'etc/modules':
<config>
<modules>
<Mage_Eav>
<active>true</active>
<codePool>core</codePool>
<depends>
<Mage_Core/>
</depends>
</Mage_Eav>
</modules>
</config>
CODE POOL
- each module should be declared within one code pools:
- core
- community
- local
Code pool > Activation flag > dependencies > names and version > class groups > class connections > own setup model > layout and translation files > module specifies
ACTIVATION FLAG
If [false]
>> module won't load
All attributes from previously installed by the module will remain ACTIVE,
so it could lead to an exception or/and source models were specified by the module.
MODULE DEPENDENCIES
If module depends on other Magneto modules as configured in a module registration file, then all of them should be listed.
MODULE NAME AND VERSION
- required by SQL install/upgrade execute
During requests flow, system checks version each module stored in 'core_resource' table and compares it with one specified in a config.
If version is different it executes upgrade script.
CLASS GROUPS
Each module - factory prefix for class groups
Example: Mage::getModel() have to specify approprieate nodes in 'config.xml'
Example code:
$model = Mage::getModel('ent_reward/action_tag');
<global>
<models>
<ent_reward>
<class>Ent_Reward_Model</class>
<resourceModel>ent_reward_mysql4</resourceModel>
</ent_reward>
<ent_reward_mysql4>
<class>Ent_Rew_Model_Mysql4</class>
See on XML declarations of
- blocks
- models
- resource models
- helpers
Mage::getModel('namespace_module/controller');
DATABASE CONNECTION
- each module can declare own database connection configuration
- allow to use different database/storage
SETUP MODEL
If module performs complex operations within install/upgrade processes it obviously requires for specific methods
- all install/upgrade scripts run within setup model scope
LAYOUT AND TRANSLATION FILES DECLARATION
- can specify own layout files (one per area)
- same for translations
Example:
Layout declaration
<adminhtml>
<layout>
<updates>
<training_catalog>
<file>some/somefile.xml>/file>
</training_catalog>
</updates>
</layout>
</adminhtml>
SPECIFIC MODULE CONFIGURATION
- every modules can have own configuration scopes
CONSIDERING:
- Module specific table declarations
- Observers declarations
- Cron job configurations
- Routes/actions registertings
- activation sequence
Exercise:
Create 'etc/modules/Day_Two.xml' config module:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Day_Two>
<active>true</active>
<codePool>local</codePool>
</Day_Two>
</modules>
</config>
Create folder 'local/Day/Two/etc' with config file 'config.xml'.
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
<blocks>
</blocks>
<helpers>
</helpers>
<models>
</models>
</global>
</config>
Then, put class groups:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
<blocks>
<day_two> <!-- class group -->
<class>Day_Two_Block</class> <!-- path of object types -->
</day_two>
</blocks>
<helpers>
<day_two>
<class>Day_Two_Helper</class>
</day_two>
</helpers>
<models>
<day_two>
<class>Day_Two_Model</class>
</day_two>
</models>
</global>
</config>
Accessing to modules:
Mage::getModel('day_two/__');
Mage::helper('day_two/___');
Mage::app()->getLayout()
Put before '</config>' for frontend settings:
<frontend>
<routers>
<day_two>
<use>standard</use>
<args>
<module>Day_Two</module>
<frontName>heyo</frontName> <!-- KEY OF MAIN FRONT NAME -->
</args>
</day_two>
</routers>
</frontend>
Create folder 'controllers' and files 'IndexController.php'.
app/code/local/Day/Two/controllers/IndexController.php
------------------------------------------------------
<?php
<?php
class Day_Two_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
die('Day_two index action');
}
}
On browser open 'http://HOSTROOT/heyo' than result will be 'Day_two index action'.
Add 'modelAction()' on IndexController.php:
------------------------------------------------------
<?php
class Day_Two_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
die('Day_two index action');
}
public function modelAction()
{
echo get_class(Mage::getModel('day_two/Whatever'));
}
}
Create folder 'Model' with php file model name 'Whatever.php'.
/local/Day/Two/Model/Whatever.php
---------------------------------
<?php
class Day_Two_Model_Whatever extends Mage_Core_Model_Abstract
{
}
Go to site 'http://HOSTROOT/heyo/index/model' you see result 'Day_Two_Model_Whatever'.
Exercise:
- create a new model (folders, two config files)
- register class group for models, block and helpers
- create an empty model file in the Model folder
- extend it from Mage_Core_Model_Abstract
- create a controller in your module
- get the model instance using Mage::getModel()
- make your module dependent from Mage_Log
- disable Log module and verify your module doesn't work any more
- enable back Log module and disable your own module
- enable your module but disable all local modules
Mage_all.xml is located on /etc/modules folder. You see 'Mage_Log' for an example.
Go to /app/etc/modules/Day_Two.xml and add '<depends>' module:
Change it config to add 'Mage_Log' depends:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Day_Two>
<active>true</active>
<codePool>local</codePool>
<!-- depends event -->
<depends>
<Mage_Log />
</depends>
<!-- depends event -->
</Day_Two>
</modules>
</config>
Go to ACP, Sytem > Advanced > Disable Modules Output. You can enable/disable specific module output.
SECTION 3 - REQUEST FLOW - DESIGN AND LAYOUT INITIALIZATION
===========================================================
- design data is populated
- layout configuration files are parsed
- layout is compiled
- output is rendered
OVERVIEW
VIEW - renders the model into a form suitable for a user interface elements
Multiple views can exist for a single model for different purposes
View model "block"
View Model is a "Model of the View" meaning it is and abstraction of the View that serves in a data binding between the View and the Model.
Magento - as described in Model-View-Controller (MVC) application with a configuration-based representation level
FLOW: Layout XML --> Block --> Template
THE LAYOUT
- compilation of the response from highly reusable UI (user interface) elements, called view BLOCKS
- ability to configure the response rendering process via sets of instructions, so called LAYOUT
The layout - hierarchial XML based structure them into tree structure and define the rendering process flow to combine response
Main Page
> Head
> Js, Css
> Header
> Top navigation, Mini Shopping Cart
> Main Content
> ***
> Footer
Layout example:
PAGE
Head block
Header block
menu - content - sidebar blocks (menu have other small blocks..)
MODULE LAYOUT UPDATE FILE
- each module can add layout xml by declaring layout update file in 'etc/config.xml'
<config>
<modules>
<Mage_Catalog>
<version>1.4.0.0.38</version> <!-- version -->
...
LAYOUT XML SCHEMA
- process follows fallback algorithm
- separate layouts of every module do ot just complement each other, but modify already loaded definitions
- Magento modules dependencies define the ordering of layout files during merge process
Layout files - merged together + used as one big layout tree
checkout.xml + catalog.xml + targetrule.xml = Final Layout XML
LAYOUT FALLBACK
app/design/frontend/enterprise/[THEME]/layout/catalog.xml
app/design/frontend/enterprise/default/layout/catalog.xml
app/design/frontend/base/default/layout/catalog.xml
Allows building of custom themes by simply extending from default theme.
EXERCISE:
- print some page's merged XML
- print to file using Mage::Log()
Use: '/local/Day/Two/controllers/IndexControler.php' and 'config.xml':
'indexController.php'
---------------------
<?php
class Day_Two_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
die('Day_two index action');
}
public function modelAction()
{
echo get_class(Mage::getModel('day_two/Whatever'));
}
// LAYOUT ACTION //
public function layoutAction()
{
$xml = $this->loadLayout()->getLayout()->getUpdate()->asString();
$this->getResponse()->setHeader('content type', 'text/plain')->setBody($xml);
}
}
Go to browser, open action '/heyo/index/layout' and you can see XML in plain format:
<block name="formkey" type="core/template" template="core/formkey.phtml"/>
<label>All Pages</label>
<block type="page/html" name="root" output="toHtml" template="page/3columns.phtml">
<block type="page/html_head" name="head" as="head">
<action method="addJs">
<script>prototype/prototype.js</script>
</action>
<action method="addJs">
<script>lib/ccard.js</script>
</action>
<action method="addJs">
<script>prototype/validation.js</script>
On 'IndexController' add method:
public function defaultAction()
{
$this->loadLayout()->renderLayout();
}
Result on '/heyo/index/default':
You can see default layout page. It's specify from default 'Mage_Core_Controller_Front_Action' default layout page.
LOGGGING:
Mage::log() is in Magento ACP under Developer > Log.
Put on 'IndexController.php' on some action:
Example:
public function layoutAction()
{
$xml = $this->loadLayout()->getLayout()->getUpdate()->asString();
$this->getResponse()->setHeader('content-type', 'text/plain')->setBody($xml);
Mage::log($xml, Zend_Log::INFO, 'layout.log', true);
}
You can see on /app/var/log/layout.log file.
Make sure that CHMOD 0777 on /var folder.
By default Magento not create folder and files in Magento directory.
SECTION 3 - REQUEST FLOW - ROLE OF TEMPLATE IN REQUEST FLOW
===========================================================
- rendering a root template
- removing a child from the page
REQUEST FLOW DIAGRAM
Init > App > FrontEnd > Controller > Rendering > output
Controller > Models > Rendering // + Database
CHILD BLOCK
- non-output blocks are child blocks and are normally rendered with the method
- getChildHtml()
- methods renders a child block according to the block name or alias supplied in the arguments
- there are also 2 other methods:
getChildChildHtml()
getBlockHtml()
Explanation of Child Controller:
PAGE
Head block <-- block class + template file
Header block
menu - content - sidebar blocks (menu have other small blocks..)
Extensions:
block class is PHP file.
template file is PHTML file.
Example: NATIVE ROOT TEMPLAT:
<html><body>
...
<?php echo $this->getChildHtml('context'); ?>
...
</body><html>
Locations:
TEMPLATE:
app/design/frontend --> package/theme/template
page/1column.phtml
MODIFIED ROOT TEMPLATE:
<html><body>
...
<?php echo $this->getChildHtml('footer_before');
<?php echo $this->getChildHtml('footer');
<?php echo $this->getChildHtml('before_body_end');
...
</body><html>
EXERCISE:
Go to app/design/frontend/enterprise/default/template/page/1column.phtml
Remove all getChildHtml(") calls <--- I prefer commenting, so use <!-- --> in HTML code
Be prepared todescribe what happens with the page
Locate: /app/design/frontend/base/default/template/page/1column.phtml
You can see template blocks calling like this:
<?php echo $this->getChildHtml('after_body_start') ?>
<div class="wrapper">
Comment something and view frontend. It will didn't show some template. (better in source code to find "<div>" styles.)
Example of header block:
<?php echo $this->getChildHtml('header') ?>
On '/app/design/frontend/base/default/template/page/html' you can see HTML blocks of separated blocks which rendered from base '/app/design/frontend/base/default/template/' template.
SECTION 3 - REQUEST FLOW - FLUSHING OUTPUT
==========================================
- renders content to browser
- flushes output variables using frontController
FLUSHING OUTPUT:
- Init
- Generate page
- Generate blocks
- Execute output blocks
- include template
- execute child blocks
- FLUSHING OUTPUT
Example: FLUSH OUTPUT CODE
class Mage_Core_Controller_Varien_Front extends Varien_Object
/app/code/core/Mage/Core/Controller/Varien/Front.php
public function dispatch()
{
$request = $this->getRequest();
$this->_checkBaseUrl($request);
$request->setPathInfo()->setDispatched(false);
$this->_getRequestRewriteController()->rewrite();
Varien_Profiler::start('mage::dispatch::routers_match');
Varien_Profiler::stop('mage::dispatch::routers_match');
Mage::dispatchEvent('controller_front_send_response_before', array('front'=>$this));
Varien_Profiler::start('mage::app::dispatch::send_response');
$this->getResponse()->sendResponse();
Varien_Profiler::stop('mage::app::dispatch::send_response');
Mage::dispatchEvent('controller_front_send_response_after', array('front'=>$this));
return $this;
}
FLUSHING JAVASCRIPT AND CSS TO ONE FILE
- Javascript: reduce loading time by limiting HTTP requests, keep requests down by flushing all of JS files (System -> Configurations -> Developer -> Javascript -> Settings)
- CSS: Some idea as JS flushing, test on staging server before taking it live to see if it has any side effects
(System -> Configurations -> Developer -> CSS -> Settings)
REVIEW
- Magento approach to rendering
- Zend_Http_Response_Object is Magento wrapper
- Front_Controller flushing output variables
Example on ACP flushing:
JavaScript Settings
Merge JavaScript Files [STORE VIEW]
CSS Settings
Merge CSS Files [STORE VIEW]
SECTION 3 - RENDERING SYSTEM - OVERVIEW
=======================================
Lessons:
- Templates
- Blocks
- Design layout XML schema
Rendering Steps:
FLUSHING OUTPUT:
- Init
- Generate page
- Generate blocks
- Execute output blocks
- include template
- execute child blocks
- flushing output
LEARNING OBJECTIVES:
- fallback themes
- design packages
- products
DESIGN ELEMENTS
skin (Visual layout info and files + UI specific JS)
- css
- images
- js
app/design
- layout (page generation xml instructions for each module)
- template (content block .phtml files for each module)
Relation:
app/design/frontend <---> skin/frontend
TEMPLATE STRUCTURE
- Page template (phtml structural blocks) <--> layout files (xml) <--> content block templates (phtml) - more files
THEME CHARACTERISTICS
- a single layout file (local.xml), all layout updates
- no layout files with same nam as any layout file in base theme
- *no css files with same name as any css in default skin
- no .phtml template files, except those that were modified to support new theme (number of number .phtml is small)
EXAMPLE:
ACP > Configuration > Admin > Design
You can see under 'Package' of current active package name 'default'.
PACKAGES
-> configured -> skin
-> template
-> layout
-> configured "Default"
-> package/defaults
TEMPLATE USE IN MAGENTO
Layout <---> Controller <--> Model
Controller <-- Block, Template
DIRECTORY STRUCTURE
Images/CSS files for theme are in skin/frontend
Visual layout info and files & UI specific JS
skin [SKIN DIRECTORY]
+ adminhtml
+ frontend [BASE DESIGN PACKAGE, default theme]
+ base
+ default [default theme]
+ css
+ images
+ js
favicon.ico
+ enterprise [ENT. DESIGN PACKAGE]
+ default [default theme]
+ css
+ images
+ js
favicon.ico
Page templates and layout files for a theme in app/design/frontend
app
+ design
+ adminhtml
+ frontend
+ base [BASE DESIGN PACKAGE]
+ etc
+ layout
+ template
+ default
+ enterprise [ENT. DESIGN PACKAGE]
+ default [default theme]
+ etc
+ layout
+ template
+ locale
THEMES AND WHAT THEY CONTROL
- visual aspect (skin) CSS, images, design/user interface
- many functional aspects of your site
- allows the presentation layer to be independent of business logic and functionality
RELATIONSHIP WITH FILESYSTEM
ACP > Design > Package - default of configuration calling on /app/design/frontend/NAME_OF_PACKAGE or /skin/frontend/NAME_OF_PACKAGE
DESIGN PACKAGES
Default packages:
- collections of related themes
Default themes:
base / default
enterprise / default
enterprise-custombrand 3 / default /variation 1
default /variation 1 == Non-default themes
STORE'S DESIGN CONFIGURATION
<stores>
<admin>
<design>
<package>
<name>default</name>
</package>
<theme>
<default>newTheme</default>
<skin>newTheme</skin>
</theme>
</design>
</admin>
</stores>
EXERCISE 1:
- create own theme
- register theme in System > Configuration > Design > Themes > Default
- override page/1column.phtml template
- echo '<h1>NEW THEME</h1>'
EXERCISE - COPY THEMES AND CHANGE FILES ON IT
Create folder /design/frontend/default/training/ and /design/frontend/default/training/template
Create folder under /design/frontend/default/training/template/page/1column.phtml
Copy 1column.phtml from some other template like 'default'.
Note! To affecting themes you need to change in ACP > System > Design > Themes
Put on 'Default' field to value 'training'. It will load from /design/frontend/default/training/template.
Copy all files from /design/frontend/default/default/template/page/..*.phtml
Change or add some text on it.
Put on 'Templates' field to value 'highest'. It will load from /design/frontend/default/highest/template.
Apply by clicking on 'Save Config' to affecting themes changes.
FLUSHING TEMPLATES/THEMES:
IF NOT AFFECTING ON CHANGES: System > Cache Management > check 'Blocks HTML output', set to 'Refresh' on Actions and Submit.
IF YOU ARE NOT SURE WHICH CLASS THEME IS CALLED:
Put in template:
<?php echo get_class($this); ?>
HOW TO FIND LAYOUT FROM *.PHTML FILES? / QUICK WAY /
Find like
<div class="main_call-layout">
<?php $this->getChildElement('header'); ?>
Find on layout folder as:
name="header" to find on XML a block of header element.
EXERCISE 2:
- create and register new package
- verify base/default fallback
- copy enterprise/default theme to new package/default
- verify is that used
- copy it again and verify is used
Create folder 'custom_pkg' under '/design/frontend/'.
Change on ACP > System > Design > Package > Current Package Name to 'custom_pkg'.
Reload frontend. In source code you can see that loads 'skin/frontend/base/default' skin folder.
Steps loading templates:
1. custom_pkg/highest/template
2. custom_pkg/training/template
custom_pkg/training/layout
custom_pkg/training/skin
3. custom_pkg/default/*
4. base/default/*
* - template,layout,skin folders
DEFAULTS: FALLBACKS
( design packages )
base --> enterprise --> brand1
| | |
default themes --> +-->default +-->default +-->default
variationX (non-default themes)
FALLBACK OF DESIGNS
if [STORE THEME]
return STORE THEME
else if [DEFAULT THEME]
return
else if [BASE THEME]
return
EXERCISE 2 - Create fallback theme
- create and register a new package
- verify base/default fallback is used
- copy enterprise/default to new package/default
- verify new package/default is used
- copy enterprise/default theme to new package/new_theme
- verify package/new_theme
Create folder /app/design/frontend/custom_pkg/custom/template/page
Copy 1column.phtml from /app/design/frontend/default/template/page and edit something.
On ACP > System > Design > Change store > Remove skin/images a value from field. Go to frontend french store. It's affected on changes.
EXERCISE 3 - CREATE FALLBACK THEME
- create a new primary theme in a new package
- copy page/1column.phtml to a new primary theme
- check a new package with default/new/primary theme
- register new primary theme for templates only
- determine order of loading templates
- add label to1column.phtml example:
<h1>NEW PRIMARY</h1>
<h1>NEW THEME</h1>
Create folder /app/design/frontend/custom_pkg/custom
Copy some files from deafault page.
Change templates name to 'custom'. Load page.
RECURSIVE VIEW: TEMPLATES
First step > catalog/product_view
catalog/product_view HAS:
Second step > catalog/product_view_media
catalog/product_list_upsell
catalog/product_view_additional
core/text_list
...
EXERCISE 4 - CREATE DIFFERENT THEME FOR DIFFERENT STORE
From ACP > System > Design > Change store > change a value from 'Template' field.
EXERCISE 5 - ADD 'HELLO WORLD' TO A PRICE AS MESSAGE APPEARS IN EACH PLACE WHERE PRICE RENDERED.
@todo
SECTION 4 - RENDERING SYSTEM - MAGENTO BLOCKS
=============================================
- create and set a new page content
- create blocks for the page
- add a child to a block
- override blocks
WHAT IS BLOCK
- "page" is not the base unit in Magento design - Blocks are
Blocks - the building material to compose different output from the same things
froentend
+base
+default
+template
+view.phtml
view.phtml
----------
<?php echo $this->getChildHtml('addto'); ?>
$this --> block instance
getChildHtml('...') <--- child block html
STRUCTURE OF BLOCKS
Custom_Block --> Mage_Core_Block_Template --> MAge_Core_Block_Abstract
--> ex: <h1><?php echo $this->__('Shopping Cart'); ?>
Mage_Core_Block_Template
#_template
+setTemplate()
+getTemplateFile()
+_toHtml()
Mage_Core_Block_Abstract
#_children
+getChild()
+getChildHtml()
+toHtml()
BLOCK CLASS GROUP DECLARATION
Mage_Catalog_blocks classs group declaraton:
<global>
<blocks>
<catalog>
<class>Mage_Catalog_Block</class>
</catalog>
</block>
</global>
$block = Mage::app()->getLayout()->createBlock('catalog/product_view');
Myspace_Custom_blocks classs group declaraton:
<global>
<blocks>
<custom>
<class>Myspace_Custom_Block</class>
</custom>
</block>
</global>
$block = Mage::app()->getLayout()->createBlock('custom/product_view');
EXERCISE - CREATE NEW PAGE, BLOCK, TEMPLATE
- create and register controller, set response body "HELLO WORLD"
- verify
- regster blocks, create blocks class, overide _toHtml() method and return 'HELLO WORLD'
- add block output to response body
- create core/template block and a template in a custom theme and in controller add output to response body
Create controller file: /app/code/local/Day/Two/controllers/RenderController.php
/app/code/local/Day/Two/controllers/RenderController.php
<?php
class Day_Two_RenderController extends Mage_Core_Controller_Front_Action
{
public function blockAction()
{
$this->getResponse()->setBody('Hello Magento');
}
// register block
public function overrideAction()
{
// calling block from Sample.php
$blockHtml = $this->getLayout()->createBlock('day_two/sample')->toHtml();
$this->getResponse()->setBody($blockHtml);
}
}
Create folder 'Block' and PHP file 'Sample.php':
/app/code/local/Day/Two/Block/Sample.php
<?php
class Day_Two_Block_Sample extends Mage_Core_Block_Template
{
protected function _toHtml()
{
return 'Hello Day_Two_Block_Sample from '.__FILE__.' and a class: '.__CLASS__;
}
}
Browser: http://HOST/index.php/heyo/render/block
Result: Hello Magento
Go to http://HOST/index.php/heyo/render/override
Result: Hello Day_Two_Block_Sample from /data3/web/www/YOURPROJECT/mag1.8.0.0/app/code/local/Day/Two/Block/Sample.php and a class: Day_Two_Block_Sample
Create template:
Add templateAction in controller 'RenderControler.php':
public function templateAction()
{
$blockHtml = $this->getLayout()->createBlock('core/template')->setTemplate('day_two/random.phtml')->toHtml();
$this->getResponse()->setBody($blockHtml);
}
Explanations:
setTemplate('path/relative/to/template/*.phtml');
It will be blank when you calling a site 'http://HOST/index.php/heyo/render/template'.
By default it will be called defined in ACP under packages and templates, in this case it will be searching for template folder under 'design/frontend/base/default/template/day_two' template folder.
Create folder design/frontend/base/default/template/day_two with filename 'random.phtml' template file.
/app/design/frontend/base/default/template/day_two/random.phtml
---------
<!-- app/design/frontend/base/default/template/day_two/random.phtml -->
<h1>Template calling: <?php echo get_class($this); ?></h1>
Result: /heyo/render/template
<h1>Template calling: Mage_Core_Block_Template</h1>
How it works:
Calls method '$this->_toHtml' from '/app/Mage/Core/Block/Abstract.php' in a method '_toHtml':
final public function toHtml()
And on 'Template.php' it render for '_toHtml()' method for rendering on class and parsing to 'fetchView()' action:
/**
* Render block HTML
*
* @return string
*/
protected function _toHtml()
{
if (!$this->getTemplate()) {
return '';
}
$html = $this->renderView();
return $html;
}
It's logged not valid templates on '/var/log/system.log'.
Example:
2013-10-24T13:54:05+00:00 ERR (3): Warning: include(): Failed opening '' for inclusion (include_path='.../lib:.:/usr/share/php:/usr/share/pear') in /app/code/core/Mage/Core/Block/Template.php on line 241
STAGES OF BLOCK'S LIFE CYCLE
- declared in layout or just in code
- block instance created, it's _prepareLayout is called
- renderLayout() method called, system calls toHtml(), all children are rendered
- _beforeToHtml(), _toHtml() and _afterToHtml
EVENTS FIRED IN BLOCKS
public functon setLayout(Mage_Core_Model_Layout $layout)
final public function _toHtml()
- before and after html dispatchEvent
TYPES OF BLOCKS
- Mage_Core_Block_Template
_Text_List
_Messages
_Flush
_Text
_Template_Links
HOW TO DISABLE OUTPUT
XML:
<config>
<default>
<advanced>
<modules_disable_output>
<Mage_Cms>1</Mage_Cms>
</modules_disable_output>
</advanced>
</default>
</config>
Or on System adminsitration control panel.
BLOCK - HOW IS RETRIEVE CMS BLOCK AS HTML
app/code/core/Mage/Catalog/Block/Category/View.php
-------------------------------------------------
public function getCmsBlockHtml()
{
if (!$this->getData('cms_block_html')) {
$html = $this->getLayout()->createBlock('cms/block')
->setBlockId($this->getCurrentCategory()->getLandingPage())
->toHtml();
$this->setData('cms_block_html', $html);
}
return $this->getData('cms_block_html');
}
Go to ACP > Catlaog > Manage categories > under category settings:
you see 'Is active' and 'CMS block'.
BLOCK INSTANTIATAION IN CONTROLLER
gridOnlyAction()
GET TEMPLATE FROM THE BLOCK
From controller:
$this->getLayout()->getBlock('head')->getTemplate()
From an arbitrary place:
Mage::app()->getLayout()->getBlock('head')->getTemplate('...')
Setting template maually:
From inside the block:
$this->setTemplate('...')
From another place:
Mage::app()->getLayout()->getBlock('head')->setTemplate('...')
PASSING PARAMETERS TO BLOCK
From controller:
$this->getLayout()->getBlock('content')->setParam('...')->setAnotherParam('...')
Through registry:
Mage::register('some_var', 'someval');
From inside the block:
public functon getSomeVar() {
if (!$this->_someVar) {
$this->_someVar = Mage::registry('some_var');
}
}
OVERRIDE THE BLOCK
<global>
<block>
<checkout>
<onepage_link>Training_Catalog_Block_Checkout_Onepage_Link</onepage_link>
....
BASIC OPERATIONS WITH BLOCKS
setChild(alias, block) - adding child to the block
insert(block) - adding child to to text_list block
getChild(name) - getting child of the block
getChildHtml(name) - equivalent to getChild(name)->toHtml()
getChildChildHtml(name, childName) - equivalent to getChild(name)->getChild(childName)->toHtml()
EXERCISE 2: ECHO PARAMETERS ON PAGE, ADD TO BLOCK AND CREATE TEXT LIST BLOCK
Part 1:
- in the controller, register parameter in the Mageregistry
- create a new block class and returns value from registry
- create template to output/format value
Part 2:
- create text list block instance
- add two child blocks to this block using the insert() method
- verify that all blocks are rendered
Put method in '/app/code/local/Day/Two/controllers/RenderController.php':
public function registryAction()
{
Mage::register('some_var', 'some value');
$blockHtml = $this->getLayout()->createBlock('day_two/registry')->setTemplate('day_two/registry.phtml')->getThatRegistryValue();
$this->getResponse()->setBody($blockHtml);
}
And create in a Block module 'Registry.php':
<?php
class Day_Two_Block_Registry extends Mage_Core_Block_Template
{
public function getThatRegistryValue()
{
$var = Mage::registry('some_var');
return ($var) ? $var : 'registry not found';
}
}
Result: /heyo/render/registry
Template calling: Day_Two_Block_Registry
Add action to 'RenderController.php':
public function listBlockAction()
{
$tlb = $this->getLayout()->createBlock('core/text_list');
$blockA = $this->getLayout()->createBlock('core/text')->setText('Block A');
$blockB = $this->getLayout()->createBlock('core/text')->setText('Block B');
$tlb->insert($blockA)->insert($blockB);
$this->getResponse()->setBody($tlb->toHtml());
}
Result: /heyo/render/listBlock
---------------
Block BBlock A
Example how to rendering blocks layout using Magento blocks and core texts:
public function listBlockAction()
{
$tlb = $this->getLayout()->createBlock('core/text_list');
$blockA = $this->getLayout()->createBlock('core/text')->setText('Block A');
$blockB = $this->getLayout()->createBlock('core/text')->setText('Block B');
$tlb->insert($blockA)->insert($blockB);
//$this->getResponse()->setBody($tlb->toHtml());
$this->loadLayout();
$this->getLayout()->getBlock('content')->insert($tlb);
$this->renderLayout();
}
EXERCISE 3 - OVERRIDE A BLOCK CLASS
- using config module override the catalog product view block
- make change and verify worked
/etc/config.xml
---------------
01:24:56
On '/local/Day/Two/etc/config.xml' add into '<blocks>' tag a '<catalog>' tag:
Example:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
<blocks>
...
<!-- catalog_product_view -->
<catalog>
<rewrite>
<product_view>Day_Two_Block_Catalog_Product_View</product_view>
</rewrite>
</catalog>
<!-- catalog_product_view -->
</blocks>
...
</models>
</global>
<frontend>
...
</frontend>
</config>
Create folder 'Block/Catalog/Product'. Create 'View.php' file.
<?php
// /app/code/local/Day/Two/Block/Catalog/Product/View.php
class Day_Two_Block_Catalog_Product_View extends Mage_Catalog_Block_Product_View
{
protected function _toHtml()
{
return 'Returned '.__CLASS__;
}
}
EXERCISE 4 - LOCATE THE BLOCK WHICH RENDERS BREADCRUMBS AND ADD HARDCODED BREADCRUMB AT THE BEGINNING
- enable template hints
- find the block
- override it using coding
- change method
- clear cache
- verify
Go to System > Configuration > Translate Inline > Set to Yes for frontend.
On Developer tab change 'Template Hints' to 'Yes'.
Now on frontend you can see translation hints.
Create folder '/app/code/local/Day/Two/Block/Catalog/Html/'.
Create file 'Breadcrumbs.php':
<?php
// /app/code/local/Day/Two/Block/Catalog/Html/Breadcrumbs.php
class Day_Two_Block_Html_Breadcrumbs extends Mage_Page_Block_Html_Breadcrumbs
{
}
See how breadcrumbs works: '/app/code/core/Mage/Page/Block/Html/Breadcrumbs.php'. A key is arrays of breadcrumbs:
/**
* Array of breadcrumbs
*
* array(
* [$index] => array(
* ['label']
* ['title']
* ['link']
* ['first']
* ['last']
* )
* )
*
* @var array
*/
Change file '/app/code/local/Day/Two/Block/Catalog/Html/Breadcrumbs.php' to:
<?php
// /app/code/local/Day/Two/Block/Catalog/Html/Breadcrumbs.php
class Day_Two_Block_Html_Breadcrumbs extends Mage_Page_Block_Html_Breadcrumbs
{
protected function __construct() {
$this->addCrumb('google link', array('label'=>'Google',
'title'=>'Go to the Google',
'link'=>'http://www.google.com'));
}
}
Now go to some catalog and you can see added breadcrumb with google link.
SECTION 4 - RENDERING SYSTEM - DESIGN, LAYOUT, XML SCHEMA
=========================================================
- register layout xml
- create and add code to pages
- passing variables from layout to block
- add and customize JS
Talking about 'Magento Layout' from previous captions.
TWO FACES OF LAYOUT
- ability to describe structure of your page
- ability to penetrate into behaviour
LAYOUT USES
- create a layout of your page
- add/remove blocks using pages
- call any method of any blockon the page
- set parameters to the block
MODULE LAYOUT UPDATE FILE
- each module can add layout xml by declaring layout update file in 'etc/config.xml'
<config>
<modules>
<Mage_Catalog>
<version>1.4.0.0.38</version> <!-- version -->
...
</modules>
<frontend>
<layout>
<updates>
<catalog>
<file>catalog.xml</file>
</updates>
</layout>
</frontend>
TRANSFORMIN 'BIG' TO 'SMALL' LAYOUT
3 blocks --> 2 blocks
LAYOUT FALLBACK
app/design/frontend/enterprise/[THEME]/layout/catalog.xml
app/design/frontend/enterprise/default/layout/catalog.xml
app/design/frontend/base/default/layout/catalog.xml
Allows building of custom themes by simply extending from default theme.
LAYOUT UPDATE
- setting up layout
public function indexAction()
{
$this->loadLayout();
$this->_initLayoutMessages('customer/session');
$this->getLayout()->getBlock('content')->append('
$this->getLayout()
->createBlock('customer/account_dashboard')
);
$this->getLayout()->getBlock('head'
->setTitle($this->__('My Account'));
$this->renderLayout();
}
Controller must not contain business logic.
All business logic must be encapsulated into models and helpers.
STEP IN RENDERING LAYOUT
loadLayout > renderLayout()
HOW IT WORKS?
Mage_Core_Controller_Varien_Action::loadLayout()
It loads appending 'addBundle()' of handlers layouts and loading layouts by rendering on page. At end it will check for layout loading otherwise will be false.
Rendering layout: Mage_Core_Controller_Varien_Action::renderLayout()
LAYOUT XML
<layout version="0.1.0">
<default translate="label" module="page">
<block type="page/html_head" name="head" as="head">
...
</block>
</default>
</layout>
EXERCISE 1 - register own layout xml file, add code from template page, pass variable from layout to block:
- register layout file in custom module
- create invalid XML to check error messages
- add core/template block to "content" block in default handle
- on product detail page reference your block and set a value using action, output value in a template
On 'config.xml' module of 'day_two' put layout config:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
...
</global>
<frontend>
<!-- layout -->
<layout>
<updates>
<bens_layout>
<file>day_two/custom_xml</file>
</bens_layout>
</updates>
</layout>
<!-- layout -->
</frontend>
</config>
Create 'custom.xml' on '/design/frontend/base/default/layout/day_two/custom.xml'
<?xml version="1.0" encoding="UTF-8"?>
<layout>
<catalog_product_view>
<reference name="content">
<block type="context">
<action method="setText">
<arg>This is some text from layout day_two/custom.xml</arg>
</action>
</block>
</reference>
</catalog_product_view>
<cool_handle>
</cool_handle>
</layout>
Go to page '/electronics/cell-phones/atandt-8525-pda.htmlt' or on any product.
Result:
Returned Day_Two_Block_Catalog_Product_View
Change ''/design/frontend/base/default/layout/day_two/custom.xml'' and on catalog products view you can view changed text for the parsed layout.
<?xml version="1.0" encoding="UTF-8"?>
<layout>
<catalog_product_view>
<reference name="content">
<block type="context" name="oops">
<action method="setText">
<arg><![CDATA[<h1>This is some text from layout day_two/custom.xml</h1>]]></arg>
</action>
</block>
</reference>
<reference name="product_info">
<action method="setThisDoesntExist">
<abcdefg>String from setThisDoesntExist</abcdefg>
</action>
</reference>
</catalog_product_view>
<cool_handle>
</cool_handle>
</layout>
LAYOUT XML BASIC DIRECTIVES
Directive Description
contacts_index_index Controller action alias name it means that blocks in this node will be showed when http://<site_name>/contacts/index/index accessed
block Block declaration directives
action Directive allows to call any method from target block
reference Reference's content will be merged into target block. Attribute "name" specifies the referenced block name.
update Merge to the current handle all directives from target handle.
remove Set "ignore" flag for the target block.
Example of layout update XML:
<layout>
<layout_update_handle>
<block>
<action />
</block>
<reference name="block" />
<update handle="" />
<remove name="block" />
</layout_update_handle>
</layout>
LAYOUT XML BLOCK DIRECTIVE
Attribute Description
type or class Block class name, which used for factory method.
name A name by which other blocks can make reference to the block in which this attribute is assigned
as Block alias name
parent Parent block name. Used for adding current block into parent block
before Block name or "-". Used for adding current block before the block name specified (core/text_list_children_only)
after Block name or "-". Used for adding current block after the block name specified (core/text_list_children_only)
template Template (*.phtml) file path.
output Method for block output rendering.
Example:
<block type="core/template"
name="something"
as="aliasstring"
parent="content"
template="some/path"
output="toHtml">
BLOCK DIRECTIVE
<default translate="label" module="page">
<block type="page/html" name="root" output="toHtml" template="page/3column.phtml">
<block type="page/html_head" name="head" as="head">
..
</block>
</defualt>
<?php
3column.phtml:
<!DOCTYPE ..>
<head>
<?php echo $this->getChildHtml('head') ?>
</head>
<body...
<?php echo $this->getChildHtml('global_notices') ?>
LAYOUT XML ACTION DIRECTIVE
Attribute Description
method Block method name
translate Attribute used for method arguments translation
module Value for this attribute will be module name which you want to translate. Mage_Core module will be default if this attribute is missing
helper Helper name with helper method
json Json values which need to be decoded. All decoded values will be passed to the block method
ifconfig Allows to check a configuration flag value in order to allow or deny the action processing.
ACTION DIRECTIVE
<block>
....
<action method="..." ifconfig="dev/js/deperecation">
<script>prototype</script>
</action>
...
LAYOUT XML REFERENCE DIRECTIVE
<catalog_product_view translate="label">
...
<reference name="root">
<action method="setTemplate">
<template>page/2columns-right.phtml</template>
</action>
</reference>
LAYOUT XML REMOVE DIRECTIVE
name Target block name to be removed.
<customer_account_login translate="label">
<label>...</label>
<remove name="right" />
<remove name="left" />
<reference ...>
</reference>
</customer_account_login>
LAYOUT XML UPDATE DIRECTIVE
Atrribute Description
handle Target block name to be included.
<checkout_multishipping_login>
<update handle="customer_account_login" />
</checkout_multishipping_login>
<checkout_multishipping_register>
<update handle="customer_account_create" />
</checkout_multishipping_register>
EXERCISE
EXERCISE 2. SETUP YOUR OWN HANDLE
Part 1:
- create own handle in layout xml file, add output block to handle
- in controller, add a new action and call: $this->loadLayout(_YOUR_HANDLE_)->renderLayout();
- verify thsat your handle works
Part 2:
- add update node to include your layout update handle in cms_index_index
Change '/app/design/frontend/base/default/layout/day_two/custom.xml' in '<cool_handle>' tag:
<?xml version="1.0" encoding="UTF-8"?>
<layout>
<catalog_product_view>
...
</catalog_product_view>
<!-- template cool_handle tag -->
<cool_handle>
<block type="core/template"
name="some.name"
output="toHtml"
template="som/template.phtml" />
</cool_handle>
<!-- template cool_handle tag -->
</layout>
Crete folder and file under '/app/design/frontend/base/default/template/day_two/some/template.phtml':
<h1>Some template</h1>
Add action on controller '/app/code/local/Day/Two/controllers/RenderController.php':
public function handleAction()
{
$this->loadLayout('cool_handle')->renderLayout();
}
Put into 'custom.xml' for rendering template data:
<foobar_render_final>
<update handle="cool_handle" />
</foobar_render_final>
Result: It will parsing template under block element.
Specify another block as simple in config.xml:
<cool_handle>
<block type="core/template"
name="some.name"
output="toHtml"
template="some/template.phtml" />
<!-- another block -->
<block type="core/text"
name="some_other_block">
<action method="setText">
<argu>Some text</argu>
</action>
</block>
</cool_handle>
Change template block for parsing template block:
'/app/design/frontend/base/default/template/day_two/some/template.phtml':
<h1>This is some Template </h1>
<?php echo $this->getChildHtml('some_otherBlock'); ?>
EXERCISE 3: MOVE BLOCK
Choose a category page with 3 columns and layered navigation
Part 1
- move the poll block for the right column to the left block above layered navigation using your module's layout xml
- use the same block instance
Part 2
- make the homepage use 3 column layout
- make the poll appear to the top of the left column
- ensure that you don't have two polls in the left colunn on the category page
ACP > CMS > Manage Pages > Edit 'Home page'
SECTION 4 - DATABASE IN MAGENTO - OVERVIEW
==========================================
Section Topics
Lesson 1: Magento Modules, Resource Models, Collections
Lesson 2: Install and Upgrade Scripts
Magento Modules, Resource Models, Collections:
- configure database conenction
- create and register new entities
- load and save an entity from database
- filter and implement group save for a set of records
Topic about MVC
STORING DATA
Entity-based approach for storing data (table)
id sku price...
1 4324 43.44
MODEL LAYER IN MAGENTO
Model - access to the data and C.R.U.D. operations
Resource Model - work only through Resource Model
Collection - manipulaton of multiple records
STORE TYPES
Simple Model (Flat) - Store,Website,URL Rewrites, Widget,...
Complex Model - CMS Block/Page, Qoute, Order,..
EAV Model - customer, address, category, product
BASIC DATA ACCESS
Databases <-> DataBase Adapter <-> Model
GENERIC DATA ACCESS
Databases <-> DataBase Adapter <-> Resource Model <-> Model
SIMPLE DATA ACCESS
Databases <-> DataBase Adapter <--> Resource Model + Resource Collection <-> Model
MODEL DECLARATION
To enable models in your module, declare modules class group in module's config xml file.
<global>
<models>
<catalog> <!-- model group -->
<class>Mage_Catalog_Model</class>
...
USING MODEL:
$model = new Mage_Catalog_Model_Product(); <--- DEPRECATED! don't use
$model = Mage::getModel('catalog/product' [, $args]); // instsance of the model
$model = Mage::getSingleton('catalog/product', [, $args]); // signleton instance of the model
RESOURCE MODEL DECLARATION
Mage_Customer
- Customer
- Model
- Entitty
- ....
Mage_Catalog
- Catalog
- Model
- Resource
- Eav
- Mysql4
...
XML MODEL DECLARATION:
Mage_Customer
<global>
<models>
<customer>
...
</customer>
<customer_entity>
</customer_entity>
</models>
</global>
Mage_Catalog
<global>
<models>
<catalog>
...
</catalog>
<customer_entity>
</customer_entity>
</models>
</global>
USING RESOURCE MODEL
$resourceModel = Mage::getResourceModel('catalog/product', [, $args]);
<global>
<models>
<catalog>
<class>Mage_Catalog_Model</class>
</catalog>
<catalog_resource_eav_mysql4>
<class>Mage_Catalog_Model_Resource_Eav_Mysql4</class> <!-- resource model -->
<entitities>..</entities>
</catalog_resource_eav_mysql4
. ....
ENTITIES DECLARATION
To enable entity (aka tables) models in your module, declare them within resource model declaration in module's config.xml file.
<global>
<models>
<catalog>
<class>Mage_Catalog_Model</class>
<resourceModel>catalog_resource</resourceModel>
</catalog>
<catalog_resource>
<class>Mage_Catalog_Model_Resource</class>
<deprecatedNode>catalog_resource_eav_mysql4</deprecatedNode>
<entities>
<product>
<table>catalog_product_entity</table>
</product>
<category>
<table>catalog_category_entity</table>
</category>
<category_product>
<table>catalog_category_product</table>
</category_product>
<category_product_index>
<table>catalog_category_product_index</table>
</category_product_index>
<compare_item>
<table>catalog_compare_item</table>
</compare_item>
.....
// catalog product entity
$tableName = Mage::getResourceModel('catalog/product')->getTable('catalog/product');
RESOURCE COLLECTION MODEL DECLARATION
There is no specific definition in the Configuration XML that is necessary for enabling resource collections.
<global>
<models>
<catalog>
<class>Mage_Catalog_Model</class>
<resourceModel>catalog_resource_eav_mysql4</resourceModel>
</catalog>
<models>
</global>
COLLECTIONS
- ensures that each object gets loaded only once
getElement[yellow] --> IDENTITY MAP [empty] --> find()
load() --> IDENTITY MAP [empty] (...*...) --> return(*)
RELATIONSHIP
Model
- Resource Model -> Resource Collection Model 1
- ... -> ...
- Resource Model X -> Resource Collection Model X
MODEL-TO-RESOURCE
On Video 23:33 should be Mage_Catalog_Model_Abstract not Mage_Core_Model_Abstract
class Mage_Catalog_Model_Product extends Mage_Catalog_Model_Abstract
{
protected function _construct()
{
$this->_init('catalog/product');
}
}
which depends's on abstract class:
abstract class Mage_Catalog_Model_Abstract extends Mage_Core_Model_Abstract
{
}
depends 'Mage_Core_Model_Abstract':
abstract class Mage_Core_Model_Abstract extends Varien_Object
{
protected function _init($resourceModel)
{
$this->_setResourceModel($resourceModel);
}
protected function _setResourceModel($resourceName, $resourceCollectionName=null)
{
$this->_resourceName = $resourceName;
if (is_null($resourceCollectionName)) {
$resourceCollectionName = $resourceName.'_collection';
}
$this->_resourceCollectionName = $resourceCollectionName;
}
}
MODEL-TO-COLLECTION
Mage::getModel('catalog/product')->getCollection();
class Mage_Catalog_Model_Product extends Mage_Catalog_Model_Abstract
{
protected function _construct()
{
$this->_init('catalog/product');
}
}
abstract class Mage_Core_Model_Abstract extends Varien_Object
{
public function getResourceCollection()
{
$collection = parent::getResourceCollection()
->setStoreId($this->getStoreId());
return $collection;
}
}
public function getCollection() {
return Mage::getResourceModel(); // or $this->getResourceModel();
}
INSTANTIATION. ALL CASES.
// Mage_Catalog_Model_Product
$model = Mage::getModel('catalog/product');
$model = Mage::getSignleton('catalog/product');
// Mage_Catalog_Model_Resource_Product
$resource = Mage::getResourceModel('catalog/product');
$resource = Mage::getResourceSingleton('catalog/product');
$resource = Mage::getModel('catalog/resource_eav_mysql4_product');
$resource = $model->getResource();
// Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection
$resource = Mage::getResourceModel('catalog/product_collection');
$resource = Mage::getResourceSingleton('catalog/product_collection');
$resource = Mage::getModel('catalog/resource_eav_mysql4_product_collection');
$resource = $model->getResource();
RELATIONSHIP
Collection -> Model <-> Resource Model
Collection -> Model - instantiate items using model's class name
Model <-> Resource Model - C.R.U.D. operations
Collection -> Resource Model - connection adapters and primary ID fieldname
EXERCISE 1: CATEGORIES
Part 1. Echo the list of all stores (store groups) and associated root catgories
- get list of all stores (Mage_Core_Model_Mysql4_Store_Collection)
- get root category $store->getRootCategoryId();
Part 2. Create a tree of all categories
- fetch all categories that doesn't have parents (Category collection)
- load children by getChildren() method of category object
- load them by yourself using catalog_category_entity table
Create 'ModelController.php' on '/local/Day/Two/controllers'.
<?php
class Day_Two_ModelsController extends Mage_Core_Controller_Front_Action
{
public function storesAction()
{
$stores = Mage::getResourceModel('core/store_collection');
foreach ($stores as $store)
{
echo '<h2>'.$store->getName().' - '.$store->getCode().'</h2>';
}
}
}
Look in database table 'core_store':
You see: store_id | code | website_id | group_id | name | sort_order | is_active
code becomes: getCode() etc....
Go to browser: /heyo/models/stores
Result:
English default
French french
German german
Get root category:
public function storescategoryAction()
{
$stores = Mage::getResourceModel('core/store_collection');
foreach ($stores as $store)
{
echo '$store array: '.get_class($store).'</p>'; // Mage_Core_Model_Store
$category = Mage::getModel('catalog/category')->load($store->getRootCategoryId());
echo '<p>Name of category: '.$category->getName().'<p>' ;
}
}
Result:
$store array: Mage_Core_Model_Store
Name of category: Root Catalog
$store array: Mage_Core_Model_Store
Name of category: Root Catalog
$store array: Mage_Core_Model_Store
Name of category: Root Catalog
Look up at catalog_category_entity table. You see 'entity_id', 'level'... 'level' describes on which root element is depends on entity_id.
public function categoriesAction()
{
// catalog_category_entity table
$categories = Mage::getResourceModel('catalog/category_collection')
->addFieldToFilter('level', 2);
foreach ($categories as $category)
{
echo '<p>'.$category->getId().' - '.$category->getName().'</p>';
}
}
You will not get a result from '$category->getName()' because a name of category in another entity table.
Result:
10 -
13 -
18 -
20 -
Now add '->addAttributeToSelect('name')' and it will show category name:
$categories = Mage::getResourceModel('catalog/category_collection')
->addFieldToFilter('level', 2) // select specific column from table
->addAttributeToSelect('name'); // select specify column data for result
Result:
10 - Furniture
13 - Electronics
18 - Apparel
20 - Household Items
BASIC OPERATIONS USING MODEL:
public function saveAction() {
$model = Mage::getModel('catalog/category');
$model->setName('Moon');
$model->setDescription('Moon');
$model->setIsAcive(1);
$model->save();
}
public function deleteAction() {
$model = Mage::getModel('catalog/category');
$model->load(3);
$model->delete();
}
VARIEN OBJECT
Varien_Object - basic class for most Magento classes
Protected array $_data declares magic method __call which process any set*, get* calls.
Example:
$obj = new Varien_Object();
$obj->setSomeName($name);
This code is transformed as:
$obj->__call('setSomeName', array($name));
$obj->setData('some_name', $name);
$this->_data['some_data'] = $name;
Same as about:
$obj->getSomeName();
DATABASE/DATA OPERATION
Models -> Resource Models -> Varien Data -> Zend -> MySQL database
LOAD/SAVE STRUCTURE DIAGRAMS
MAIN METHODS (Mage_Core_Mdodel_Abstract)
save()
Location: /app/core/code/Mage/Core/Model.php
/**
* Save object data
*
* @return Mage_Core_Model_Abstract
*/
public function save()
{
/**
* Direct deleted items to delete method
*/
if ($this->isDeleted()) {
return $this->delete();
}
if (!$this->_hasModelChanged()) {
return $this;
}
$this->_getResource()->beginTransaction();
$dataCommited = false;
try {
$this->_beforeSave();
if ($this->_dataSaveAllowed) {
$this->_getResource()->save($this);
$this->_afterSave();
}
$this->_getResource()->addCommitCallback(array($this, 'afterCommitCallback'))
->commit();
$this->_hasDataChanges = false;
$dataCommited = true;
} catch (Exception $e) {
$this->_getResource()->rollBack();
$this->_hasDataChanges = true;
throw $e;
}
if ($dataCommited) {
$this->_afterSaveCommit();
}
return $this;
}
RESOURCE MODEL LAYER IN MAGENTO
- performs loading/saving/deleting of data
- data is corresponds to a model and implements necessary additional logic that allows to manipulate data for the model
- resource models are responsible for data manipulation only
- must not implement business logic inside resource model
RESOURCE MODEL
-> Load data
-> Delete data
-> Save data
ADAPTER
- install/upgrade script
$this->run('...sql...');
- resource model
$this->getReadAdapter/getWriteAdapter('...sql...');
- public method
$resource->getReadConnection();
- any place
Mage::getSingleton('core/resource')->getConnection('default_read');
Mage_Core_Model_Mysql4_Abstract::load()
Resource Model: load()
System > Mage_Core_Model_Abstract -> _beforeLoad() / load(modelId) / setData(mixed) / _afterLoad() -> Mage_Core_Model_Mysql4_Abstract -> _getLoadSelect() / query(select) / _afterLoad(model) / -> Adapter
Resource Model: save()
MAGE_CORE_MODEL_MYSQL4_ABSTRACT
_afterLoad(Mage_Core_Model_Abstract $object)
_beforeSave(Mage_Core_Model_Abstract $object)
_afterSave(Mage_Core_Model_Abstract $object)
_beforeDelete(Mage_Core_Model_Abstract $object)
_afterDelete(Mage_Core_Model_Abstract $object)
RESOURCE COLLECTION MODEL
Varien_Data_Collection --->
+getItemsByColumnValues()
+addItem()
+walk()
---> Countable (IteratorAggregate)
---> VarienData_Collection_Db
+isLoaded()
+load()
#_loadCache()
#_saveCache()
---> Mage_Core_Model_Mysql4_Collection_Abstract
+addFieldToSelect()
+setModel()
+setResourceModel()
+save()
EXAMPLE:
class Mage_Cms_Model_Mysql4_Page_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
/* Specify model class */
protected function _construct()
{
$this->_init('cms_page');
...
}
}
COLLECTION LOAD FLOW
NOTICE!
- method load() executes SQL and loads data only once (first time called)
- subsequents calls of load() won't affect the collection/data being loaded
- it keeps actual database access to the necessary minimum
- good example of LAZY LOADING pattern
Init > definitions of filters/columns > is collection loaded? -> yes -> return elements set -> reset() - clean from loaded elements / iterate collections
Init > definitions of filters/columns > is collection loaded? -> no -> prepare filters -> set restrictions -> load array from resource -> count paging -> generate pages with objects -> reset() - clean from loaded elements / iterate collections
VARIEN_DATA_COLLECTION_DB
- most useful method: addFieldToFilter()
- adds conditional expressions to collection's select object
$stores = Mage::getModel('core/store')->getCollection();
$stores->addFieldToFilter('code', array('like'=>'uk'));
echo count($stores);
First parameter: field name
Secound parameter: value for filter
COMPARISON OPERATORS
array("eq"=>'n2610')
WHERE (e.sku = 'n2610')
array("neq"=>'n2610')
WHERE (e.sku != 'n2610')
array("like"=>'n2610')
WHERE (e.sku like 'n2610')
array("nlike"=>'n2610')
WHERE (e.sku not like 'n2610')
array("is"=>'n2610')
WHERE (e.sku is 'n2610')
array("in"=>array('n2610'))
WHERE (e.sku in ('n2610'))
array("nin"=>array('n2610'))
WHERE (e.sku not in ('n2610'))
array("notnull"=>true)
WHERE (e.sku is NOT NULL)
array("null"=>true)
WHERE (e.sku is NULL)
array("gt"=>'n2610')
WHERE (e.sku > 'n2610')
array("lt"=>'n2610')
WHERE (e.sku < 'n2610')
array("gteq"=>'n2610')
WHERE (e.sku >= 'n2610')
array("moreq"=>'n2610') //a weird, second way to do greater than equal
WHERE (e.sku >= 'n2610')
array("lteq"=>'n2610')
WHERE (e.sku <= 'n2610')
array("finset"=>array('n2610'))
WHERE (find_in_set('n2610',e.sku))
array('from'=>'10','to'=>'20')
WHERE e.sku >= '10' and e.sku <= '20'
VARIEN_DATA_COLLECTION_DB
addBindParam($name, $value)
addFieldToFilter($field, $conditional=null)
addOrder($field, $direction = self::SORT_ORDER_DESC)
distinct($flag)
getConnection()
getItems()
getIdFieldName()
getSelect()
getSelectCountSql()
getSize()
initCache($object, $idPrefix, $tags)
loadData($printQuery = false, $logQuery = false)
load($printQuery = false, $logQuery = false)
printLogQuery($printQuery = false, $logQuery = false, $sql = null)
resetData()
setConnection($conn)
setOrder($field, $direction = self::SORT_ORDER_DESC)
unshiftOrder($field, $direction = self::SORT_ORDER_DESC)
RECORD SETS
public function addStoreFilter($store, $withAdmin = true)
{
if ($store instanceof Mage_Core_Model_Store) {
$store = array($store->getId());
}
$this->addFilter('store', array('in' => ('in' => ($withAdmin ? array(0, $store)), 'public');
return $this;
}
COLLECTION SAVE
foreach ($this->_items as $item) {
$item->save();
}
TYPES OF STORING SESSION
- default php session
- database
- eaccelerator
- memcached
SESSION NAMESPACES:
- core
- checking
- customer
- admin
EXAMPLE:
app/etc/modules/Training_Animal.xml
<config>
<modules>
<Training_Animal /> <!-- Training: etc/config --> <!-- Animal: app/code/[codePool] -->
<active />
<codePool />
</modules>
</config>
// app/code/local/Training/Animal/Model/Animal.php
class Training_Animal_Model_Animal extends Mage_Core_Model_Abstract
{
protected function _construct()
{
$this->_int('training/animal');
// passed to _setResourceModel, setup a collection by appending collection
}
}
// app/code/local/Training/Animal/Model/Mysql/Animal.php
class Training_Animal_Model_Mysql4_Animal extends Mage_Core_Model_Mysql4_Abstract
{
protected function _construct()
{
$this->_init('Training', 'entity_id');
}
}
Steps for checking config files:
- enable developer mode, cache
- check module config (if not listed: check registration files)
- check paths classname & config-specified class prefixes <class/> nodes
SECTION 5 - DATABASE IN MAGENTO - INSTALL UPGRADE-SCRIPTS, SETUP RESOURCES
==========================================================================
- install/upgrade workflow
- rollback/uninstall scripts
OVERVIEW
Magenton install scripts > upgrade / install
UPGRADE SCRIPTS
- once Magento runs an installer script for a module, it will never run another installer for that module again
- instead you need to create an upgrade script
Pool
- Block
- controllers
- etc
- Helper
- Model
- sql
- poll_setup <!-- pool setup -->
- mysql4-install-0.7.0.php
- mysql4-upgrade-0.5.1.php
- mysql4-upgrade-0.4.2.php
CONFIGURATION
<resources>
<checkout_setup>
<module>Mage_Checkout</module>
<class>Mage_Checkout_Model_Mysql</class>
</checkout_setup>
</resources>
Directory:
+sql
+checkout_setup
+mysql4-install-x.x.x.php
+mysql4-upgrade-x.x.x.php
+mysql4-upgrade-x.x.x.php
static public function applyAllUpdates() {
$resources = Mage::getConfig()
->getNode('global/resources') /* loaded from folder checkout_setup */
->children();
foreach ($resoures as $resName->$resource) {
...
}
}
INSTALL/UPGRADE WORKFLOW
Mage::run()
Mage_Core_Model_app::run()
Mage_Core_Resource_Setup::applyAllUpdates()
Check version of module from core_resource table. If it differs from current module version - execute upgrade
INSTALL SCRIPTS
Install/Upgrade scripts
$installer = $this;
/* executes multi_query database data and checks foreign keys */
$installer->startSetup()
$installer->endSetup()
INSTALL SCRIPTS
- 2 ways to upgrade database:
1) Plain SQL ($installer->run(....))
2) Using DDL operations from adapter
Example:
$installer = $this;
/* @var $installer Mage_Core_Model_Resource_Setup */
$installer->startSetup();
$installer->run(" -- DROP TABLE IF EXISTS .... CREATE TABLE .... ");
mysql4-install-0.7.2.php --> XML file with version <config><modules><Mage_Pool><version>0.7.2</version></Mage_Pool></modules></config>
DATA INSTALL
- application starts
- initializations of modules access and install/upgrade scripts are executed
- the rest of the application starts, current store is initialized
- execution of data installation scripts
core_resource_table:
code version data version
adminnotification_setup 1.0.0 1.0.0
admin_setup 0.7.2 0.7.2
Located in %your_module% / sql / $resource_name% / mysql4-data-install-0.7.0.php
ROLLBACK SCRIPTS AND UNINSTALL SCRIPTS
- rollback is an operation opposite to upgrade
- uninstall is opposite to install
public function applyUpdates()
{
$status = version_compare($configVer, $dbVer);
switch($status)
{
case self::VERSION_COMPARE_LOWER:
$this->_rollbackResourceDb($configVer, $dbVer);
break;
}
}
USING PLAIN SQL AND MAGENT METHODS IN INSTALL/UPGRADE SCRIPTS
- by extension Mage_Eav_Model_Entity_Setup - you get access to one important method:
Mage_Eav_Model_Entity_Setup::getDefaultEntities()
array(
'entity_model' => 'catalog/product',
'increment_model' => 'eav/entity_increment_numeric',
'attributes' => array(
'name' => array(
'type' => 'text',
'label' => 'Name',
'required' => true
)
)
);
EXERCISE:
Exercise 1: Create an install script and data upgrade script
Part 1. Create an install script for the module that creates a table
- declare setup in your config
- create install script, use $installer->run("..."); method for executing raw sql
- clear cache and hit any page
- verify that it works
Part 2. Create a data upgrade script which populates the table created in Part 1.
- create data upgrade script
- change data version in config
- clear cache and hit any page
Create XML configuration /etc/modules/Training_Animal.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Training_Animal>
<version>0.1.1</version>
</Training_Animal>
</modules>
<global>
<models>
<training>
<class>Training_Animal_Model</class>
<resourceModel>training_animal_resource</resourceModel>
</training>
<training_animal_resource>
<class>Training_Animal_Model_Mysql4</class>
<entities>
<animal>
<table>training_animal_entity</table>
</animal>
</entities>
</training_animal_resource>
</models>
<!-- setup -->
<resources>
<training_animal_setup>
<setup>
<module>Training_Animal</module>
<class>Mage_Core_Model_Resource_Setup</class>
</setup>
</training_animal_setup>
</resources>
</global>
</config>
Look at file /app/code/core/Mage/Eav/Model/Entity/Setup.php
Create folder /app/code/local/Training/sql/training_animal_setup with mysql4_install-0.1.0.php file:
$installer = $this;
$installer->startSetup();
$installer->run("
DROP TABLE IF EXISTS {$installer->getTable('training/animal')};
CREATE TABLE {$installer->getTable('training/animal')} (
entity_id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL DEFAULT,
type VARCHAR(255) NOT NULL DEFAULT,
edible TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
comment TEXT NULL,
updated_at DATETIME,
created_at DATETIME
) Engine=InnoDB DEFAULT CHARSET=utf8;
");
$installer->endSetup();
Under MySQL database you can manually delete a row from table 'core_resource' WHERE 'core_resource_code="training_animal_setup".'
Increase version in xml file:
<Training_Animal>
<version>0.2.1</version>
</Training_Animal>
After reloading it will execute SQL query due to newer version.
EXPLANATION:
- version number
- module not installed (record in core_resource)
then: install script with version # <= version
specified in config
config version = 0.2.1
script version <= 0.2.1
mysql4_install-0.1.0.php
Upgrade SQL data specified in '/local/Training/Animal/sql/training_animal_setup/mysql4-data-upgrade-0.2.4.php':
$installer = $this;
$installer->startSetup();
$installer->run("
ALTER TABLE IF EXISTS {$installer->getTable('training/animal')}
ADD training_data
TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 AFTER edible
");
$installer->endSetup();
After upgrade you can see on 'core_resource' version.
Increase version on XML:
<Training_Animal>
<version>0.2.5</version>
</Training_Animal>
Execute Magento and SQL will be process.
SECTION 6 - EAV MODEL - OVERVIEW
================================
TYPICAL DATA STORAGE SCHEMA
Field 1 | Field 2 | Field 3 | ...
....
Typically entities/products - reflected to plain table where each property has corresponding fields.
REGULAR DATA STORAGE PROBLEMS
- multi scope values
- adding/removing new fields
- changing type of the fields
VALUE OF USING EAV
- separates values from attributes and entities
- multiple scope values become possible
- adding/removing attributes is very easy
EAV STORAGE CONCEPT
tables:
- entity
- attribute
- values table 1
- values table n
...
TWO FACES OF EAV
Meta Information
- entity type
- attribute set and groups
Content
- entity records
- attribute values
EAV HIERARCHY
entity_type -> Entity -> Attribute Sets -> Backend/Source/Frontend model
Attribute Sets = Attributes = table (Attribute Groups)
entity_type -> attribute table
ACP -> Catalog -> Attributes > Manage Attribute Sets -> Edit Attribute Sets
Add attribute > 'somthing'.
EAV META TABLES
eav_entity_type <-- eav_attribute_set <-- eav_attribute_group <-- --> eav_entity_attribute --> eav_attribute <-- additional_attribute_table
See in database 'eav_entity_type' for an example. Look at tables and data.
EAV DATA STORAGE STRUCTURE
catalog_product_entity
- _int
- _datetime
- _text
- _varchar
- _decimal
ENTITY TYPE DESCRIPTION
- Entity_type_id
- Entity_type_code
- Default_attribute_set_id
- Increment_id
- Increment_model
ATTRIBUTE DESCRIPTION
- Attribute_code
- Backend_type
- Backend_model
- Source_model
- Frontend_model
EAV PROCESS
Entity:
- attribute (source + backend)
source ---> Source Model (during rendering)
backend --> Backend Model (CRUD operations)
EAV ACTORS
Core_Model_Resource_Abstract
- Entity_Abstract - Entity_Interface - Entity - Config - Entity_Type - Core_Model_Abstract
- Entity_Setup
Entity_Attribute_Interface
- Entity_Attribute_Abstract
- Entity_Attribute
Core_Model_Abstract
Entity_Attribute -> Entity_Attribute_Source_Abstract -> _Interface -> Frontend_Interface -> _Frontend_Abstract
EAV DATA ACCESS
- Databases <--> DataBase Adapter -> Resource Model + Resource Collection + Attribute Model --> Model + Entity Type
- Databases <--> DataBase Adapter -> Resource Model + Resource Collection + Attribute Model + Attribute Group Model + Attribute Set Model --> Model + Entity Type
- Databases <--> DataBase Adapter -> Resource Model + Resource Collection + Attribute Model + Attribute Group Model + Attribute Set Model + Attribute BackendModel + Attribute Source Model + Attribute Frontend Model --> Model + Entity Type
- Databases <--> DataBase Adapter -> Resource Model + Resource Collection + Attribute Model + Attribute Resource Model + Attribute Collection Model + Attribute Group Model + Attribute Group Collection Model + Attribute Group Resource Model + Attribute Set Model + Attribute Set Collection Model + Attribute Set Resource Model + Attribute BackendModel + Attribute Source Model + Attribute Frontend Model --> Model + Entity Type
Example:
ACP -> Catalog -> Add Product -> [Product Type]
See example in database 'eav_entity_attribute'. You can see relations with other entity in tables by using numbers (example: attribute_set_id).
http://www.youtube.com/watch?v=_H5GLP0BXyQ
SECTION 6 - EAV MODEL, LESSION 2 - EAV ENTITY, LOAD AND SAVE
============================================================
=======================
MAGENTO TUTORIAL:
=======================
== LOADING THEME ==
CUSTOM THEME:
app/design/frontend/custom_package/custom_theme
skin/frontend/custom_package/custom_theme
IF NOT [CUSTOM THEME], loading default:
app/design/frontend/custom_package/default
skin/frontend/custom_package/default
IF NOT [DEFAULT]:
app/design/frontend/base/default
skin/frontend/base/default
== MODULES LOADERS ==
app/etc/modules/*.xml
== EXTENSION COMUNITY MODULE ==
app / code / community / NAME_OF_MODULE / MODULE / BLOCK,CONTROLLER,DATA [[NAME_OF_MODULE]_[MODULE]_setup], ETC, HELPER, MODEL, SQL [[[NAME_OF_MODULE]_[MODULE]_setup]]
1. Magentostudy_News.xml
<?xml version="1.0"?>
<!--
/**
* Module initial config
*
* @author DEV
*/
-->
<config>
<modules>
<Magentostudy_News>
<active>true</active>
<codePool>community</codePool>
<depends>
<Mage_adminhtml />
</depends>
</Magentostudy_News>
</modules>
</config>
2. models, classes, helpers, database, tables,...
config.xml
<?xml version="1.0"?>
<!--
/**
* Module configuration
*
* @author Magento
*/
-->
<config>
<modules>
<Magentostudy_News>
<version>1.0.0.0.1</version>
</Magentostudy_News>
</modules>
<global>
<models>
<magentostudy_news>
<class>Magentostudy_News_Model</class>
<resourceModel>news_resource</resourceModel>
</magentostudy_news>
<news_resource>
<class>Magentostudy_News_Model_Resource</class>
<entities>
<news>
<table>magentostudy_news</table>
</news>
</entities>
</news_resource>
</models>
<helpers>
<magentostudy_news>
<class>Magentostudy_News_Helper</class>
</magentostudy_news>
</helpers>
<blocks>
<magentostudy_news>
<class>Magentostudy_News_Block</class>
</magentostudy_news>
</blocks>
<resources>
<magentostudy_news_setup>
<setup>
<module>Magentostudy_News</module>
<class>Mage_Core_Model_Resource_Setup</class>
</setup>
</magentostudy_news_setup>
</resources>
<events>
<before_news_item_display>
<observers>
<magentostudy_news>
<class>magentostudy_news/observer</class>
<method>beforeNewsDisplayed</method>
</magentostudy_news>
</observers>
</before_news_item_display>
</events>
</global>
<frontend>
<routers>
<magentostudy_news>
<use>standard</use>
<args>
<module>Magentostudy_News</module>
<frontName>news</frontName>
</args>
</magentostudy_news>
</routers>
<layout>
<updates>
<magentostudy_news>
<file>magentostudy_news.xml</file>
</magentostudy_news>
</updates>
</layout>
</frontend>
<Admin>
<routers>
<adminhtml>
<args>
<modules>
<Magentostudy_News
before="Mage_adminhtml">Magentostudy_News_adminhtml</Magentostudy_News>
</modules>
</args>
</adminhtml>
</routers>
</Admin>
<adminhtml>
<layout>
<updates>
<magentostudy_news>
<file>magentostudy_news.xml</file>
</magentostudy_news>
</updates>
</layout>
</adminhtml>
<default>
<news>
<view>
<enabled>1</enabled>
<items_per_page>20</items_per_page>
<days_difference>3</days_difference>
</view>
</news>
</default>
</config>
4. On admin page we can see module. If not enable System > Advanced > [see modules]
5. Prepare structure and layouts.
Frontend templates: frontend/base/default
Backend templates: adminhtml/default/default
Example of module: frontend/base/default/template/magentostudy
Example of layout: frontend/base/default/layout.magentostudy{{_news.xml}}
Frontend structure:
app / design / frontend / base / default / layout / magentostudy_news.xml
template / magentostudy / news
Admin panel structure:
app / design / adminhtml / default / default / layout / magentostudy_news.xml
template / magentostudy / news
JS:
Frontend:
Skin folder: skin/frontend/base/default/js/<namespace>_<name>
skin/frontend/base/default/js/magentostudy_news
Backend:
Skin folder: skin/adminhtml/default/default/js/<namespace>_<name>
skin/adminhtml/default/default/js/magentostudy_news
CSS:
Frontend:
Skin folder: skin/frontend/base/default/css
skin/frontend/base/default/js/magentostudy_news.css
Backend:
Skin folder: skin/adminhtml/default/default/css/<namespace>_<name>
skin/adminhtml/default/default/css/magentostudy_news.css
Images/media media/
External lib (open-source!) lib/
DATABASE:
sales_flat_order_item >> sales/order_item
config.xml setup XML:
<resources>
<magentostudy_news_setup>
<setup>
<module>Magentostudy_News</module>
<class>Mage_Core_Model_Resource_Setup</class>
</setup>
</magentostudy_news_setup>
</resources>
=======================
TIPS:
=======================
Protected classname start with: "_"
Directory structures:
_reportDir - /var/report/
GMT DATE: gmdate('Y-m-d H:i:s \G\M\T')
email match: /^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/
index.php:
Varien_Profiler::enable();
ini_set('display_errors', 1);
ACTION: $act = $this->getRequest()->getActionName();
===========================
MAGE CALLS:
===========================
Mage::app()->get...
Dev mode: Mage::setIsDeveloperMode(true);
Query rules: Mage::app()->getRequest()->getParam('type');
Singletons: Mage::getSingleton('api/server');
Log exception: Mage::logException($e); echo $e->getMessage(); exit;
destruct()
__() translate
uc_words($str, $destSep='_', $srcSep='_')
now()
is_empty_date($date)
mageFindClassFile($class)
mageCoreErrorHandler($errno, $errstr, $errfile, $errline)
mageDebugBacktrace($return=false, $html=true, $showFirst=false)
mageSendErrorHeader() .. mageSendErrorFooter()
mageDelTree()
mageParseCsv()
mageParseCsv($dir)
===========================
DEFINES:
===========================
DS
PS
BP
MAGENTO_ROOT
DEVELOPMENT_MODE
===========================
FILE STRUCTURES:
===========================
===========================
ERRORS:
===========================
/errors
process404
process503
processReport - email, render
getSkinUrl - skin uri
getHostUrl - HTTPS, server name check
getBaseUrl - replace \\ / uri
saveReport - serialire port, save to gen file
loadReport - return _setReportData unserialized report
sendReport - email send report using mail()
_setReportData - result
<config>
<skin>default</skin>
<report>
<!--
"action" can be set to "print" to show exception on screen and "email"
to send exception on specified email
-->
<action>email</action>
<subject>Store Debug Information</subject>
<email_address>admin@your-store.com</email_address>
<!--
"trash" is handle about trace info
value "leave" is for store on disk
value "delete" is for cleaning
-->
<trash>leave</trash>
</report>
</config>
==========================
MAGE DEV MODE:
==========================
$_SERVER['MAGE_IS_DEVELOPER_MODE']
SetEnv MAGE_IS_DEVELOPER_MODE "true"
.htaccess
SetEnv MAGE_RUN_CODE "base"
SetEnv MAGE_RUN_TYPE "store"
=======================
Codes:
=======================
$msg .= "Telephone: {$this->postData['telephone']}\n";
PS . BP . DS . 'downloader' . DS . 'lib';
=======================
MERCURIAL:
=======================
hg status # show all changes + .hgignores
cd project/...
hg commit
<changes>
hg commit
PULL:
hg pull http://path..
hg push ssh://...
hg sum
# TO SERVER #
hg iniit # creates .hg
hg add # add sources
hg commit # commit to server
hg commit -m "Name of change" # commit to server, after that recommend "hg log"
hg push # push to server
hg log # log of mercurial
# ---------- #
del something # remove
Back to latest commits:
hg revert -all
hg diff file1 file2
- #removed lines
+ #added
hg remove # remove file from repos
hg brench branchname
hg addremove - add files that not contains on server
hg rm delete
hg revert filetorevert
hg commit -m 'KDEV-74' project name
//
hg commit -m 'KDEV-74 TITLE - description'
hg push
hg push --new #pushing branch
hg up default - latest stable
si have underscore "_
PULL:
hg pull
hg update
hg up default #start to develop
hg branch ""kdev75" && commit -m 'KDEV-75 Title - description'
hg export -r1032 > ~/patch/kdev
CHECKING CHANGES
hg branch
hg update
hg update 'kdev-74'
---------------------
---------------------
CREATE A NEW PROJECT:
hg init my-repository
GET A LATEST CODE
hg pull
hg update # not update working dir
hg pull -u # pull/push
IGNORE FILES
hg add .hgignore
*.ext
SEE LISTS OF CHANGES
hg status
M/A/R/?-unknown, not tracked by hg/!-missing,tracked by hg but not found
SEE CHANGES
hg diff
COMMITS
hg commit -m "added readme" README
-A --addremove mark new/missing files as added/removed before committing
--close-branch mark a branch as closed, hiding it from the branch list
-I --include include names matching the given patterns
-X --exclude exclude names matching the given patterns
-m --message use <text> as commit message
-l --logfile read commit message from <file>
-d --date record datecode as commit date
-u --user record user as committer
COMPARE TWO REVISIONS OF A FILE
hg diff -r{rev1} -r{rev2} {file.code}
MERGE BRANCHES
cd repository-where-i-want-to merge
hg pull branch-i-want-to-merge
hg merge
LISTS OF CHANGESETS TO THE UPSTREAM DIR
hg outgoing
hg outgoing -p // show code changes
REMOVE FILE FROM REPOSITORY
hg remove {file(s)}
GET PREVIOUS VER OF CODE
hg update [-r REV]
HISTORY LOG
hg log {file(s)}
hg log -r # reverse order
CREATE BRANCH
Though it should be noted that branch creates a "virtual" directory (i.e., the files stay the same, but hg treats them as if they were different inside the system), while clone creates an actual, complete copy. Strictly speaking, clone isn't branching.
hg branch my-branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment