Глава 8. Создание модуля

[16 июля 2019 г.]    Российская сборка Magento 2.52.2
Magento 2: модули и услуги
magereport.com: составление перечня необходимых для установки заплаток SUPEE
  • Тема закрыта
#1 Надежда А
  • Иконка
  • Группа: Администратор
  • Сообщений: 50
  • Регистрация: 12.12.2010

29.03.2011 13:07

В этой главе мы рассмотрим:
  • Создание пустого модуля с Module Creator
  • Создание необходимых каталогов
  • Активация модуля
  • Создание контроллера для модуля
  • Создание конфигурации XML-файла для модуля
  • Создание помощника для модуля Новости
  • Создание моделей для модуля
  • Настройка SQL для модуля Новости
  • Проектирование шаблона для модуля Новости
  • Добавление необходимых блоков для модуля Новости


Введение

Модули – это строительные блоки Magento CMS. Каждое действие на сайте, в интерфейсе или серверной части, проходит через модуль. Модули выступают в качестве контейнеров для одного или более из следующих пунктов:
  • Настройки

  • Схемы баз данных

  • Объекты изображения (Rendering objects)

  • Помощники утилита

  • Модели данных

  • Контроллеры действия

Модуль может содержать все шесть пунктов, упомянутых, или только один (как и многие требуемые). Модули определяются как ON или OFF через XML файл конфигурации, расположенный в app/etc/modules/ каталоге. Каждый модуль может указать свои настройки в файле XML, а также, расположенные под каталогом etc/ модуля. Т.к. все в Magento является модулем, модули имеют автономную конфигурацию и настройки базы данных, это позволяет расширить Magento точно так, как построено ядро системы.

В этой главе мы создадим пользовательский модуль Новости (News) с пользовательской таблицей базы данных. Этот модуль включает в себя как внешние, так и внутренние связанные задачи. Давайте начнем!

В этой главе мы использовали Packt как пространство имен (Namespace)и новости (News), как имя модуля.


Создание пустого модуля с Module Creator

Прежде чем мы начнем, я хотел бы поблагодарить Даниэля Ница, который написал хорошие раширение под названием Module Creator для создания необходимых каталогов и файлов в нужном месте. В этот рецепте мы создадим пустой модуль с этим расширением.


Подготовка

У нас есть некоторая предварительная работа. Нам необходимо установить расширение Module creator через Magento Connect.
1. Войдите в ваш бэкэнд Magento.
2. Перейдите на страницу системы System | Magento Connect | Magento Connect Manager.
3. Войдите с помощью админ имени пользователя и пароля.\
4. Перейдите на вкладку settings и установите Preferred State как: beta. Сохраните эту настройку.
5. Перейдите обратно на вкладку extensions и введите ключ расширения для module creator в текстовом поле как: magento-community/Netz98_ModuleCreator.
6. Нажмите install.
7. После завершения процесса установки новый каталог будет создан в вашем корневом каталоге Magento, коим является moduleCreator.

moduleCreator расширение не будет установлено, если вы установили ваш Preferred State как stable. Убедитесь, что вы установили его как beta на вкладке settings.


Как это сделать…

Давайте предположим, что мы уже установили расширение module creator в нашей Magento установке. Если вы еще не установили его, пожалуйста, установите его из вышеупомянутых шагов.
1. Направьте ваш браузер на следующий URL:
http://magento.local.com/moduleCreator/

2. Введите необходимую информацию, когда вы увидите страницу что-то вроде этого:

Прикрепленное изображение: 1.png


Создание необходимых каталогов

Magento поддерживает строгие правила именования. Мы будем следовать им при создании новых каталогов и файлов для нашего модуля. Мы будем использовать локальный codePool, то есть app/code/local каталог.


Как это сделать…

1. Откройте Magento корневой каталог с вашим файловым путеводителем (file explorer). В моем случае это находится по следующему адресу:
/var/www/magento.local.com/public/
2. Теперь перейдите в местоположение app/code/local каталога. Если вы следовали нашему предыдущему рецепту, вы должны увидеть здесь папку с именем Packt. В этом случае вы можете пропустить этот рецепт. Если вы не следовали предыдущему рецепту, то вы должны выполнить следующие шаги.
3. Давайте теперь создадим каталоги для нашего модуля, как следующее дерево в app/code/local каталоге:

Прикрепленное изображение: 2.png

4. Мы закончили с созданием необходимых каталогов для локального раздела. Мы должны создать еще несколько каталогов для нашего текущего шаблона Magento.
5. Создайте новый каталог с именем News в вашем активном шаблонном каталоге темы Magento. Шаблонный каталог (template directory ) должен находиться в app/design/frontend/YOUR_INTERFACE/YOUR_THEME/ шаблоне. В моем случае полный путь активного шаблона выглядит следующим образом:
/var/www/magento.local.com/public/app/design/frontend/default/default/template/
6. Хорошо, мы создали все каталоги для нашего модуля.
7. Пойдем к следующему рецепту, который занимается активацией нашего модуля


Как это работает...

Это задача нашей операционной системы, в то время как мы создавали некоторые каталоги на разных местах. Имейте в виду, что мы создаем эти папки в app/code/local. Не делайте этого в локальном каталоге. app/code имеет три папки, как следует:
  • Core: Код Magento ядра находится здесь

  • Community: Этот документ содержит код, установленный через Magento Connect

  • Local: Этот документ содержит код пользовательского модуля



Включение модуля

Включение или отключение модуля в Magento обрабатывается XML файлом. В этом рецепте мы будем активировать наш News модуль, указывая его в XML файле по имени Packt_News.xml.


Как это сделать…

1. Создайте новый XML файл под названием INTERFACE_MODULE в каталоге pp/etc/modules. В моем случае это Packt_News.xml.
2. Если у вас уже есть этот файл, нет необходимости создавать новый. Просто замените существующий код на следующий фрагмент кода:
<?xml version="1.0"?>
<config>
<modules>
<Packt_News>
<active>true</active>
<codePool>local</codePool>
</Packt_News>
</modules>
</config>

3. Сохраните этот файл и выйдите.


Как это работает...

Magento сильно зависит от конфигураций XML-файла. Для нашего нового модуля мы должны были сказать Magento активировать News модуль из пакета Packt. И codepool для нашего модуля News расположен в каталоге app/code/local. Мы написали его в XML файле, т.к. Magento может его интерпретировать.


Создание контроллера для модуля

Контроллер играет жизненно важную роль в отображении URL для конкретной бизнес-логики. В Magento контроллер расширяет фронт-контроллер Magento, который в конечном счете связан с фронт-контроллером Zend Framework через Varien фронт-контроллером действия (Varien front action controller). В этом рецепте мы создадим некоторые контроллеры как для внешней, так и для внутренней деятельности.


Как это сделать...

1. Создайте новый файл с именем IndexController.php в app/code/local/Packt/News/controllers/ каталоге.
2. Вставьте следующий код в файл IndexController.php. Если он не пуст, то сделайте его пустым и вставьте следующий код:
<?php
class Packt_News_IndexController extends Mage_Core_Controller_
Front_Action
{
public function indexAction()
{
$resource = Mage::getSingleton('core/resource');
$read = $resource->getConnection('core_read');
$newsTable = $resource->getTableName('news');
$select = $read->select()
->from($newsTable, array('news_id', 'title', 'filename',
'content', 'status'))
->where('status', 1)
->order('created_time DESC');
$news = $read->fetchAll($select);
Mage::register('list', $news);
$this->loadLayout();
$this->renderLayout();
}
public function viewAction()
{
$news_id = $this->getRequest()->getParam('id');
if ($news_id != null && $news_id != '') {
$news = Mage::getModel('news/news')->load($news_id)-
>getData();
} else {
$news = null;
}
/**
If no param we load a the last created item
*/
if ($news == null) {
$resource = Mage::getSingleton('core/resource');
$read = $resource->getConnection('core_read');
$newsTable = $resource->getTableName('news');
$select = $read->select()
->from($newsTable, array('news_id', 'filename', 'title',
'content', 'status', 'update_time'))
->where('status', 1)
->order('created_time DESC');
$news = $read->fetchRow($select);
}
Mage::register('news', $news);
$this->loadLayout();
$this->renderLayout();
}
} // end of class

3. Сохраните этот файл. Нам необходимо создать другой контроллер для административной части нашего модуля News.
4. Давайте создадим другой контроллер внутри каталога app/code/local/Packt/News/controllers/Adminhtml/. Давайте назовем его NewsController.php.
5. В моем случае полный путь:
/var/www/magento.local.com/public/app/code/local/Packt/News/controllers/Adminhtml/
6. Наши News модуль состоит из двух разделов. Один для интерфейса, а другой для управления новостями в админ-панели. Этот шаг создаст контроллер для серверной части (бэкенда), который будет использоваться для CRUD (Create, Read, Update and Delete) новостей. Этот контроллер имеет несколько методов, которые начинаются с Action, который работает так, как следует из их названия. Взаимодействие с данными осуществляется с помощью модели News.
7. Содержание контроллера News разбирается далее для нашего лучшего понимания. Полный контроллер вместе с другими файлами поставляется в комплекте с этой книгой.
8. Следующий блок кода имеет метод с именем _initAction (), который вызывается перед вызовом любого действия контроллера. Мы сделали три задачи в _initAction ():
  • Загрузка макета

  • Настройка активного меню в бэкэнде

  • Добавление Breadcrumb текста

<?php
class Packt_News_Adminhtml_NewsController extends Mage_Adminhtml_
Controller_action
{
protected function _initAction()
{
$this->loadLayout()
->_setActiveMenu('news/items')
->_addBreadcrumb(Mage::helper('adminhtml')->__('Items
Manager'), Mage::helper('adminhtml')->__('Item Manager'));
return $this;
}

9. Этот раздел посвящен индексному действию News контроллера в админке. Когда пользователь нажимает на Manage Items из меню News в панели администратора, это действие, которое вызывается через фронт-контроллер:
public function indexAction()
{
$this->_initAction();
$this->_addContent($this->getLayout()-
>createBlock('news/adminhtml_news'));
$this->renderLayout();
}

10. Действие изменения вызывается, когда пользователь нажимает на ссылку edit в листинге новостей под разделом News item management. Иентификатор связанных новостей присваивается из ID параметра и передается к модели новостей для извлечения конкретной новости. Если данный ID новостей дает результат, он передает данные на новостную форму для заполнения формы с полученными данными. При подаче формы редактирования новостей данные передаются к действию сохранение с новостным ID для его обновления. Есть и другие задачи, которые были достигнуты в этом методе, такие как загрузка макета, настройка активного меню, добавление Breadcrumb текста и т.д.
public function editAction()
{
$id = $this->getRequest()->getParam('id');
$model = Mage::getModel('news/news')->load($id);
if ($model->getId() || $id == 0) {
$data = Mage::getSingleton('adminhtml/session')-
>getFormData(true);
if (!empty($data)) {
$model->setData($data);
}
Mage::register('news_data', $model);
$this->loadLayout();
$this->_setActiveMenu('news/items');
$this->_addBreadcrumb(Mage::helper('adminhtml')->__('Item
Manager'), Mage::helper('adminhtml')->__('News Item Manager'));
$this->_addBreadcrumb(Mage::helper('adminhtml')->__('Item
News'), Mage::helper('adminhtml')->__('Item News'));
$this->getLayout()->getBlock('head')-
>setCanLoadExtJs(true);
$this->_addContent($this->getLayout()->createBlock('news/
adminhtml_news_edit'))
->_addLeft($this->getLayout()->createBlock('news/
adminhtml_news_edit_tabs'));
$this->renderLayout();
} else {
Mage::getSingleton('adminhtml/session')->addError(Mage::he
lper('news')->__('Item does not exist'));
$this->_redirect('*/*/');
}
}

11. Когда пользователь нажимает на кнопку Add item, запрос передается на это действие, которое направлено на действие редактирования без какого-либо ID. Когда действие изменения вызывается без новостного ID, он рассматривает запрос как новую запись и загружает форму как пустую.
public function newAction()
{
$this->_forward('edit');
}

12. Действие новостной формы установлено на this метод для обработки представленных данных формы. Если данные формы действительны, она сохраняет данные в таблице новостей через News модель, либо она показывает соответствующие сообщения об ошибках. Это действие вызывается как для новых новостей или для редактирования существующих новостей. Существует поле для фото в этой форме. Мы установили разрешенные расширения как 'jpg', 'jpeg', 'gif', 'png'. Вы можете изменить ее по своему усмотрению. Мы установили пути загрузки файлов, как: $path = Mage::getBaseDir('media') . DS . 'news' . DS, который переводит его в media/news/.
public function saveAction()
{
if ($data = $this->getRequest()->getPost()) {
if (isset($_FILES['filename']['name']) && $_
FILES['filename']['name'] != '') {
try {
/* Starting upload */
$uploader = new Varien_File_Uploader('filename');
// Any extention would work
$uploader->setAllowedExtensions(array('jpg', 'jpeg',
'gif', 'png'));
$uploader->setAllowRenameFiles(false);
$uploader->setFilesDispersion(false);
// We set media as the upload dir
$path = Mage::getBaseDir('media') . DS . 'news' . DS;
$uploader->save($path, $_FILES['filename']['name']);
} catch (Exception $e) {
}
//this way the name is saved in DB
$data['filename'] = $_FILES['filename']['name'];
}
$model = Mage::getModel('news/news');
$model->setData($data)
->setId($this->getRequest()->getParam('id'));
try {
if ($model->getCreatedTime == NULL || $model-
>getUpdateTime() == NULL) {
$model->setCreatedTime(now())
->setUpdateTime(now());
} else {
$model->setUpdateTime(now());
}
$model->save();
Mage::getSingleton('adminhtml/session')->addSuccess(Mage::he
lper('news')->__('Item was successfully saved'));
Mage::getSingleton('adminhtml/session')->setFormData(false);
if ($this->getRequest()->getParam('back')) {
$this->_redirect('*/*/edit', array('id' => $model-
>getId()));
return;
}
$this->_redirect('*/*/');
return;
} catch (Exception $e) {
Mage::getSingleton('adminhtml/session')->addError($e-
>getMessage());
Mage::getSingleton('adminhtml/session')-
>setFormData($data);
$this->_redirect('*/*/edit', array('id' => $this-
>getRequest()->getParam('id')));
return;
}
}
Mage::getSingleton('adminhtml/session')->addError(Mage::help
er('news')->__('Unable to find item to save'));
$this->_redirect('*/*/');
}

13. Операция удаления на новостном контроллере имеет отношение к удалению новостей, указанных ID параметром. В этом действии мы схватили ID параметр и проверили, существуют ли новости с заданным ID. Если новости существуют, мы удалим его и перенаправим его на страницк списка с сообщением об успехе.
public function deleteAction()
{
if ($this->getRequest()->getParam('id') > 0) {
try {
$model = Mage::getModel('news/news');
$model->setId($this->getRequest()->getParam('id'))
->delete();
Mage::getSingleton('adminhtml/session')->addSuccess(Mage::
helper('adminhtml')->__('Item was successfully deleted'));
$this->_redirect('*/*/');
} catch (Exception $e) {
Mage::getSingleton('adminhtml/session')->addError($e-
>getMessage());
$this->_redirect('*/*/edit', array('id' => $this-
>getRequest()->getParam('id')));
}
}
$this->_redirect('*/*/');
}

14. В панели News management мы предоставили для выполнения некоторую пакетную обработку, такую как удаление массы и изменение статуса массы. Это действие вызывается, когда пользователь выбирает некоторые новости и отправляет форму с выбором Delete в выпадающего меню actions. В этом действии мы достали представленные идентификаторы новостей и передал его к модели новостей для удаления. Когда модель новостей сделана с даннымы идентификаторами новостей, мы показали соответствующие статусные сообщения в Magento админ-панели и перенаправили их на страницу индекса новостей.
public function massDeleteAction()
{
$newsIds = $this->getRequest()->getParam('news');
if (!is_array($newsIds)) {
Mage::getSingleton('adminhtml/session')-
>addError(Mage::helper('adminhtml')->__('Please select item(s)'));
} else {
try {
foreach ($newsIds as $newsId) {
$news = Mage::getModel('news/news')->load($newsId);
$news->delete();
}
Mage::getSingleton('adminhtml/session')->addSuccess(
Mage::helper('adminhtml')->__(
'Total of %d record(s) were successfully deleted',
count($newsIds)
)
);
} catch (Exception $e) {
Mage::getSingleton('adminhtml/session')->addError($e-
>getMessage());
}
}
$this->_redirect('*/*/index');
}

15. Существует еще одна партия действий в нашем новостном контроллере для замены существующего новостного статуса между "включено" и "выключено ". Это действие управляет данными формы, когда пользователь выбирает некоторые существующие новости в админ-панели и представляет их с измененным статусом. Это действие полезно, когда пользователь хочет изменить несколько новостей в одном запросе. В этом действии все новостные идентификаторы (news IDs) собираются от формы и повторяются над ней, чтобы применить соответствующий статус, как предусмотрено в панели администратора.
public function massStatusAction()
{
$newsIds = $this->getRequest()->getParam('news');
if (!is_array($newsIds)) {
Mage::getSingleton('adminhtml/session')->addError($this->__
('Please select item(s)'));
} else {
try {
foreach ($newsIds as $newsId) {
$news = Mage::getSingleton('news/news')
->load($newsId)
->setStatus($this->getRequest()-
>getParam('status'))
->setIsMassupdate(true)
->save();
}
$this->_getSession()->addSuccess(
$this->__('Total of %d record(s) were successfully
updated', count($newsIds))
);
} catch (Exception $e) {
$this->_getSession()->addError($e->getMessage());
}
}
$this->_redirect('*/*/index');
}

16. exportCsvAction работает так, как предполагает его название. Мы использовали CSV функцию разбора Magento для обработки процесса экспорта. Мы установили имя как news.csv, которое может быть установлено как вам нравится. Экспортированный news.csv файл будет загружен по представлению формы в админ-панели. Файл будет содержать все новости в формате CSV (значения, разделенные запятыми) с именами столбцов, как первая строка.
public function exportCsvAction()
{
$fileName = 'news.csv';
$content = $this->getLayout()->createBlock('news/adminhtml
->getCsv();
$this->_sendUploadResponse($fileName, $content);
}

17. Это действие аналогично предыдущему, в то время как на выходное форматирование будет в XML. В этой функции другая защищенная функция вызывается, чтобы послать некоторые XML связанные заголовкки.
public function exportXmlAction()
{
$fileName = 'news.xml';
$content = $this->getLayout()->createBlock('news/adminhtml_
news_grid')
->getXml();
$this->_sendUploadResponse($fileName, $content);
}

18. Эта функция вызывается в exportXmlAction, чтобы послать некоторые HTTP заголовки при экспорте новостной сетки как XML.
protected function _sendUploadResponse($fileName, $content,
$contentType='application/octet-stream')
{
$response = $this->getResponse();
$response->setHeader('HTTP/1.1 200 OK', '');
$response->setHeader('Pragma', 'public', true);
$response->setHeader('Cache-Control', 'must-revalidate, postcheck=
0, pre-check=0', true);
$response->setHeader('Content-Disposition', 'attachment;
filename=' . $fileName);
$response->setHeader('Last-Modified', date('r'));
$response->setHeader('Accept-Ranges', 'bytes');
$response->setHeader('Content-Length', strlen($content));
$response->setHeader('Content-type', $contentType);
$response->setBody($content);
$response->sendResponse();
die;
}
}

19. Это было все о новостном контроллере для области News management в Magento Admin. Файл поставляется в комплекте с другими вместе с этой книгой.


Как это работает...

Контроллер в Magento работает как любой другой контроллер приложения на основе Zend Framework. Контроллер работает за всем MVC шаблоном проектирования. Он манипулирует моделями, решает, какое изображение отобразить, основываясь на запросе пользователя и других факторах, проходит вдоль данных, в которых каждое изображение будет нуждаться, или передает контроль на другой контроллер полностью. Это всегда хорошая практика, чтобы держать контроллер так тонко, как это возможно.
Класс IndexController имеет два действия: индекс и изображение (вид). Индексное действие показывает страницу списка для новостей, в то время как изображение представляет новостные детали для конкретных новостей. Этот контроллер отвечает за функции интерфейса.
NewsController внутри папки Adminhtml связан с управлением новостей в панели администратора. Он имеет следующие действия:
  • Index

  • new

  • edit

  • delete

  • exportCsv

  • exportXml

  • massDelete

  • massStatus

  • save


Каждое действие в Zend Framework добавляется с Action, которое помогает в отображении правильного пути для этого действия в контроллере.


Создание конфигурации XML-файла для модуля

Файл config.xml – это сердце любого модуля в Magento CMS. Он играет жизненно важную роль для соединения всех компонентов модуля. В этом рецепте мы создадим файл конфигурации, которым является config.xml для нашего News модуля.


Как это сделать…

1. Создайте новый файл с именем config.xml в каталоге app/code/local/NAMESPACE/MODULE/etc. В моем случае это /var/www/magento.local.com/public/app/code/local/Packt/News/etc/config.xml.
2. Замените содержимое следующим:
<?xml version="1.0"?>
<config>
<modules>
<Packt_News>
<version>0.1.0</version>
</Packt_News>
</modules>
<frontend>
<routers>
<news>
<use>standard</use>
<args>
<module>Packt_News</module>
<frontName>news</frontName>
</args>
</news>
</routers>
<layout>
<updates>
<news>
<file>news.xml</file>
</news>
</updates>
</layout>
</frontend>
<admin>
<routers>
<news>
<use>admin</use>
<args>
<module>Packt_News</module>
<frontName>news</frontName>
</args>
</news>
</routers>
</admin>
<adminhtml>
<menu>
<news module="news">
<title>News</title>
<sort_order>71</sort_order>
<children>
<items module="news">
<title>Manage Items</title>
<sort_order>0</sort_order>
<action>news/adminhtml_news</action>
</items>
</children>
</news>
</menu>
<acl>
<resources>
<all>
<title>Allow Everything</title>
</all>
<admin>
<children>
<Packt_News>
<title>News Module</title>
<sort_order>10</sort_order>
</Packt_News>
</children>
</admin>
</resources>
</acl>
<layout>
<updates>
<news>
<file>news.xml</file>
</news>
</updates>
</layout>
</adminhtml>
<global>
<models>
<news>
<class>Packt_News_Model</class>
<resourceModel>news_mysql4</resourceModel>
</news>
<news_mysql4>
<class>Packt_News_Model_Mysql4</class>
<entities>
<news>
<table>news</table>
</news>
</entities>
</news_mysql4>
</models>
<resources>
<news_setup>
<setup>
<module>Packt_News</module>
</setup>
<connection>
<use>core_setup</use>
</connection>
</news_setup>
<news_write>
<connection>
<use>core_write</use>
</connection>
</news_write>
<news_read>
<connection>
<use>core_read</use>
</connection>
</news_read>
</resources>
<blocks>
<news>
<class>Packt_News_Block</class>
</news>
</blocks>
<helpers>
<news>
<class>Packt_News_Helper</class>
</news>
</helpers>
</global>
</config>

3. Сохраните и закройте.
4. Нет такого шага, как шаг 4!


Как это работает...

Этот файл содержит информацию различных компонентов нашего News модуля как для внешнего, так и для внутреннего интерфейса (frontend и backend). Если мы посмотрим на структуру этого файла config.xml, мы увидим там пять блоков:
1. modules: Содержит информацию о версии
2. frontend: Содержит информацию о маршрутизаторе и макете
3. admin: Это настройки маршрутизатора и аргументы для административной части
4. adminhtml: Это меню, acl, и макет для администратора
5. global: Содержит конфигурацию для модели, ресурсов, блоков и помощников

Убедитесь, что нет строки внутри глобальной global | models | news |class тега.


Создание помощника для модуля News

В приложении MVC модель управляет бизнес-логикой и возвращает данные в контроллер, и контроллер, в конечном счете, передает данные для показа. Если нужно применить довольно сложную логику, которую надо будет повторить или мы не хотим помещать ее в файл представления, то Helper – это компонент, который поможет. В большинстве случаев Helper помогает организации данных в презентабельной форме и имеет дело с логикой в ней, что возможно будет повторяться на месте. В этот рецепте мы создадим пустой Helper файл для нашего News модуля. Вот некоторые наиболее часто встречающиеся случаи использования для Helper в Magento:
  • Модели доступа

  • Выполнение сложной или повторяющейся логики отображения

  • Управление и форматирование данными модели

  • Сохранение данных между скриптами изображения



Как это сделать…

1. Создайте новый файл PHP по имени Data.php в каталоге app/code/local/NAMESPACE/MODULE/Helper/. В моем случае Data.php расположен в:
/var/www/magento.local.com/public/app/code/local/Packt/News/Helper/Data.php
2. Контент должен быть, как следует:
<?php
class Packt_News_Helper_Data extends Mage_Core_Helper_Abstract
{
}



Как это работает...

Этот файл является частью нашего модуля News. Мы могли бы добавить некоторые методы в него, если нам нравится. Мы сохранили его пустым, т.к. у нас не было никакой сложной логики для нашего News модуля. Но вам пришла в голову мысль, почему требуется Helper в Magento и что он делает.


Создание моделей для модуля

Модель – это вещь, которая управляет бизнес логикой приложения MVC. В Magento модель управляет извлечением, обновлением записей базы данных, диалогом с веб-сервисами и т.д. Модель является важной частью шаблона проектирования MVC. Модель Magento использует Zend_Db_Table_Abstract, который подключен через Varien объект при работе с базой данных. В этом рецепте мы создадим кучу моделей для работы с нашей пользовательской таблицей для модуля News.


Как это сделать…

1. Создайте новый файл с именем News.php в каталоге app/code/local/Packt/News/Model/.
2. Замените контент следующим кодом:
<?php
class Packt_News_Model_News extends Mage_Core_Model_Abstract
{
public function _construct()
{
parent::_construct();
$this->_init('news/news');
}
}

3. Давайте создадим другую модель для MySQL4 версии. Создайте новый файл с именем News.php в папке app/code/local/Packt/News/Model/Mysql4/. Замените содержимое следующим:
<?php
class Packt_News_Model_Mysql4_News extends Mage_Core_Model_Mysql4_
Abstract
{
public function _construct()
{
// Note that the news_id refers to the key field in your
database table.
$this->_init('news/news', 'news_id');
}
}

4. Создайте еще один файл класса модели с именем Collection.php в папке app/code/local/Packt/News/Mode/Mysql4/News/ и замените содержание следующим кодом:
<?php
class Packt_News_Model_Mysql4_News_Collection extends Mage_Core_
Model_Mysql4_Collection_Abstract
{
public function _construct()
{
parent::_construct();
$this->_init('news/news');
}
}

5. Давайте создадим последнюю модель для нашего модуля News. Создайте новый файл с именем Status.php в каталоге app/code/local/Packt/News/Model замените содержание (контент) следующим:
<?php
class Packt_News_Model_Status extends Varien_Object
{
const STATUS_ENABLED = 1;
const STATUS_DISABLED = 2;
static public function getOptionArray()
{
return array(
self::STATUS_ENABLED => Mage::helper('news')->__('Enabled'),
self::STATUS_DISABLED => Mage::helper('news')->__
('Disabled')
);
}
}

6. Сохраните и закройте Status.php.
7. На этот раз нет задачи слева для создания модели.


Как это работает...

Модель в Magento работает с базой данных через Varien_Object, которая относится к Zend_Db_Abstract. Мы просто расширили Mage_Core_Model_Abstract класс Varien, который связан с Zend_Db_Adapter. Мы использовали некоторые общие методы для наших нужд.
Самая первая модель, которую мы написали, - это News.php. В этом файле мы расширили Mage_Core_Model_Abstract класс, который в конечном счете связан с базой данных. Внутри конструктора мы вызвали его родительский конструктор и _init (), который на самом деле защищенная функция в Mage_Core_Model_Abstract классе. Эта инициализация позволит нам использовать методы, определенные в Mage_Core_Model_Abstract, такие как: load(), save(), delete(), среди других.
Файл Status.php является моделью, которая занимается обновлением статуса в Magento админке для модуля новостей. Эта модель распространяется на Varien_Object, который имеет некоторые общие функциональные возможности для обновления статуса записи базы данных в Magento админ-панели.
Модели внутри папки MySQL4 выполняют аналогичные задачи, как модели MySQL4 за ее пределами. Они распространяются на различные классы Magento для предоставления аналогичной функциональности в MySQL 4 версии.


Настройка SQL для модуля News

В этом рецепте мы создадим новый файл PHP, который будет создавать новую таблицу в базе данных во время установки Magento.


Как это сделать...

1. Создайте новый файл с именем mysql4-install-0.1.0.php внутри каталога app/code/local/Packt/News/sql/news_setup/.
2. Замените текущий контент следующим:
<?php
$installer = $this;
$installer->startSetup();
$installer->run("
-- DROP TABLE IF EXISTS {$this->getTable('news')};
CREATE TABLE {$this->getTable('news')} (
`news_id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL default '',
`filename` varchar(255) NOT NULL default '',
`content` text NOT NULL default '',
`status` smallint(6) NOT NULL default '0',
`created_time` datetime NULL,
`update_time` datetime NULL,
PRIMARY KEY (`news_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
$installer->endSetup();

3. Сохраните и закройте этот файл.


Как это работает...

Во время установки этот файл будет вызываться, т.к. мы определили его в файле конфигурации. Он удаляет любую существующую таблицу с именем news в нашей базе данных Magento и создает новую с помощью упомянутой ранее схемы.
Вам предлагается внести изменения в файл SQL в соответствии со своими потребностями.


Проектирование шаблона для модуля News

Мы уже установили необходимые бизнес-логики в наших предыдущих рецептах в этой главе. В этом рецепте мы будет представлять данные через уровень представления Magento, которым является View. Мы создадим несколько файлов в нашем текущем каталоге активной темы интерфейса. Давайте сделаем это сейчас!


Как это сделать…

1. Создайте новый файл с именем News.phtml в каталоге app/design/frontend/YOUR_INTERFACE/YOUR_THEME/template/news/. В моем случае полный путь – это /var/www/magento.local.com/public/app/design/frontend/default/default/template/news/news.phtml.
2. Замените существующее содержимое следующим кодом:
<h3><?php echo $this->__('Latest news from Packt') ?></h3>
<table>
<?php foreach($this->getNewsList() as $item): ?>
<tr>
<td><img src="<?php echo Mage::getBaseUrl('media') . 'news'
. DS . $item['filename'] ?>" alt="<?php echo $item['title']
?>" width="64" height="64" style="border: 1px solid
#d5d5d5;padding:3px;margin-right: 10px;" /></td>
<td>
<h5><?php echo $item['title'] ?></h5>
<p><?php echo $this->limitCharacter($item['content'], 180, "
<a href='/news/index/view/id/".$item['news_id']."'>read more</a>")
?></p>
</td>
</tr>
<?php endforeach; ?>
</table>

3. Создайте еще один файл с именем view.phtml в том же месте и замените существующий код следующим:
<?php $item = $this->getNews(); ?>
<h3><?php echo $item['title']; ?></h3>
<p style="font-size: small;color: #777">published on: <?php echo
date('M jS, Y', strtotime($item['update_time'])); ?> <a href="/
news">Back to all news</a></p>
<div>
<span style="float: left">
<img src="<?php echo Mage::getBaseUrl('media') . 'news'
. DS . $item['filename'] ?>" alt="<?php echo $item['title']
?>" width="128" height="128" style="border: 1px solid
#d5d5d5;padding:3px;margin-right: 10px;" />
</span>
<span>
<p><?php echo $item['content']; ?></p>
</span>
<p style="clear: both"></p>
</div>
<a href="/news">Back to all news</a>

4. Сохраните и закройте этот файл.
5. Мы должны создать файл макета для нашего шаблона. Создайте новый файл XML с именем News.xml в каталоге app/design/frontend/YOUR_INTERFACE/YOUR_THEME/layout/.
6. Замените существующий контент следующим:
<?xml version="1.0"?>
<layout version="0.1.0">
<default>
</default>
<news_index_index>
<reference name="content">
<block type="news/news" name="news" template="news/news.
phtml" />
</reference>
</news_index_index>
<news_index_view>
<reference name="content">
<block type="news/news" name="news" template="news/view.
phtml" />
</reference>
</news_index_view>
</layout>

7. Сохраните этот файл.


Как это работает...

Magento сохраняет свой файл изображения как *.phtml файл. Мы написали два файлы изображения (просмотра) для интерфейса – один для листинга новостей и другой для страницы подробностей новостей.


Добавление необходимых блоков для модуля News

Блоки - это важные части Magento, которая является определенной частью экрана. Полная страница разделена на несколько блоков, чтобы позволить детализацию в Magento CMS. В каждом модуле блоки являются неотъемлемой частью. В этом рецепте мы создадим несколько блоков, которые помогут представить данные для нашего News модуля.


Как это сделать…

1. Откройте PHP IDE и создайте новый PHP файл в app/code/local/Packt/News/Block/News.php.
2. Замените существующий код на следующий код:
<?php
class Packt_News_Block_News extends Mage_Core_Block_Template
{
public function _prepareLayout()
{
return parent::_prepareLayout();
}
public function getNews()
{
if (!$this->hasData('news')) {
$this->setData('news', Mage::registry('news'));
}
return $this->getData('news');
}
public function getNewsList()
{
if (!$this->hasData('list')) {
$this->setData('list', Mage::registry('list'));
}
return $this->getData('list');
}
function limitCharacter($string, $limit = 20, $suffix = ' . .
.')
{
$string = strip_tags($string);
if (strlen($string) < $limit) {
return $string;
}
for ($i = $limit; $i >= 0; $i--) {
$c = $string[$i];
if ($c == ' ' OR $c == "\n") {
return substr($string, 0, $i) . $suffix;
}
}
return substr($string, 0, $limit) . $suffix;
}
}

3. Нам нужен другой помощник для административной части. Создайте новый файл с именем News.php в каталоге app/code/local/Packt/News/Block/Adminhtml/ и замените содержание (контент) следующим кодом:
<?php
class Packt_News_Block_Adminhtml_News extends Mage_Adminhtml_
Block_Widget_Grid_Container
{
public function __construct()
{
$this->_controller = 'adminhtml_news';
$this->_blockGroup = 'news';
$this->_headerText = Mage::helper('news')->__('Item Manager');
$this->_addButtonLabel = Mage::helper('news')->__('Add Item');
parent::__construct();
}
}

4. Создайте еще один block файл с именем Edit.php в папке app/code/local/Packt/News/Block/Adminhtml/News/ и замените контент следующим:
<?php
class Packt_News_Block_Adminhtml_News_Edit extends Mage_Adminhtml_
Block_Widget_Form_Container
{
public function __construct()
{
parent::__construct();
$this->_objectId = 'id';
$this->_blockGroup = 'news';
$this->_controller = 'adminhtml_news';
$this->_updateButton('save', 'label', Mage::helper('news')->__
('Save Item'));
$this->_updateButton('delete', 'label',
Mage::helper('news')->__('Delete Item'));
$this->_addButton('saveandcontinue', array(
'label' => Mage::helper('adminhtml')->__('Save And Continue
Edit'),
'onclick' => 'saveAndContinueEdit()',
'class' => 'save',
), -100);
$this->_formScripts[] = "
function toggleEditor() {
if (tinyMCE.getInstanceById('news_content') == null) {
tinyMCE.execCommand('mceAddControl', false, 'news_
content');
} else {
tinyMCE.execCommand('mceRemoveControl', false, 'news_
content');
}
}
function saveAndContinueEdit(){
editForm.submit($('edit_form').action+'back/edit/');
}
";
}
public function getHeaderText()
{
if (Mage::registry('news_data') && Mage::registry('news_
data')->getId()) {
return Mage::helper('news')->__("Edit Item '%s'", $this->htm
lEscape(Mage::registry('news_data')->getTitle()));
} else {
return Mage::helper('news')->__('Add Item');
}
}
}
Next block is to create Grid.php in app/code/local/Packt/News/
Block/Adminhtml/News/Gr<?php
class Packt_News_Block_Adminhtml_News_Grid extends Mage_
Adminhtml_Block_Widget_Grid
{
public function __construct()
{
parent::__construct();
$this->setId('newsGrid');
$this->setDefaultSort('news_id');
$this->setDefaultDir('ASC');
$this->setSaveParametersInSession(true);
}
protected function _prepareCollection()
{
$collection = Mage::getModel('news/news')->getCollection();
$this->setCollection($collection);
return parent::_prepareCollection();
}
protected function _prepareColumns()
{
$this->addColumn('news_id', array(
'header' => Mage::helper('news')->__('ID'),
'align' => 'right',
'width' => '50px',
'index' => 'news_id',
));
$this->addColumn('title', array(
'header' => Mage::helper('news')->__('Title'),
'align' => 'left',
'index' => 'title',
));
$this->addColumn('status', array(
'header' => Mage::helper('news')->__('Status'),
'align' => 'left',
'width' => '80px',
'index' => 'status',
'type' => 'options',
'options' => array(
1 => 'Enabled',
2 => 'Disabled',
),
));
$this->addColumn('action',
array(
'header' => Mage::helper('news')->__('Action'),
'width' => '100',
'type' => 'action',
'getter' => 'getId',
'actions' => array(
array(
'caption' => Mage::helper('news')->__('Edit'),
'url' => array('base' => '*/*/edit'),
'field' => 'id'
)
),
'filter' => false,
'sortable' => false,
'index' => 'stores',
'is_system' => true,
));
$this->addExportType('*/*/exportCsv',
Mage::helper('news')->__('CSV'));
$this->addExportType('*/*/exportXml',
Mage::helper('news')->__('XML'));
return parent::_prepareColumns();
}
protected function _prepareMassaction()
{
$this->setMassactionIdField('news_id');
$this->getMassactionBlock()->setFormFieldName('news');
$this->getMassactionBlock()->addItem('delete', array(
'label' => Mage::helper('news')->__('Delete'),
'url' => $this->getUrl('*/*/massDelete'),
'confirm' => Mage::helper('news')->__('Are you sure?')
));
$statuses = Mage::getSingleton('news/status')-
>getOptionArray();
array_unshift($statuses, array('label' => '', 'value' =>
''));
$this->getMassactionBlock()->addItem('status', array(
'label' => Mage::helper('news')->__('Change status'),
'url' => $this->getUrl('*/*/massStatus', array('_current'
=> true)),
'additional' => array(
'visibility' => array(
'name' => 'status',
'type' => 'select',
'class' => 'required-entry',
'label' => Mage::helper('news')->__('Status'),
'values' => $statuses
)
)
));
return $this;
}
public function getRowUrl($row)
{
return $this->getUrl('*/*/edit', array('id' => $row-
>getId()));
}
} 

5. Создайте Tabs.php в папке app/code/local/Packt/News/Block/Adminhtml/News/Edit/. Вот контент:
<?php
class Packt_News_Block_Adminhtml_News_Edit_Tabs extends Mage_
Adminhtml_Block_Widget_Tabs
{
public function __construct()
{
parent::__construct();
$this->setId('news_tabs');
$this->setDestElementId('edit_form');
$this->setTitle(Mage::helper('news')->__('Item
Information'));
}
protected function _beforeToHtml()
{
$this->addTab('form_section', array(
'label' => Mage::helper('news')->__('Item Information'),
'title' => Mage::helper('news')->__('Item Information'),
'content' => $this->getLayout()->createBlock('news/
adminhtml_news_edit_tab_form')->toHtml(),
));
return parent::_beforeToHtml();
}
}

6. Пришло время создать другой помощник (helper) с именем form.php в папке app/code/local/Packt/News/Block/Adminhtml/News/Edit/. Замените контент следующим кодом:
<?php
class Packt_News_Block_Adminhtml_News_Edit_Form extends Mage_
Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
$form = new Varien_Data_Form(array(
'id' => 'edit_form',
'action' => $this->getUrl('*/*/save', array('id' =>
$this->getRequest()->getParam('id'))),
'method' => 'post',
'enctype' => 'multipart/form-data'
)
);
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
}

7. Мы пришли в последний блок, Form.php, в папке app/code/local/Packt/News/Block/Adminhtml/Edit/Tab. Замените контент следующим кодом:
<?php
class Packt_News_Block_Adminhtml_News_Edit_Tab_Form extends
Mage_Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
$form = new Varien_Data_Form();
$this->setForm($form);
$fieldset = $form->addFieldset('news_form', array('legend'
=> Mage::helper('news')->__('Item information')));
$fieldset->addField('title', 'text', array(
'label' => Mage::helper('news')->__('Title'),
'class' => 'required-entry',
'required' => true,
'name' => 'title',
));
$fieldset->addField('filename', 'file', array(
'label' => Mage::helper('news')->__('File'),
'required' => false,
'name' => 'filename',
));
$fieldset->addField('status', 'select', array(
'label' => Mage::helper('news')->__('Status'),
'name' => 'status',
'values' => array(
array(
'value' => 1,
'label' => Mage::helper('news')->__('Enabled'),
),
array(
'value' => 2,
'label' => Mage::helper('news')->__('Disabled'),
),
),
));
$fieldset->addField('content', 'editor', array(
'name' => 'content',
'label' => Mage::helper('news')->__('Content'),
'title' => Mage::helper('news')->__('Content'),
'style' => 'width:700px; height:500px;',
'wysiwyg' => false,
'required' => true,
));
if (Mage::getSingleton('adminhtml/session')->getNewsData())
{
$form->setValues(Mage::getSingleton('adminhtml/session')-
>getNewsData());
Mage::getSingleton('adminhtml/session')-
>setNewsData(null);
} elseif (Mage::registry('news_data')) {
$form->setValues(Mage::registry('news_data')->getData());
}
return parent::_prepareForm();
}
}

8. Мы завершили создание блоков!


Как это работает...

Блоки - важная часть Magento. В этом рецепте мы создали семь блоков для создания функционального модуля новостей. Мы написали некоторые методы в каждом блоке, чтобы передавать данные в макет. Первый блок, News.php, отвечает за интерфейс, в то время как остальные внутри Adminhtml предназначены для админ-панели. News.php имеет четыре метода:
  • _prepareLayout(): Мы только что назвали его родителей _prepareLayout (). Этот метод используется для изменения макета.

  • GetNews (): Эта функция используется для установки новостных данных в блог регистров (registry) и их извлечения (retrieve).

  • GetNewsList (): Эта функция такая же, как и предыдущий метод, но в этом случае она извлекает список новостей.

  • LimitCharacter (): Это метод утилита, который обрезает текст до заданной длины.

Magento бэк-офис (админ-панель) очень организован. Мы последовали их конвенциям, чтобы дать пользователю аналогичный UI (пользовательский интерфейс) при управлении новостями в админ-панели. Вот почему мы должны были создать шесть блоков для рендеринга (rendering) и обработки действий в админ-панели. Файл Packt/News/Block/Adminhtml/News.php имеет конфигурацию для указания файла-контроллера, групп, лейбла и текстов для заголовка, кнопки и т.д. Внутри папки Edit есть несколько блоков, которые имеют отношение с формой редактирования, вкладкой, и сетку для News админ-панели.
Больше нет шагов слева для создания нашего Packt News модуля! Давайте запустим его в браузере! Давайте посетим админ-панель в первую очередь. Войдите в ваш бэкэнд Magento. Вы должны увидеть новое меню под названием News, пойдите к нему! Я нашел его, как показано на следующем скриншоте (после добавления некоторых ненастоящих новостей (dummy news)):

Прикрепленное изображение: 3.png

Если вы указали ваш браузер на http://magento.local.com/news как в моем локальном окне, вы должны увидеть что-то вроде страницы на следующем скриншоте:

Прикрепленное изображение: 4.png

Вот еще один скриншот для админ-панели:

Прикрепленное изображение: 5.png

#2 guchiam
  • Группа: Пользователь
  • Сообщений: 1
  • Регистрация: 30.03.2011

30.03.2011 19:53

делаю все как написано, но почему-то во фронте не отображается совсем ничего
код для отображения есть, кэш почищена
что не так, подскажите плиз

#3 Дмитрий Федюк
  • Администратор
  • Иконка
  • Группа: Администратор
  • Сообщений: 8995
  • Регистрация: 20.02.2010

30.03.2011 23:11

Знакомы ли вы с опцией PHP display_errors?

#4 cyrus
  • Группа: Пользователь
  • Сообщений: 2
  • Регистрация: 18.09.2011

24.09.2011 00:19

Доброго времени суток :)
У меня выскакивает сообщение
"Fatal error: Class 'Mage_News_Helper_Data' not found in ...\app\Mage.php on line 519"
что это может быть? и как вылечить? заранее благодарю.

#5 Дмитрий Федюк
  • Администратор
  • Иконка
  • Группа: Администратор
  • Сообщений: 8995
  • Регистрация: 20.02.2010

24.09.2011 00:47

Откуда вы взяли модуль Mage_News?

#6 cyrus
  • Группа: Пользователь
  • Сообщений: 2
  • Регистрация: 18.09.2011

24.09.2011 08:12

я его нигде не брал, делал все по написанному в статье.

#7 Дмитрий Федюк
  • Администратор
  • Иконка
  • Группа: Администратор
  • Сообщений: 8995
  • Регистрация: 20.02.2010

24.09.2011 12:04

Вероятно, вы делали не всё, как описано в статье, потому что в статье модуль называется по-другому.

#8 Зафар
  • Группа: Пользователь
  • Сообщений: 7
  • Регистрация: 26.09.2011

26.09.2011 19:32

В файле index.php находящийся в DOCUMENT_ROOT, можете включить Developer Mode.

на 66ой строке
if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
    Mage::setIsDeveloperMode(true);
}



Поправив получим:
if (true || isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
    Mage::setIsDeveloperMode(true);
}


Перед запуском не забудьте вернуть изменения.

#9 Varan
  • Группа: Заблокирован
  • Сообщений: 3
  • Регистрация: 20.12.2011

22.12.2011 18:25

что то я все так сделал, в админке добавляю запись, а на сайте не отображается. В чем может быть проблема?

#10 Дмитрий Федюк
  • Администратор
  • Иконка
  • Группа: Администратор
  • Сообщений: 8995
  • Регистрация: 20.02.2010

22.12.2011 18:32

По каким критериям специалист должен проводить диагностику вашей ситуации?

#11 Varan
  • Группа: Заблокирован
  • Сообщений: 3
  • Регистрация: 20.12.2011

22.12.2011 18:44

включил Developer Mode, на странице www.мойсайт/news пишет
Warning: Invalid argument supplied for foreach() in Z:\home\magento\www\app\design\frontend\default\default\template\news\news.phtml on line 3

у меня 3я строка
<?php foreach($this->getNewsList() as $item): ?>

Поделиться темой: