Magento入門開發教程


Modules->模塊

Controller->控制器

Model->模型

Magento是這個星球上最強大的購物車網店平台。當然,你應該已經對此毫無疑問了。不過,你可能還不知道,Magento同樣是一個面向對象的PHP框架。你可以配合Magento購物車程序強大的功能,開發動態WEB應用程序。

這是Magento中文開發手冊的開篇,我們會在整個手冊中介紹絕大部分Magento的開發框架特性。不要想在這片文章中立刻掌握所有的特性。這僅僅是個開始,但是足夠讓你在同行中鶴立雞群了。

在這片文章中,你將了解到:

  • Magento模塊(Magento Modules)代碼組織形式
  • 配置型MVC架構
  • Magento控制器(Magento Controllers)
  • 基於URI的模型實例化(Context-based URI Model Loading)
  • Magento模型(Magento Models)
  • Magento助手(Magento Helpers)
  • Magento布局(Magento Layouts)
  • 事件監聽(Observers)
  • Magento類重寫(Class Overrides)
  • 總結

開始之前,你可以試着看下Magento MVC模式的一個圖形化直觀體現。Magento_MVC.pdf

Magento模塊中的代碼組織形式

Magento通過將代碼放入獨立的模塊進行組織。在一個典型的PHP MVC應用中,所有的控制器會被放在一個文件夾中,所有的模型會被放在另外一個文件夾里,等等。而在Magento中,文件是基於功能進行分組的,這種分組后的代碼塊叫做模塊。

Magento的代碼:

舉例來說,如果你想尋找Magento中關於付款的功能,你僅僅需要找到下面代碼中的文件夾,就能獲取所有的控制器,模型,助手,Blocks等。

app/code/core/Mage/Checkout

如果你想尋找Magento中關於Google Checkout的功能,也僅僅需要找到如下文件夾,即可獲取所有你想要的信息。

app/code/core/Mage/GoogleCheckout

你的代碼:

如果你想擴展Magento,千萬不要想當然的去修改core文件夾中的文件,也不要將你自己的控制器,模型,助手或者Blocks放在Core文件夾中。所有對於Magento的擴展,都將在local文件夾中進行。

app/code/local/<Package>/<Modulename>

Package(也可稱為命名空間,當然這不是PHP手冊中提到的命名空間)是唯一的命名,通過Package來標識你的公司,組織或個人。通過Package,世界范圍內的Magento社區在創建模塊擴展時,能夠使用他們自己的Package名稱,以避免與其他開發者有命名沖突。

創建一個新的模塊時,你需要告訴Magento新模塊的相關信息。可以通過添加一個XML文件在下面的目錄中。

app/etc/modules

在這個目錄中有兩類xml文件,第一種用來開啟獨立的模塊,以下列方式命名:

Packagename_Modulename.xml

第二種文件用來從一個Package中開啟多個模塊,以下列方式命名:

Packagename_All.xml

配置型MVC系統

Magento是一個配置型MVC(Configuration-based MVC)系統。另外一種MVC系統則是大部分PHP框架使用的,約定性MVC(convertion-based MVC)。

在約定型MVC系統中,如果你添加一個控制器,或者一個模型,只需要根據約定的內容,創建這個文件以及類即可,系統會自動識別它。

而在配置型MVC系統中,比如Magento,除了需要添加相應的文件及類之外,還需要明確的告訴系統該類的存在。在Magento中,每個模塊都有一個config.xml文件。這個文件中包含了一個模塊相關的配置信息。在運行時,所有模塊的配置文件,都會被加載到一個巨大的配置文件樹中(后面的文章會介紹如何查看這個配置樹)。

比如,想在模塊中使用模型。你需要添加類似下面的代碼,來告訴Magento你會在這個模塊中使用這個模型。

<models>

<packagename>

<class>Packagename_Modulename_Model</class>

</packagename>

<models>

當然,這種配置不僅限於模型,對於控制器,助手,Blocks,路由,事件句柄等都需要在該模塊的config.xml中進行相關的配置。

Magento控制器(Magento Controllers)

在任何PHP系統當中,核心文件肯定是PHP文件。Magento也不例外,index.php是Magento的核心文件。

不過,永遠不要編輯index.php中的任何代碼。在MVC系統中,index.php的左右大概有以下幾項:

  • 檢測URL地址。
  • 根據路由規則,將訪問的URL地址分發到控制器類中的方法。
  • 初始化控制器,並調用相應的動作方法。這一步驟叫做分發。Dispatching。

這意味着Magento(或任何MVC系統)每一個有效的entry point都是控制器文件中的一個方法。一起來看下面這個URL:

http://example.com/catalog/category/view/id/25

上述域名后URL地址可以被分拆為以下幾個部分。

Front Name – catalog

該URL的第一部分被稱為Front Name。它用來指示Magento應該在哪個模塊中尋找URL中的控制器。在這個例子中,catalog就是Front Name,對應於catalog模塊。

Controller Name – Category

第二部分指示Magento應該匹配的控制器。每個擁有控制器的模塊都包含一個‘controllers’的文件夾,用來存放該模塊下的所有控制器。上述URL地址,匹配了下面這個控制器文件。

app/code/core/Mage/Catalog/controllers/CategoryController.php

其中的類定義格式大概為:

class Mage_Catalog_CategoryController extends Mage_Core_Controller_Front_Action

{

}

在Magento中,所有的控制器都繼承自Mage_Core_controller_Front_Action類。

Action Name – view

第三部分是一個action方法的名稱。在此URL中,view便是一個action方法的名字。

class Mage_Catalog_CategoryController extends Mage_Core_Controller_Front_Action {

public function viewAction() {

}

}

Paramater/Value – id/25

任何位於action方法名之后的路徑,都會被認為是key/value形式傳遞的GET變量。那么在我們的例子當中,’id/25′表示有一個值為25的$_GET['id']變量。

如前所述,如果你想讓自定義模塊使用控制器,你必須對它進行配置。下面是在模塊中開啟控制器的代碼。

<frontend>

<routers>

<catalog>

<use>standard</use>

<args>

<module>Mage_Catalog</module>

<frontName>catalog</frontName>

</args>

</catalog>

</routers>

</frontend>

現在不清楚上述內容都是什么意思還沒關系,但是注意<frontName>catalog</frontName>。這是用來關聯模塊與URL地址中frontname的。Magento核心代碼選擇將一個模塊的名字與frontname一致,但這不是強制規定的。

Multiple Routers

上面提到的路由規則主要是針對Magento購物車程序(即你所能看到的前端)。如果Magento在URL中無法匹配到正確的控制器/動作,它會嘗試使用針對Admin程序(后台管理端)的另一套路由規則。如果依舊無法正確匹配,它會使用一個特殊的控制器Mage_Cms_IndexController。

CMS控制器會檢查Magento內容管理系統中是否有內容需要輸出,如果有內容輸出,則讀取該內容,如果找不到,則輸出404頁面。

例如,Magento默認的首頁就是在使用CMS控制器。

Context-Based URI 模型讀取

目前為止,我們已經建立了一個控制器以及一個方法,到實例化一個類做點什么的時候了。Magento提供了一種特殊的方式去實例化模型,助手以及Blocks,即使用Mage全局類提供的靜態工廠方法。例如,

Mage::getModel('catalog/product');

Mage::helper('catalog/product');

‘catalog/product’字符串被稱為Grouped Class Name。通常叫做URI。Grouped Class Name的第一部分用來指示該類存在於哪個模塊當中。第二部分用來決定哪個類將被調用。

那么,上述例子中,‘catalog’對應於app/code/core/Mage/Catalog模塊,也就意味着我們的類名將以Mage_Catalog開頭,然后根據調用的類型,將product類名加入到最后一部分。即,

Mage::getModel('catalog/product');

Mage_Catalog_Model_Product;

Mage::helper('catalog/product');

Mage_Catalog_Helper_Product;

Magento 模型

和現在的多數框架一樣,Magento也提供ORM支持。ORM讓你能夠專注於數據,而非無盡的SQL語句。例如,

$model = Mage::getModel('catalog/product')->load(27);

$price = $model->getPrice();

$price += 5;

$model->setPrice($price)->setSku('SK1231414');

$model->save();

在上面這個例子中,我們調用了“getPrice”和“setPrice”方法。然而,在Mage_Catalog_Model_Product類中並沒有此方法。那為什么上面這個例子能夠使用這些方法呢?因為Magento的ORM系統中使用了PHP的_get和_set魔術方法。

調用$product->getPrice()會獲取模型屬性price,而調用$product->setPrice()會設置price屬性。當然,所有的這些都假設模型類沒有getPrice和setPrice方法。如果它們存在於模型類中,PHP魔術方法會被忽略。如果你有興趣知道這是如何實現的,可以參考Varien_Object類,所有的模型類都繼承自該類。

如果你想獲取模型當中所有的數據,可以直接調用$product->getData()方法,它會返回包含所有字段的一個數組。

你可能已經注意到上例中的方法存在使用->符號鏈接的形式:

$model->setPrice($price)->setSku(‘SK12312542′);

能夠使用這種方式調用方法,最主要的原因是所有的set方法都會返回一個模型的實例。 你會經常在Magento的核心代碼中看到此類調用方法的形式。

Magento的ORM系統中還包含一種通過Collections接口查詢多個對象的方式。下例會讀取系統中所有5美元的產品。

$product_collection = Mage::getModel('catalog/product')

->getCollection()

->addAttributeToSelect('*')

->addFieldToFilter('price','5.00');

這里我們又一次看到了鏈接調用方法的形式。Collections use the PHP Standard Library to implement Objects that have array like properties.(這句超出理解范圍)。

foreach($products_collection as $product)

{

echo $product->getName();

}

在上面的一個例子當中,你可能注意到了addAttributeToSelect方法。這里單獨提到此方法,是因為它代表了Magento模型中的一個類別。Magento擁有兩種形式的模型對象。一種是傳統的“一個對象,一張表”的Active Record模型。當你實例化這些模型的時候,所有的屬性都會被自動選取。

Magento中第二種模型叫做Entity Attribute Value(EAV)模型。這種模型會按照一定的規律將數據分散存儲在數據庫不同的表中。EAV模型的高級特性,讓Magento不用在增加一種產品屬性的時候改變數據庫模型(一般的購物車系統在增加新的屬性時,有兩種方式,一種是增加數據庫字段,一種是使用預留的空字段。),從而保證了Magento系統的高度擴展性。當創建一個EAV模型的collection時,Magento會conservative in它會查詢的字段數,所有你可以使用addAttributeToSelect來制定你想獲取的列,或者使用addAttributeToSelect(*)來獲取所有列。

Magento Helpers 助手

Magento的助手類包含一系列實用的方法,通過這些方法可以對對象及變量做日常性的操作。例如,

$helper = Mage::helper('catalog');

是否注意到這里舍棄了Grouped Class Name的第二部分?每個模塊都有一個默認的data助手類。下面的語句與上面的作用是相同的,即默認使用模塊下的data助手類。

$helper = Mage::helper('catalog/data');

大部分的助手類繼承自Mage_Core_Helper_Abstract,默認提供了很多使用的方法。

$translated_output = $helper->__('Magento is Great');

if ($helper->isModuleOutputEnabled()) {

}

Magento Layout 布局

目前為止,我們已經介紹了控制器,模型以及助手。在典型的PHP MVC系統當中,在操作模型之后,一般會

  • 傳遞變量到視圖中。
  • 系統會自動讀取默認的外層布局
  • 接着將視圖讀取到外層布局中

不過,如果你仔細觀察Magento 控制器動作方法,你不會看到這些步驟,

public function galleryAction() {

if (!$this->_initProduct()) {

if (isset($_GET['store']) && !$this->getResponse()->isRedirect()) {

$this->redirect('');

} elseif (!$this->getResponse()->isRedirect()) {

$this->_forward('noRoute');

}

return;

}

$this->loadLayout();

$this->renderLayout();

}

不同於典型PHP MVC形式的是,控制器動作方法,以兩個輸出布局的方法結束。所以說,Magento MVC系統中的V視圖部分可能與你經常使用的大相徑庭。因為,你必須在控制器中明確的輸出布局。

並且,Magento的布局本身也區別與你經常使用的MVC系統。Magento布局是一個包含嵌套或者樹狀的Block對象的對象。每一個Block對象輸出一部分HTML,輸出HTML的環節包含兩個部分,PHP代碼組成的Block以及.phtml模板文件。

Blocks對象負責與Magento系統交互並從模型中獲取數據,而phtml模板文件則為頁面生成必須的HTML代碼。

例如,頁面頭部Block文件app/code/core/Mage/Page/Block/Html/Head.php使用與其對應的page/html/head.phtml模板文件。

換種方式說的話,Blocks類就像迷你控制器,而.phtml文件就是視圖文件。

默認的,當你調用,

 

$this->loadLayout();

$this->renderLayout();

Magento will load up a Layout with a skeleton site structure(此段能夠理解,但想不到最佳翻譯,大概意思是Magento會讀取網站的布局框架)。這些結構Blocks用來輸出head,body以及設定單欄或多欄的布局。另外,還有一些內容Blocks負責實際輸出像導航,產品分類等。

“結構”和“內容”Blocks在布局系統中是隨意設置的。一般不會在代碼中刻意添加代碼,從而區分一個Block是結構還是內容,但是Blocks要么屬於“結構”,要么屬於“內容”。

為了添加一個內容Blocks到布局中,你需要告訴Magento系統

“Magento,快把這幾個Blocks添加到內容Block 里”

或者

“Magento,把這邊幾個Blocks放到“左邊欄”結構Block里”

這些可以通過控制器中的代碼進行控制,

public function indexAction() {

$block = $this->getLayout()->createBlock('adminhtml/system_account_edit');

$this->getLayout()->getBlock('content')->append($block);

}

但是更常用的方式(至少在前台購物車應用中)是使用基於XML文件的布局系統。

在一款風格中,基於XML文件的布局 允許你刪除正常輸出的Blocks或者添加默認的skeleton區域(即Structure Blocks)。例如下面這個XML布局文件,

<catalog_category_default>

<reference name="left">

<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml" />

</reference>

</catalog_category_default>

上面這段代碼的作用是,在catalog模塊的category控制器的默認動作方法中,將catalog/navigation Block插入到左邊欄結構Block中,並使用catalog/navigation/left.phtml模板文件。

關於Blocks還有一個比較重要的特性。在模板文件中,你會看到很多類似下面的代碼,

1

$this->getChildHtml('order_items')

這是Block輸出套嵌Block的方式。但是,只有在XML布局文件中明確聲明一個Block包含另一個子Block時,才能在模板文件中通過getChildHtml()方法調用子Block的模板文件。

例如,在XML布局文件中,

<catalog_category_default>

<reference name="left">

<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml">

<block type="core/template" name="foobar" template="foo/baz/bar.phtml" />

</block>

</reference>

</catalog_category_default>

那么從catalog/navigation Block中,我們才可以調用$this->getChildHtml(‘foobar’);

Observers 觀察者

和許多優秀的面向對象系統一樣,Magento通過實現觀察者模式給用戶作為鈎子。對於在頁面請求時(模型存儲,用戶登錄等)調用的特定動作方法,Magento會生成一個事件信號。

當創建新的模塊時,你可以“監聽”這些事件。比如說,你想在特定用戶登錄商店的時候發一封郵件到管理員信箱里,可以通過“監聽”customer_login事件做到。

<events>

<customer_login>

<observers>

<unique_name>

<type>singleton</type>

<class>mymodule/observer</class>

<method>iSpyWithMyLittleEye</method>

</unique_name>

</observers>

</customer_login>

</events>

接下來是當用戶登錄時應該運行的代碼:

class Packagename_Mymodule_Model_Observer

{

public function iSpyWithMyLittleEye($observer)

{

$data = $observer->getData();

//code to check observer data for out user,

//and take some action goes here

}

}

Class Overrides 類的復寫

最后,Magento系統還提供了類的復寫功能,你可以通過自己的代碼覆蓋核心代碼里的模型,助手,和Blocks類。

下面舉些例子幫助你更容易理解這個功能。產品的模型類是Mage_Catalog_Model_Product.無論何時,下面的代碼被調用時,就會生成一個Mage_Catalog_Model_Product對象。

1

$product = Mage::getModel('catalog/product');

這是工廠模式。Magento的類復寫系統運作模式大概是這樣,

“Magento! 如果有請求對應catalog/product,不要實例化Mage_Catalog_Model_Product,讓Packagename_Modulename_Model_Foobazproduct接手”

在模型中,類的覆蓋方式及命名規則如下,

class Packagename_Modulename_Model_Foobazproduct extends Mage_Catalog_Model_Product

{

}

通過這種方式,你可以改變父類中方法的行為,並且完全繼承父類的所有功能。

class Packagename_Modulename_Model_Foobazproduct extends Mage_Catalog_Model_Product

{

public function validate() {

//添加一些自定義的驗證功能

return $this;

}

和之前所說的一樣,復寫同樣需要在config.xml配置文件中進行配置。

<models>

<!-- tells the system this module has models -->

<modulename>

<class>Packagename_Modulename_Model</class>

</modulename>

<!-- does the override for catalog/product-->

<catalog>

<rewrite>

<product>Packagename_Modulename_Model_Foobazproduct</product>

</rewrite>

</catalog>

</models>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM