Created
December 14, 2017 10:13
-
-
Save marinsagovac/e4c5f215b1d1323e01a1a7abefafe5bf to your computer and use it in GitHub Desktop.
Magento - 2014 - tutorials
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
======================= | |
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