Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Пошаговое создание модуля в Magento — начальство начинающего разработчика

Anna | 29.05.2014 | нет комментариев

Сколько о Magento не пиши, а все равно вопросов много ;) © jeje

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

В данной статье я попытаюсь показать создание модуля пошагово с объяснениями всякого метаморфозы на примере модуля новостей «DS News», где DS — это Namespace (Пространство имён), а News — это наименование модуля. Данная схема именования модулей является достаточно комфортной для того, Дабы не опасаться раздора имён в наименовании модулей. Специальный упор постараюсь сделать на трактование значений, используемых в файле конфигурации — наименования узлов и места, где они применяются. Сам я пользуюсь данным начальством непрерывно при создании нового модуля, т.к. запомнить откуда какие данные идут, какие классы необходимо наследовать и т.д… легко немыслимо физически. А здесь всё в одной статье :)

Не буду описывать установку Magento и заполнение товарами, считая что система теснее работает. Впрочем следует удостовериться в том, что кеширование отключено (кеш дозволено отключить в админке на страницеSystem/Cache Management) — это нужно для того, Дабы сразу видеть изготавливаемые метаморфозы.

Шаг 1. Регистрация модуля в системе

На первом шаге сотворим наименьший модуль, тот, что не будет ничего делать, но но будет учитываться в системе (и показываться в админке):

  1. Сделать директорию /app/code/local/DS/News
  2. Сделать файл etc/config.xml
    <?xml version="1.0" ?>
    <config>
        <modules>
            <DS_News>
                <version>0.0.1</version>
            </DS_News>
        </modules>
    </config>
    
  3. Сделать файл DS_News.xml в директории /app/etc/modules/
    <?xml version="1.0" ?>
    <config>
        <modules>
            <DS_News>
                <active>true</active>
                <codePool>local</codePool>
            </DS_News>
        </modules>
    </config>
    

В итоге создания данной конструкции директорий и файлов, в системе будет зарегистрирован модуль DS_News. Данный модуль дозволено увидеть в списке модулей в админке по путиSystem/Configuration/Advanced/Advanced/Disable Modules Output.

В пункте 1 создаётся директория /app/code/local/DS/News, в которой будет храниться стержневой код модуля: модели, контроллеры, хелперы и т.д… В пункте 2 создаётся директория etc, в которой хранятся файлы конфигурации модуля, а также создаётся стержневой файл конфигурации config.xml, в котором пока указывается только версия модуля. В пункте 3 модуль регистрируется в системе.

По факту, для задачи регистрации модуля самым значимым является пункт 3 — модуль будет показан в админке даже при отсутствии директории модуля. Наименование файла DS_News.xml в директории/app/etc/modules/ тоже не является непременным — при загрузке система парсит все XML файлы из данной директории. Это может быть комфортно при разработке в случае, когда устанавливаются несколько взаимосвязанных модулей — все модули дозволено указать в одном XML файле.

Наименования модуля DS_News в xml файлах состоит из 2-х частей:[название директории namespace]_[название директории модуля]. В связи с данным подходом именования, наименование директорий желанно составлять только из букв, и, также, значимо рассматривать регистр — наименование модуля в файлах конфигурации должно верно совпадать с наименованием директорий для правильной работы на *nix системах.

В файле /app/etc/modules/DS_News.xml используютсeModel> </dsnews> <dsnews_resource> <entities> <table_news> <table>ds_news_entities</table> </table_news> </entities> </dsnews_resource> </models> <resources> … </resources> </global> </config>

  • Изменить файл sql/dsnews_setup/install-0.0.1.php
    <?php
    
    $installer = $this;
    $tableNews = $installer->getTable('dsnews/table_news');
    
    die($tableNews);
    
    $installer->startSetup();
    $installer->run("DROP TABLE IF EXISTS {$tableNews}");
    $installer->run("CREATE TABLE {$tableNews} (
            `news_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
            `title` VARCHAR(255) NOT NULL,
            `content` TEXT NOT NULL,
            `created` DATETIME,
    
            PRIMARY KEY (`news_id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
    
    $installer->endSetup();
    
    

Если в браузере выведется наименование таблиы ds_news_entities, то всё сделано верно — дозволено удалить строчку с die и обновить окно для заключения установки модуля. В отвратном случае следует удалить запись о источнике модуля из таблицы core_resource с поддержкой какого-либо SQL администратора:

DELETE FROM `core_resource` WHERE `code` = 'dsnews_setup';

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

Наименование узла модели dsnews, также как и наименование узла модели-источника dsnews_resource, расположенных в config/global/models/, может быть любым. Исключительное требование — уникальность наименования узла среди других моделей/ресурсов системы.

Внутри кода инстраллера в пункте 2 запрашивается наименование таблицы с поддержкой функции $installer->getTable(‘dsnews/table_news’), которая принимает в качестве довода строку
[название узла модели]/[название узла entities ресурса]. Сам узел модели не хранит наименования таблиц, так как низкоуровневое взаимодействие с БД, по логике Magento, должно протекать с поддержкой моделей-источников. По этой причине модель хранит только ссылку на узел модели-источника в теге resourceModel.

Шаг 4. Доступ к модулю через FrontEnd

Сейчас выведем надпись «Hello World» на сайте.

  1. Добавить секцию frontend в файл конфигурации etc/config.xml
    <?xml version="1.0" ?>
    <config>
        <modules>
            ...
        </modules>
        <frontend>
            <routers>
                <dsnews>
                    <use>standard</use>
                    <args>
                        <module>DS_News</module>
                        <frontName>news</frontName>
                    </args>
                </dsnews>
            </routers>
        </frontend>
        <global>
            ...
        </global>
    </config>
    
  2. Сделать файл controllers/IndexController.php
    <?php
    
    class DS_News_IndexController extends Mage_Core_Controller_Front_Action
    {
    
        public function indexAction()
        {
            echo '<h1>News</h1>';
        }
    
    }
    

Сейчас по адресу http://site.com/news/ будет выводиться надпись «News».

Наименование узла dsnews, расположенного в сегменты config/frontend/routers/, может быть любым; исключительное требование — это уникальность наименования среди других роутеров системы. Больше значимым является значение узла frontName — это значение будет применяться для определения надобного модуля во время обработки запроса.

Путь определяется по дальнейшей схеме: http://[site]/[router]/[controller]/[action]. При запросе дозволено не указывать [action] либо пару [controller]/[action], тогда взамен недостающих параметров будет применяться значение по умолчанию index. Таким образом пути http://site.com/news/http://site.com/news/index/,http://site.com/news/index/index являются равнозначными — обращаются к модулю DS News, контроллеруIndexController и действию indexAction.

Шаг 5. Применение образца для итога данных

Информацию дозволено генерировать напрямую в контроллере, а дозволено применять особый образец для отделения логики отображения от программной логики

  1. Сделать файл образца /app/design/frontend/[package]/[theme]/template/ds_news/index.phtml
    <h1>Template ds_news/index.phtml</h1>
    
  2. Сделать файл конфигурации макета страницы /app/design/frontend/[package]/[theme]/layout/ds_news.xml
    <?xml version="1.0" ?>
    <layout>
        <dsnews_index_index>
            <reference name="content">
                <block type="core/template" template="ds_news/index.phtml" />
            </reference>
        </dsnews_index_index>
    </layout>
    
  3. Добавить секцию layout в файл конфигурации etc/config.xml
    <?xml version="1.0" ?>
    <config>
        <modules>
            ...
        </modules>
        <frontend>
            <layout>
                <updates>
                    <dsnews>
                        <file>ds_news.xml</file>
                    </dsnews>
                </updates>
            </layout>
            <routers>
                ...
            </routers>
        </frontend>
        <global>
            ...
        </global>
    </config>
    
  4. Изменить контроллер controllers/IndexController.php
    <?php
    
    class DS_News_IndexController extends Mage_Core_Controller_Front_Action
    {
    
        public function indexAction()
        {
            $this->loadLayout();
            $this->renderLayout();
        }
    
    }
    

Сейчас по пути http://site.com/news/ должна открываться страница сайта со всеми блоками (header, footer, sidebar), а на месте содержимого страницы будет показана надпись «Template ds_news/index.phtml».

Все образцы хранятся в директории template, а настройки макета страницы — в директории нынешней темы/app/design/frontend/[package]/[theme]. При работе с Magento нужно создавать личные темы, не трогая содержимого базовых тем /app/design/frontend/base/ и /app/design/frontend/default/ из-за того, что содержимое базовых тем может быть перезаписано при обновлении версии Magento.

При инициализации макета страницы $this->loadLayout() в действии контроллера происходит поочерёдная загрузка layout handle-ов страницы, которые могут изменять друг друга. По умолчанию, первым грузитсяhandle default, а одним из последних — handle с наименованием [router]_[controller]_[action], где [router] — это наименование узла config/frontend/routers/[router].

В пункте 2 определяется layout handle для нашего контроллера/действия: dsnews_index_index, внутри которого создаётся блок встроенного типа core/template и образцом dsnews/index.phtml, сделанным в пункте 1. В пункте 3 в файл конфигурации добавляется сегмент config/frontend/layout/updates, в которой указывается файл обновлений, тот, что необходимо применить при инициализации темы.

Если изменений в макете темы не так много и модуль не планируется реализовывать в других планах, то layout handle дозволено поместить в файле /app/design/frontend/[package]/[theme]/layout/local.xml,
тот, что грузится неизменно позже всех обновлений — данный файл нет необходимости прописывать в файле конфигурации модуля config.xml

В случае затруднения с определением положительного наименования layout handle, дозволено посмотреть список применяемых handle-ов в контроллере:

<?php

class DS_News_IndexController extends Mage_Core_Controller_Front_Action
{

    public function indexAction()
    {
        $this->loadLayout();
        $layoutHandles = $this->getLayout()->getUpdate()->getHandles();
        echo '<pre>' . print_r($layoutHandles, true) . '</pre>';
    }

}

В итоге дозволено будет увидеть итог, подобно данного:

Array
(
    [0] => default
    [1] => STORE_default
    [2] => THEME_frontend_[package]_[theme]
    [3] => dsnews_index_index
    [4] => customer_logged_out
)
Шаг 6. Показ новостей напрямую из базы данных

На данном этапе дозволено испробовать вывести новости в образец, применяя данные напрямую из базы. Для этого заранее необходимо добавить в базу несколько тестовых новостей:

  1. Добавить в базу тестовые новости для итога:
    INSERT INTO `ds_news_entities` VALUES
        (NULL, 'News 1', 'News 1 Content', '2013-10-16 17:45'),
        (NULL, 'News 2', 'News 2 Content', '2013-11-07 04:12'),
        (NULL, 'News 3', 'News 3 Content', '2014-01-12 15:55');
    
  2. Изменить образец /app/design/frontend/[package]/[theme]/template/dsnews/index.phtml
    <h1>News</h1>
    <?php
    $news = Mage::registry('news');
    foreach ($news as $item) {
        echo '<h2>' . $item['title'] . '</h2>';
    }
    
  3. Изменить контроллер controllers/IndexController.php
    <?php
    
    class DS_News_IndexController extends Mage_Core_Controller_Front_Action
    {
    
        public function indexAction()
        {
            $resource = Mage::getSingleton('core/resource');
            $read = $resource->getConnection('core_read');
            $table = $resource->getTableName('dsnews/table_news');
    
            $select = $read->select()
                    ->from($table, array('news_id', 'title', 'content', 'created'))
                    ->order('created DESC');
    
            $news = $read->fetchAll($select);
            Mage::register('news', $news);
    
            $this->loadLayout();
            $this->renderLayout();
        }
    
    }
    

В данном примере показан вариант прямого запроса данных из базы, а также применение реестраMage::registry для передачи новостей от контроллера в образец. Реестр доступен глобально в всякий части кода и, в редких случаях, бывает самым оптимальным решением в трудных обстановках.

Применение реестра, как и применение глобальных переменных, считается знаком некачественного кода. Прямой доступ к базе данных также не рекомендуется, так как это является потенциальной задачей совместимости и переностимости кода. Отменнее применять вероятности, предоставляемые самой системой для работы с объектами — модели и коллекции, которые будут рассматриваться в дальнейшем разделе.

Шаг 7. Создание и применение моделей

По логике Magento (и многих других ООП систем), обработка данных должна производиться на ярусе объектов/моделей и коллекций, не рассматривая методы приобретения и сохранения данных. Операции приобретения и сохранения данных оставляются для моделей-источников, как больше низкоуровневые — тогда, в случае метаморфозы метода хранения данных (БД, файлы и т.д…), не требуется изменять логику обработки, а понадобится лишь изменить модель-источник, занимающейся приобретением и сохранением данных. Переделаем итог новостей с применением моделей, а также добавим итог содержимого новости по айди:

  1. Добавить узел class в узел моделей в файле конфигурации:
    <?xml version="1.0" ?>
    <config>
        <modules>
            ...
        </modules>
        <frontend>
            ...
        </frontend>
        <global>
            <models>
                <dsnews>
                    <class>DS_News_Model</class>
                    <resourceModel>dsnews_resource</resourceModel>
                </dsnews>
                <dsnews_resource>
                    <class>DS_News_Model_Resource</class>
                    <entities>
                        <table_news>
                            <table>ds_news_entities</table>
                        </table_news>
                    </entities>
                </dsnews_resource>
            </models>
            <resources>
                ...
            </resources>
        </global>
    </config>
    
  2. Сделать файл модели новостей Model/News.php
    <?php
    
    class DS_News_Model_News extends Mage_Core_Model_Abstract
    {
    
        public function _construct()
        {
            parent::_construct();
            $this->_init('dsnews/news');
        }
    
    }
    
  3. Сделать файл источника модели новостей Model/Resource/News.php
    <?php
    
    class DS_News_Model_Resource_News extends Mage_Core_Model_Mysql4_Abstract
    {
    
        public function _construct()
        {
            $this->_init('dsnews/table_news', 'news_id');
        }
    
    }
    
  4. Сделать файл источника коллекции Model/Resource/News/Collection.php
    <?php
    
    class DS_News_Model_Resource_News_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
    {
    
        public function _construct()
        {
            parent::_construct();
            $this->_init('dsnews/news');
        }
    
    }
    
  5. Изменить контроллер controllers/IndexController.php
    <?php
    
    class DS_News_IndexController extends Mage_Core_Controller_Front_Action
    {
    
        public function indexAction()
        {
            $news = Mage::getModel('dsnews/news')->getCollection()->setOrder('created', 'DESC');
            $viewUrl = Mage::getUrl('news/index/view');
    
            echo '<h1>News</h1>';
            foreach ($news as $item) {
                echo '<h2><a href="' . $viewUrl . '?id=' . $item->getId() . '">' . $item->getTitle() . '</a></h2>';
            }
        }
    
        public function viewAction()
        {
            $newsId = Mage::app()->getRequest()->getParam('id', 0);
            $news = Mage::getModel('dsnews/news')->load($newsId);
    
            if ($news->getId() > 0) {
                echo '<h1>' . $news->getTitle() . '</h1>';
                echo '<div>' . $news->getContent() . '</div>';
            } else {
                $this->_forward('noRoute');
            }
        }
    
    }
    

Сейчас по ссылке http://site.com/news откроется список новостей в виде ссылок, при клике на которые будет открываться страница с содержимым новости.

В пункте 1 в файл кофигурации добавляется узел class, в котором прописываются базовые префиксы классов для модели DS_News_Model и источника DS_News_Model_Resource.

При запросе модели в контроллере (пункт 5) Mage::getModel(‘dsnews/news’), функция getModel принимает строку типа [model]/[class], из которой формируется наименование класса модели, где [model] — это наименование узла config/global/models/[model], из которой берётся значение узла class — DS_News_Model, и к этому префиксу класса добавляется значение [class] (первая буква в всяком слове [class] преобразуется в заглавную). К примеру, из строки dsnews/news получается класс DS_News_Model_News, а из строкиdsnews/news_gallery получится класс DS_News_Model_News_Gallery.

В пункте 2 создаётся базовая модель новости DS_News_Model_News, в конструкторе которого происходит инициализация источника $this->_init(‘dsnews/news’): в качестве параметров функция принимает строку[model]/[class], где [model] — это наименование узла config/global/models/[model], а [class] — это наименование класса. Впрочем, в различие от модели, инициализация класса источника в качестве префикса класса использует значение узла config/global/models/[resourceModel]/class источника, на тот, что ссылается модель в узле resourceModel. В результате при инициализации источника $this->_init(‘dsnews/news’) будет инициализироваться класс DS_News_Model_Resource_News.

В пункте 3 создаётся класс источника DS_News_Model_Resource_News, в котором происходит инициализация таблицы $this->_init(‘dsnews/table_news’, ‘news_id’): первым параметром является путь к наименованию требуемой таблицы, а вторым — поле, применяющееся в качестве первичного ключа (PRIMARY KEY) таблицы.

В пункте 4 происходит инициализация класса коллекции объектов, в конструкторе которой происходит инициализация начальной модели DS_News_Model_News.

В пункте 5 происходит метаморфоза приобретения данных из базы. На данный раз применяются модели и коллекции. В действии indexAction происходит запрос всех новостей с поддержкой приобретения коллекции$news = Mage::getModel(‘dsnews/news’)->getCollection(). Наименование класса коллекции вычисляется из наименования класса источника модели DS_News_Model_Resource_News _Collection.

В действии viewAction происходит загрузка новости по id, полученному в запросе. Если новость с данным айди существует, то выведется наименование новости и содержимое. В отвратном случае будет сгенерирована страница 404.

Шаг 8. Создание и применение блоков

Для итога данных в Magento применяется особые блоки — это объекты кода, отвечающие за рендеринг того либо другого участка кода. На шаге 5: Применение образца для итога данных, был использован типовой блокcore/template. На этом шаге для модуля новостей будут сделаны блоки для итога списка и содержимого новости.

  1. Добавить узел блоков config/global/blocks
    <?xml version="1.0" ?>
    <config>
        ...
        <global>
            <blocks>
                <dsnews>
                    <class>DS_News_Block</class>
                </dsnews>
            </blocks>
            ...
        </global>
    </config>
    
  2. Сделать класс блока списка новостей Block/News.php
    <?php
    
    class DS_News_Block_News extends Mage_Core_Block_Template
    {
    
        public function getNewsCollection()
        {
            $newsCollection = Mage::getModel('dsnews/news')->getCollection();
            $newsCollection->setOrder('created', 'DESC');
            return $newsCollection;
        }
    
    }
    
  3. Сделать класс блока содержимого новости Block/View.php
    <?php
    
    class DS_News_Block_View extends Mage_Core_Block_Template
    {
    
    }
    
  4. Обновить файл конфигурации /app/design/frontend/[package]/[theme]/layout/ds_news.xml
    <layout>
        <dsnews_index_index>
            <reference name="content">
                <block type="dsnews/news" template="ds_news/index.phtml" />
            </reference>
        </dsnews_index_index>
        <dsnews_index_view>
            <reference name="content">
                <block type="dsnews/view" name="news.content" template="ds_news/view.phtml" />
            </reference>
        </dsnews_index_view>
    </layout>
    
  5. Изменить файл образца /app/design/frontend/[package]/[theme]/template/ds_news/index.phtml
    <?php
    $news = $this->getNewsCollection();
    $newsViewUrl = Mage::getUrl('news/index/view');
    ?>
    <h1>News</h1>
    <?php foreach ($news as $item): ?>
        <h2>
            <a href="<?php echo $newsViewUrl; ?>?id=<?php echo $item->getId(); ?>">
                <?php echo $item->getTitle(); ?>
            </a>
        </h2>
    <?php endforeach; ?>
    
  6. Сделать файл образца /app/design/frontend/[package]/[theme]/template/ds_news/view.phtml
    <h1><?php echo $newsItem->getTitle(); ?></h1>
    <div><?php echo $newsItem->getContent(); ?></div>
    
  7. Изменить контролллер controllers/IndexController.php
    <?php
    
    class DS_News_IndexController extends Mage_Core_Controller_Front_Action
    {
    
        public function indexAction()
        {
            $this->loadLayout();
            $this->renderLayout();
        }
    
        public function viewAction()
        {
            $newsId = Mage::app()->getRequest()->getParam('id', 0);
            $news = Mage::getModel('dsnews/news')->load($newsId);
    
            if ($news->getId() > 0) {
                $this->loadLayout();
                $this->getLayout()->getBlock('news.content')->assign(array(
                    "newsItem" => $news,
                ));
                $this->renderLayout();
            } else {
                $this->_forward('noRoute');
            }
        }
    
    }
    

При открытии страницы http://site.com/news/ сейчас будет выводиться список новостей как содержимое страницы сайта, а переход по ссылке новости откроет страницу с содержимым новости.

Механизм блоков разрешает применять логику отображения вне зависимости от контроллера. Как видно в пункте 3, действие indexAction сейчас занимается только загрузкой и отображением образцов. Логика приобретения новостей сейчас лежит на блоке DS_News_Block_News, а логика отображения — на образце/app/design/frontend/[package]/[theme]/template/dsnews/index.phtml. Таким образом, к примеру, если список последних новостей должен показываться в сайдбаре какой-либо иной страницы, то довольно будет подключить необходимый блок к странице без задействования контроллера.

Внутри образца дозволено обращаться ко каждому способам и свойствам класса блока через переменную$this, таким образом логику получения/обработки каких-либо данных дозволено реализовать в виде способов блока.

В пункте 1 в конфигурацию был добавлен узел блоков модуля новостей для того, Дабы при итоге образцов Magento знала к какому классу блока обращаться. Классы блоков, используемые для рендеринга образцов указываются как тип (type) блока в .xml файле. Довод type блока принимает строку вида [module]/[block], где[module] — это наименование узла config/global/blocks/[module], из которого берётся значение узла class для образования наименования класса блока; a [block] — наименование класса. В итоге type=«dsnews/news»будет преобразован в класс блока DS_News_Block_News.

В пункте 4 произшло добавление в содержимое страницы <reference name=«content» были добавлены сделанные блоки. Для того, Дабы к блокам дозволено было обращаться, всякий блок должен иметь неповторимое имя в макете нынешней страницы. Для отображения содержимого новости был добавлен новейший layout handle dsnews_index_view, внутри которого был добавлен блок <block type=«dsnews/view» name=«news.content» template=«ds_news/view.phtml» />. Внутри контроллера в действии viewAction позже загрузки макета страницы происходит обращение к данному блоку и предназначение нынешней новости через функцию assign, в которую передаётся ассоциативный массив с данными. При генерации образца к ключам данного массива дозволено обращаться как к переменным.

Шаг 9. Создание админки для модуля

Сейчас, когда модуль может показывать новости, нужно как-то эти новости добавлять в базу. Для этого будет сделана административная часть модуля.

  1. Сделать контроллер controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction()
        {
            echo '<h1>News Module: Admin section</h1>';
        }
    
    }
    
  2. Добавить путь до контроллера в файле etc/config.xml
    <?xml version="1.0" ?>
    <config>
        ...
        <admin>
            <routers>
                <dsnews_admin>
                    <use>admin</use>
                    <args>
                        <module>DS_News</module>
                        <frontName>dsnews_admin</frontName>
                    </args>
                </dsnews_admin>
            </routers>
        </admin>
        ...
    </config>
    
  3. Добавить пункт меню для модуля в админке, изменив файл etc/config.xml
    <?xml version="1.0" ?>
    <config>
        ...
        <adminhtml>
            <menu>
                <dsnews module="dsnews">
                    <title>News</title>
                    <sort_order>77</sort_order>
                    <action>dsnews_admin/adminhtml_news</action>
                </dsnews>
            </menu>
        </adminhtml>
        ...
    </config>
    
  4. Добавить хелпер-заглушку Helper/Data.php
    <?php
    
    class DS_News_Helper_Data extends Mage_Core_Helper_Abstract
    {
    
    }
    
  5. Добавить секцию хелпера в файл конфигурации etc/config.xml
    <?xml version="1.0" ?>
    <config>
        ...
        <global>
            ...
            <helpers>
                <dsnews>
                    <class>DS_News_Helper</class>
                </dsnews>
            </helpers>
            ...
        </global>
        ...
    </config>
    

Сейчас, залогинившись в админке, дозволено увидеть новейший пункт меню News, перейдя по которому дозволено увидеть надпись «News Module: Admin section».

В пункте 1 создаётся контроллер для админки модуля новостей, тот, что регистрируется в пункте 2 — в роутерах config/admin/routers/[router]. Позже регистрации в админке, контроллер будет доступен по путиhttp://site.com/index.php/[frontName]/[controller]/index/, где [frontName] — это значение узлаconfig/admin/routers/[router]/args/[frontName], а [controller] — это путь, из которого генерируется класс контроллера из класса модуля DS_News [controller] Controller. В итоге данной схемы из значенияadminhtml_news получится класс контроллера DS_News_Adminhtml_NewsController. Таким образом админка модуля новостей будет доступна по адресу http://site.com/index.php/dsnews_admin/adminhtml_news/index/.

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

Если пропустить пункты 4 и 5, то при попытке зайти на страницу админки, будет сгенерирована оплошность типа «Warning: include(Mage\DS\News\Helper\Data.php): failed to open stream: No such file or directory…». Для образования меню Magento использует хелпер модуля, указанного в качестве параметра module в узлеconfig/adminhtml/menu/[menu]. В качестве значения параметра задаётся наименование узла модуля хелпераconfig/global/helpers/[helper], из которого формируется наименование класса хелпера. По этой причине в пункте 4 и 5 создаётся хелпер-заглушка, тот, что регистрируется в файле конфигурации по нужному пути.

Ещё одинмомент, на тот, что стоит обратить внимание — это различие наименований роутеров и frontName в сегменты config/admin/routers от таких же в сегменты config/frontend/routers. При совпадении данных значений
(а сходственное искушение будет), допустимы задачи с сохранением сообщений в сессии, а также задачи с редиректами модуля при включении HTTPS на сайте.

Шаг 10. Применение блоков в админке

На данном шаге применяется примитивный блок для итога информации в контексте содержимого админки как пример итога информации. В последующем для образования админки будут использваться наследуемые блоки для итога стандартных и нестандартных форм.

  1. Изменить контроллер controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction()
        {
            $this->loadLayout();
            $this->_setActiveMenu('dsnews');
    
            $contentBlock = $this->getLayout()->createBlock('dsnews/adminhtml_news');
            $this->_addContent($contentBlock);
            $this->renderLayout();
        }
    
    }
    
  2. Сделать блок Block/Adminhtml/News.php
    <?php
    
    class DS_News_Block_Adminhtml_News extends Mage_Adminhtml_Block_Abstract
    {
    
        public function _toHtml()
        {
            return '<h1>News Module: Admin section</h1>';
        }
    
    }
    

Magento для генерации всякий страницы использует образцы, как в frontend (на сайте), так и в backend (админке) части. Дозволено воспользоваться .xml файлом макета для добавления/изменения блоков на странице (как это сделано на шаге 5: «Применение образца для итога данных»), впрочем в большинстве случаев вероятностей, предоставляемых админ-классами блоков Magento, абсолютно довольно. А в редких случаях дозволено формировать HTML «на лету» прямо в классе своего блока. К тому же, образцы для админки и файлы конфигурации модуля будут находиться в директории /app/design/adminhtml/default/default/, что не есть отлично, т.к. приводит к излишнему распределению кода модуля по различным директориям.

В пункте 1 происходит инициализация макета страницы админки $this->loadLayout() и выставление нынешнего энергичного пункта меню. Позже инициализации макета дозволено обращаться к теснее сформированному макету с поддержкой функции $this->getLayout(): добавлять/удалять/изменять блоки и исполнять другие программные действия с содержимым страницы. Функция создания блока createBlock в качестве первого параметра принимает строку блока [module]/[block] такого же типа, как и в макете <block type=”[module]/[block]“ для образования класса блока; позже чего сделанный блок добавляется в макет в качестве содержимого страницы.

Шаг 11. Итог списка в Data Grid

Позже удачного итога блока в админку, воспользуемся гридом для итога списка новостей.

  1. Изменить блок Block/Adminhtml/News.php
    <?php
    
    class DS_News_Block_Adminhtml_News extends Mage_Adminhtml_Block_Widget_Grid_Container
    {
    
        protected function _construct()
        {
            parent::_construct();
    
            $helper = Mage::helper('dsnews');
            $this->_blockGroup = 'dsnews';
            $this->_controller = 'adminhtml_news';
    
            $this->_headerText = $helper->__('News Management');
            $this->_addButtonLabel = $helper->__('Add News');
        }
    
    }
    
  2. Сделать блок грида Block/Adminhtml/News/Grid.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Grid extends Mage_Adminhtml_Block_Widget_Grid
    {
    
        protected function _prepareCollection()
        {
            $collection = Mage::getModel('dsnews/news')->getCollection();
            $this->setCollection($collection);
            return parent::_prepareCollection();
        }
    
        protected function _prepareColumns()
        {
    
            $helper = Mage::helper('dsnews');
    
            $this->addColumn('news_id', array(
                'header' => $helper->__('News ID'),
                'index' =>'news_id'
            ));
    
            $this->addColumn('title', array(
                'header' => $helper->__('Title'),
                'index' => 'title',
                'type' => 'text',
            ));
    
            $this->addColumn('created', array(
                'header' => $helper->__('Created'),
                'index' => 'created',
                'type' => 'date',
            ));
    
            return parent::_prepareColumns();
        }
    
    }
    

Наименование класса блока грида формируется из значений, заданных в пункте 1 в блокеDS_News_Block_Adminhtml_News[_blockGroup]/[_controller]_grid, где _blockGroup — это наименование узла блоков модуля config/global/blocks/[_blockGroup]. В итоге получится строка типа блока«dsnews/adminhtml_news_grid».

В функции _prepareColumns добавляются колонки, которые будут показываться в гриде. Функция addColumnпервым параметром принимает наименование наименование колонки, а вторым параметром — массив с данными о колонке, где index — наименование колонки в базе данных, а type — тип колонки. Список поддерживаемых типов и опций дозволено увидеть в классе Mage_Adminhtml_Block_Widget_Grid_Column.

Шаг 12. Массовые операции в Data Grid

Сейчас добавим вероятность удаления сразу нескольких новостей

  1. Добавить в блок грида Block/Adminhtml/News/Grid.php способ _prepareMassaction
    <?php
    
    class DS_News_Block_Adminhtml_News_Grid extends Mage_Adminhtml_Block_Widget_Grid
    {
    
        protected function _prepareCollection(){ ... }
    
        protected function _prepareColumns(){ ... }
    
        protected function _prepareMassaction()
        {
            $this->setMassactionIdField('news_id');
            $this->getMassactionBlock()->setFormFieldName('news');
    
            $this->getMassactionBlock()->addItem('delete', array(
                'label' => $this->__('Delete'),
                'url' => $this->getUrl('*/*/massDelete'),
            ));
            return $this;
        }
    
    }
    
  2. Добавить способ massDeleteAction в контроллер controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction(){ ... }
    
        public function massDeleteAction()
        {
            $news = $this->getRequest()->getParam('news', null);
    
            if (is_array($news) && sizeof($news) > 0) {
                try {
                    foreach ($news as $id) {
                        Mage::getModel('dsnews/news')->setId($id)->delete();
                    }
                    $this->_getSession()->addSuccess($this->__('Total of %d news have been deleted', sizeof($news)));
                } catch (Exception $e) {
                    $this->_getSession()->addError($e->getMessage());
                }
            } else {
                $this->_getSession()->addError($this->__('Please select news'));
            }
            $this->_redirect('*/*');
        }
    
    }
    
    

Сейчас в списке новостей первой колонкой обязаны показываться чекбоксы. Дозволено выделить сразу несколько новостей, предпочесть в правом верхнем углу списка Actions опцию Delete и нажать кнопку Submitдля удаления выбранных новостей.

В пункте 1 классу грида был добавлен способ _prepareMassaction, в котором устанавливается id-поле news_id, а также наименование параметра, которое будет применяться для приобретения массива id-полей. Дальше происходит добавление опций в список массовых операций — айди операции, наименование и ссылка, куда отправлять выбранные записи. Дозволено добавлять также зависимые селекты: в зависимости от выбранной операции будет показываться 2-й селект. Скажем, обновление ранга в списке продуктов. Пример зависимых селектов в массовых операциях дозволено посмотреть в классе гридаMage_Adminhtml_Block_Catalog_Product_Grid.

Шаг 13. CRUD: добавление, редактирование и удаление

Реализуем добавление, редактирование и удаление записей

  1. Изменить контроллер controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction()
        {
            $this->loadLayout()->_setActiveMenu('dsnews');
            $this->_addContent($this->getLayout()->createBlock('dsnews/adminhtml_news'));
            $this->renderLayout();
        }
    
        public function newAction()
        {
            $this->_forward('edit');
        }
    
        public function editAction()
        {
            $id = (int) $this->getRequest()->getParam('id');
            Mage::register('current_news', Mage::getModel('dsnews/news')->load($id));
    
            $this->loadLayout()->_setActiveMenu('dsnews');
            $this->_addContent($this->getLayout()->createBlock('dsnews/adminhtml_news_edit'));
            $this->renderLayout();
        }
    
        public function saveAction()
        {
            if ($data = $this->getRequest()->getPost()) {
                try {
                    $model = Mage::getModel('dsnews/news');
                    $model->setData($data)->setId($this->getRequest()->getParam('id'));
                    if(!$model->getCreated()){
                        $model->setCreated(now());
                    }
                    $model->save();
    
                    Mage::getSingleton('adminhtml/session')->addSuccess($this->__('News was saved successfully'));
                    Mage::getSingleton('adminhtml/session')->setFormData(false);
                    $this->_redirect('*/*/');
                } 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($this->__('Unable to find item to save'));
            $this->_redirect('*/*/');
        }
    
        public function deleteAction()
        {
            if ($id = $this->getRequest()->getParam('id')) {
                try {
                    Mage::getModel('dsnews/news')->setId($id)->delete();
                    Mage::getSingleton('adminhtml/session')->addSuccess($this->__('News was deleted successfully'));
                } catch (Exception $e) {
                    Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
                    $this->_redirect('*/*/edit', array('id' => $id));
                }
            }
            $this->_redirect('*/*/');
        }
    
        public function massDeleteAction(){ ... }
    
    }
    
  2. Добавить способ getRowUrl в блок грида Block/Adminhtml/News/Grid.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Grid extends Mage_Adminhtml_Block_Widget_Grid
    {
    
        protected function _prepareCollection(){ ... }
    
        protected function _prepareColumns(){ ... }
    
        protected function _prepareMassaction(){ ... }
    
        public function getRowUrl($model)
        {
            return $this->getUrl('*/*/edit', array(
                        'id' => $model->getId(),
                    ));
        }
    
    }
    
  3. Сделать блок для редактирования новости Block/Adminhtml/News/Edit.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
    {
    
        protected function _construct()
        {
            $this->_blockGroup = 'dsnews';
            $this->_controller = 'adminhtml_news';
        }
    
        public function getHeaderText()
        {
            $helper = Mage::helper('dsnews');
            $model = Mage::registry('current_news');
    
            if ($model->getId()) {
                return $helper->__("Edit News item '%s'", $this->escapeHtml($model->getTitle()));
            } else {
                return $helper->__("Add News item");
            }
        }
    
    }
    
  4. Сделать блок для итога формы Block/Adminhtml/News/Edit/Form.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
    {
    
        protected function _prepareForm()
        {
            $helper = Mage::helper('dsnews');
            $model = Mage::registry('current_news');
    
            $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'
                    ));
    
            $this->setForm($form);
    
            $fieldset = $form->addFieldset('news_form', array('legend' => $helper->__('News Information')));
    
            $fieldset->addField('title', 'text', array(
                'label' => $helper->__('Title'),
                'required' => true,
                'name' => 'title',
            ));
    
            $fieldset->addField('content', 'editor', array(
                'label' => $helper->__('Content'),
                'required' => true,
                'name' => 'content',
            ));
    
            $fieldset->addField('created', 'date', array(
                'format' => Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT),
                'image' => $this->getSkinUrl('images/grid-cal.gif'),
                'label' => $helper->__('Created'),
                'name' => 'created'
            ));
    
            $form->setUseContainer(true);
    
            if($data = Mage::getSingleton('adminhtml/session')->getFormData()){
                $form->setValues($data);
            } else {
                $form->setValues($model->getData());
            }
    
            return parent::_prepareForm();
        }
    
    }
    

Сейчас при клике на новость в Data Grid откроется форма редактирования, а при клике на кнопку Add News — форма добавления записи.

В пункте 1 в контроллер были добавлены способы neweditsave и delete, отвечающие за соответствующие действия. Так как действие new по большей части повторяет действие edit, то в действии new легко происходит редирект на действие edit. Внутри действия инициализируется модель новости, и добавляется в всеобщий реестр (это один из тех случаев, когда применение глобального реестра меньшее из гневен), позже чего в содержимое добавляется блок редактирования. Стоит обратить внимание в действии save на
строку $model->setData($data)->setId(…) — при сохранении у модели добавление айди происходит позже инициализации данных, т.к. если сначала добавить айди, то при вызове setData айди будет перезатёрт. В результате при сохранении взамен редактирования нынешней записи, будет сделана новая.

В пункте 2 добавляется способ инициализации ссылки для всякой из строк Data Grid — реакция на клик пользователя по строке. В пункте 3 инициализируется блок редактирования новости, в конструкторе которого происходит инициализация переменных формы, которые будут применяться для построения класса самой формы по схеме [_blockGroup]/[_controller]_[_mode]_form, в итоге получитсяdsnews/adminhtml_news_edit_form (_mode по умолчанию имеет значение edit).

В последнем пункте происходит непринужденно инициализация самой формы и добавление надобных полей. Следует обратить внимание, что при создании формы new Varien_Data_Form в качестве параметра id должен указываться неповторимый HTML id формы на странице, т.к. отправка и метаморфоза формы происходит JavaScript-ом, тот, что обращается по данному айди. При добавлении полей на форму необходимо, Дабы значение name совпадало с наименованиями полей в базе данных.

Шаг 14. Применение вкладок (tabs)

При редактировании модели дозволено разбить форму на несколько частей, и показывать их отдельно в различных TAB-ах.

  1. Изменить контроллер controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction(){ ... }
    
        public function newAction(){ ... }
    
        public function editAction()
        {
            $id = (int) $this->getRequest()->getParam('id');
            $model = Mage::getModel('dsnews/news');
    
            if($data = Mage::getSingleton('adminhtml/session')->getFormData()){
                $model->setData($data)->setId($id);
            } else {
                $model->load($id);
            }
            Mage::register('current_news', $model);
    
            $this->loadLayout()->_setActiveMenu('dsnews');
            $this->_addLeft($this->getLayout()->createBlock('dsnews/adminhtml_news_edit_tabs'));
            $this->_addContent($this->getLayout()->createBlock('dsnews/adminhtml_news_edit'));
            $this->renderLayout();
        }
    
        public function saveAction(){ ... }
    
        public function deleteAction() { ... }
    
        public function massDeleteAction(){ ... }
    
    }
    
  2. Сделать блок с вкладками Block/Adminhtml/News/Edit/Tabs.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs
    {
    
        public function __construct()
        {
            $helper = Mage::helper('dsnews');
    
            parent::__construct();
            $this->setId('news_tabs');
            $this->setDestElementId('edit_form');
            $this->setTitle($helper->__('News Information'));
        }
    
        protected function _prepareLayout()
        {
            $helper = Mage::helper('dsnews');
    
            $this->addTab('general_section', array(
                'label' => $helper->__('General Information'),
                'title' => $helper->__('General Information'),
                'content' => $this->getLayout()->createBlock('dsnews/adminhtml_news_edit_tabs_general')->toHtml(),
            ));
            $this->addTab('custom_section', array(
                'label' => $helper->__('Custom Fields'),
                'title' => $helper->__('Custom Fields'),
                'content' => $this->getLayout()->createBlock('dsnews/adminhtml_news_edit_tabs_custom')->toHtml(),
            ));
            return parent::_prepareLayout();
        }
    
    }
    
  3. Изменить блок формы Block/Adminhtml/News/Edit/Form.php
    <?php
    
    class DS_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();
        }
    
    }
    
  4. Добавить блок вкладки General Block/Adminhtml/News/Edit/Tabs/General.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Edit_Tabs_General extends Mage_Adminhtml_Block_Widget_Form
    {
    
        protected function _prepareForm()
        {
    
            $helper = Mage::helper('dsnews');
            $model = Mage::registry('current_news');
    
            $form = new Varien_Data_Form();
            $fieldset = $form->addFieldset('general_form', array(
                        'legend' => $helper->__('General Information')
                    ));
    
            $fieldset->addField('title', 'text', array(
                'label' => $helper->__('Title'),
                'required' => true,
                'name' => 'title',
            ));
    
            $fieldset->addField('content', 'editor', array(
                'label' => $helper->__('Content'),
                'required' => true,
                'name' => 'content',
            ));
    
            $fieldset->addField('created', 'date', array(
                'format' => Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT),
                'image' => $this->getSkinUrl('images/grid-cal.gif'),
                'label' => $helper->__('Created'),
                'name' => 'created'
            ));
    
            $form->setValues($model->getData());
            $this->setForm($form);
    
            return parent::_prepareForm();
        }
    
    }
    
  5. Добавить блок вкладки Custom
    Block/Adminhtml/News/Edit/Tabs/Custom.php

    <?php
    
    class DS_News_Block_Adminhtml_News_Edit_Tabs_Custom extends Mage_Adminhtml_Block_Widget
    {
    
        protected function _toHtml()
        {
            return '<h2>Custom Fields</h2>';
        }
    
    }
    

Сейчас при открытии формы редактирования будет открываться форма с вкладками. В данном наглядном примере показано разбиение формы на 2 вкладки — одна с полями, а иная — с любым иным содержимым. Если нужно разбить поля по различным вкладкам, то дозволено продублировать вкладку General, инициализируя на всякой форме только надобные поля.

Дозволено обратить внимание, что сейчас инициализация значений формы вынесена в контроллер, там же добавляется блок вкладок. В пункте 2 создаётся блок-контейнер для вкладок, внутри которого происходит инициализация вкладок в способе _prepareLayout. Инициализация полей и их значений сейчас вынесена из стержневой формы в форму вкладки. Не смотря на то, что во вкладке General происходит создание новой формы $form = new Varien_Data_Form(), при генерации страницы будет сгенерирована только один элемент формы, где вкладки — это легко контейнеры элементов.

Шаг 15. Добавление поля загрузки изображения

Добавление поля загрузки изображения для новости в админке.

  1. Добавить поле изображения в блок General Block/Adminhtml/News/Edit/Tabs/General.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Edit_Tabs_General extends Mage_Adminhtml_Block_Widget_Form
    {
    
        protected function _prepareForm()
        {
    
            $helper = Mage::helper('dsnews');
            $model = Mage::registry('current_news');
    
            $form = new Varien_Data_Form();
            $fieldset = $form->addFieldset('general_form', array('legend' => $helper->__('General Information')));
    
            $fieldset->addField('title', 'text', array(
                'label' => $helper->__('Title'),
                'required' => true,
                'name' => 'title',
            ));
    
            $fieldset->addField('content', 'editor', array(
                'label' => $helper->__('Content'),
                'required' => true,
                'name' => 'content',
            ));
    
            $fieldset->addField('image', 'image', array(
                'label' => $helper->__('Image'),
                'name' => 'image',
            ));
    
            $fieldset->addField('created', 'date', array(
                'format' => Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT),
                'image' => $this->getSkinUrl('images/grid-cal.gif'),
                'label' => $helper->__('Created'),
                'name' => 'created'
            ));
    
            $formData = array_merge($model->getData(), array('image' => $model->getImageUrl()));
            $form->setValues($formData);
            $this->setForm($form);
    
            return parent::_prepareForm();
        }
    
    }
    
  2. Добавить добавочные функции в хелпер Helper/Data.php
    <?php
    
    class DS_News_Helper_Data extends Mage_Core_Helper_Abstract
    {
    
        public function getImagePath($id = 0)
        {
            $path = Mage::getBaseDir('media') . '/ds_news';
            if ($id) {
                return "{$path}/{$id}.jpg";
            } else {
                return $path;
            }
        }
    
        public function getImageUrl($id = 0)
        {
            $url = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA) . 'ds_news/';
            if ($id) {
                return $url . $id . '.jpg';
            } else {
                return $url;
            }
        }
    
    }
    
  3. Добавить функции к модели Model/News.php
    <?php
    
    class DS_News_Model_News extends Mage_Core_Model_Abstract
    {
    
        protected function _construct()
        {
            parent::_construct();
            $this->_init('dsnews/news');
        }
    
        protected function _afterDelete()
        {
            $helper = Mage::helper('dsnews');
            @unlink($helper->getImagePath($this->getId()));
            return parent::_afterDelete();
        }
    
        public function getImageUrl()
        {
            $helper = Mage::helper('dsnews');
            if ($this->getId() && file_exists($helper->getImagePath($this->getId()))) {
                return $helper->getImageUrl();
            }
            return null;
        }
    
    }
    
  4. Изменить контроллер controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction(){ ... }
    
        public function newAction(){ ... }
    
        public function editAction(){ ... }
    
        public function saveAction()
        {
            $id = $this->getRequest()->getParam('id');
            if ($data = $this->getRequest()->getPost()) {
                try {
                    $helper = Mage::helper('dsnews');
                    $model = Mage::getModel('dsnews/news');
    
                    $model->setData($data)->setId($id);
                    if (!$model->getCreated()) {
                        $model->setCreated(now());
                    }
                    $model->save();
                    $id = $model->getId();
    
                    if (isset($_FILES['image']['name']) && $_FILES['image']['name'] != '') {
                        $uploader = new Varien_File_Uploader('image');
                        $uploader->setAllowedExtensions(array('jpg', 'jpeg'));
                        $uploader->setAllowRenameFiles(false);
                        $uploader->setFilesDispersion(false);
                        $uploader->save($helper->getImagePath(), $id . '.jpg'); // Upload the image
                    } else {
                        if (isset($data['image']['delete']) && $data['image']['delete'] == 1) {
                            @unlink($helper->getImagePath($id));
                        }
                    }
    
                    Mage::getSingleton('adminhtml/session')->addSuccess($this->__('News was saved successfully'));
                    Mage::getSingleton('adminhtml/session')->setFormData(false);
                    $this->_redirect('*/*/');
                } catch (Exception $e) {
                    Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
                    Mage::getSingleton('adminhtml/session')->setFormData($data);
                    $this->_redirect('*/*/edit', array(
                        'id' => $id
                    ));
                }
                return;
            }
            Mage::getSingleton('adminhtml/session')->addError($this->__('Unable to find item to save'));
            $this->_redirect('*/*/');
        }
    
        public function deleteAction(){ ... }
    
        public function massDeleteAction(){ ... }
    
    }
    

К всякой новости дозволено прикрепить только одно изображение в формате JPG, следственно нет необходимости изменять конструкцию таблицы новостей в базе данных. Изображение будет храниться в директории /media/ds_news, в качестве имени файла будет выступать айди изображения, что обеспечит уникальность наименования в файловой системе. В модель были добавлены две функции — одна для приобретения URL изображения, если изображение имеется, а вторая — удаление изображения при удалении новости. В форму было добавлено поле загрузки изображения, а ниже изменено предназначение данных для формы — в массив данных было добавлено поле image$formData = array_merge($model->getData(), array(‘image’ => $model->getImageUrl())).

Шаг 16. Подключение JavaScript/CSS файлов в админке

Сейчас подключим необохдимые скрипты и жанры в админке

  1. Сделать файл /skin/adminhtml/default/default/ds_news/adminhtml/scripts.js
    console.log('DS News admin');
    
  2. Сделать файл /skin/adminhtml/default/default/ds_news/adminhtml/styles.css
    #general_form label {
        color: #FF0000;
        font-weight: bold;
    }
    
  3. Подключить скрипты и жанры в контроллере controllers/Adminhtml/NewsController.php
    <?php
    
    class DS_News_Adminhtml_NewsController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction(){ ... }
    
        public function newAction(){ ... }
    
        public function editAction()
        {
            $id = (int) $this->getRequest()->getParam('id');
            $model = Mage::getModel('dsnews/news');
    
            if ($data = Mage::getSingleton('adminhtml/session')->getFormData()) {
                $model->setData($data)->setId($id);
            } else {
                $model->load($id);
            }
            Mage::register('current_news', $model);
    
            $this->loadLayout()->_setActiveMenu('dsnews');
    
            $this->getLayout()->getBlock('head')->addItem('skin_js', 'ds_news/adminhtml/scripts.js');
            $this->getLayout()->getBlock('head')->addItem('skin_css', 'ds_news/adminhtml/styles.css');
    
            $this->_addLeft($this->getLayout()->createBlock('dsnews/adminhtml_news_edit_tabs'));
            $this->_addContent($this->getLayout()->createBlock('dsnews/adminhtml_news_edit'));
            $this->renderLayout();
        }
    
        public function saveAction(){ ... }
    
        public function deleteAction(){ ... }
    
        public function massDeleteAction(){ ... }
    
    }
    

В итоге при открытии формы редактирования новости в консоли отладки браузера будет отображена надписьDS News admin, а шрифт наименования полей станет толстым и красным.

По умолчанию скрипты и жанры, как на сайте, так и в админке, дозволено подгружать из 2-х мест: либо директория JS, либо директория нынешней темы. Где беречь скрипты и жанры — в нынешней теме админки/skin/adminhtml/default/default/ либо в директории /js/ — дело вкуса всякого разработчика. Впрочем, по суждению автора, (), )); … } }

  • Добавить в хелпер Helper/Data.php образование списка категорий для селекта на форме редактирования, а также для списка новостей
    <?php
    
    class DS_News_Helper_Data extends Mage_Core_Helper_Abstract
    {
    
        public function getImagePath($id = 0){ ... }
    
        public function getImageUrl($id = 0){ ... }
    
        public function getCategoriesList()
        {
            $categories = Mage::getModel('dsnews/category')->getCollection()->load();
            $output = array();
            foreach($categories as $category){
                $output[$category->getId()] = $category->getTitle();
            }
            return $output;
        }
    
        public function getCategoriesOptions()
        {
            $categories = Mage::getModel('dsnews/category')->getCollection()->load();
            $options = array();
            $options[] = array(
                'label' => '',
                'value' => ''
            );
            foreach ($categories as $category) {
                $options[] = array(
                    'label' => $category->getTitle(),
                    'value' => $category->getId(),
                );
            }
            return $options;
        }
    
    }
    
  • Добавить итог категории в списке новостей Block/News/Grid.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Grid extends Mage_Adminhtml_Block_Widget_Grid
    {
    
        protected function _prepareCollection(){ ... }
    
        protected function _prepareColumns()
        {
    
            $helper = Mage::helper('dsnews');
    
            $this->addColumn('news_id', array(
                'header' => $helper->__('News ID'),
                'index' => 'news_id',
                'width' => '100px',
            ));
    
            $this->addColumn('title', array(
                'header' => $helper->__('Title'),
                'index' => 'title',
                'type' => 'text',
            ));
    
            $this->addColumn('category', array(
                'header' => $helper->__('Category'),
                'index' => 'category_id',
                'options' => $helper->getCategoriesList(),
                'type'  => 'options',
                'width' => '150px',
            ));
    
            $this->addColumn('created', array(
                'header' => $helper->__('Created'),
                'index' => 'created',
                'type' => 'date',
            ));
    
            return parent::_prepareColumns();
        }
    
        protected function _prepareMassaction(){ ... }
    
        public function getRowUrl($model){ ... }
    
    }
    
  • Изменить меню админки в файле конфигурации etc/config.xml
    <?xml version="1.0" ?>
    <config>
        ...
        <adminhtml>
            <menu>
                <dsnews module="dsnews">
                    <title>News</title>
                    <sort_order>77</sort_order>
                    <children>
                        <dsnews_news translate="title" module="dsnews">
                            <title>News</title>
                            <sort_order>10</sort_order>
                            <action>dsnews_admin/adminhtml_news</action>
                        </dsnews_news>
                        <dsnews_category translate="title" module="dsnews">
                            <title>Categories</title>
                            <sort_order>20</sort_order>
                            <action>dsnews_admin/adminhtml_category</action>
                        </dsnews_category>
                    </children>
                </dsnews>
            </menu>
        </adminhtml>
        ...
    </config>
    

Если при открытии сайта в браузере возникает надпись «Upgrade DS News to version 0.0.2», то значит всё типично — дозволено удалить две строчки из файла обновления и обновить окно с сайтом. В отвратном случае необходимо глядеть на версию модуля в таблице core_resource — для строки dsnews_setup значение версии должно быть 0.0.1.

Создание остальных моделей, источников, контроллера в админке для категорий остаётся как независимая работа. Для нетерпеливых, надобные метаморфозы имеются в директории начальных кодов для данного шага. Стоит обратить внимание на то, что при удалении категории будет протекать обнулениезначения категории для всякой из новостей.

Шаг 18. Отображение Data Grid во вкладке

Если на предыдущем шаге всё прошло типично, то сейчас дозволено приступить к отображению списка новостей во вкладке при редактировании категории

  1. Добавить в модель категории Model/Category.php способ приобретения всех новостей данной категории
    <?php
    
    class DS_News_Model_Category extends Mage_Core_Model_Abstract
    {
    
        protected function _construct(){ ... }
    
        protected function _afterDelete(){ ... }
    
        public function getNewsCollection()
        {
            $collection = Mage::getModel('dsnews/news')->getCollection();
            $collection->addFieldToFilter('category_id', $this->getId());
            return $collection;
        }
    
    }
    
  2. Добавить вкладку Block/Adminhtml/Category/Edit/Tabs/News.php
    <?php
    
    class DS_News_Block_Adminhtml_Category_Edit_Tabs_News extends Mage_Adminhtml_Block_Widget_Grid
    {
    
        public function __construct()
        {
            parent::__construct();
            $this->setId('categoryNewsGrid');
            $this->setUseAjax(true);
        }
    
        protected function _prepareCollection()
        {
            $collection = Mage::registry('current_category')->getNewsCollection();
            $this->setCollection($collection);
            return parent::_prepareCollection();
        }
    
        protected function _prepareColumns()
        {
    
            $helper = Mage::helper('dsnews');
    
            $this->addColumn('ajax_grid_news_id', array(
                'header' => $helper->__('News ID'),
                'index' => 'news_id',
                'width' => '100px',
            ));
    
            $this->addColumn('ajax_grid_title', array(
                'header' => $helper->__('Title'),
                'index' => 'title',
                'type' => 'text',
            ));
    
            $this->addColumn('ajax_grid_created', array(
                'header' => $helper->__('Created'),
                'index' => 'created',
                'type' => 'date',
            ));
    
            return parent::_prepareColumns();
        }
    
        public function getGridUrl()
        {
            return $this->getUrl('*/*/news', array('_current' => true));
        }
    
    }
    
  3. Добавить вкладку на форму редактирования категории Block/Adminhtml/Category/Edit/Tabs.php
    <?php
    
    class DS_News_Block_Adminhtml_Category_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs
    {
    
        public function __construct(){ ... }
    
        protected function _prepareLayout()
        {
            $helper = Mage::helper('dsnews');
    
            $this->addTab('general_section', array(
                'label' => $helper->__('General Information'),
                'title' => $helper->__('General Information'),
                'content' => $this->getLayout()->createBlock('dsnews/adminhtml_category_edit_tabs_general')->toHtml(),
            ));
            $this->addTab('news_section', array(
                'class' => 'ajax',
                'label' => $helper->__('News'),
                'title' => $helper->__('News'),
                'url' => $this->getUrl('*/*/news', array('_current' => true)),
            ));
    
            return parent::_prepareLayout();
        }
    
    }
    
  4. Добавить в контроллер категории controllers/Adminhtml/CategoryController.php действие, которое будет генерировать список новостей для нынешней категории
    <?php
    
    class DS_News_Adminhtml_CategoryController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction(){ ... }
    
        public function newAction(){ ... }
    
        public function editAction(){ ... }
    
        public function saveAction(){ ... }
    
        public function deleteAction(){ ... }
    
        public function newsAction()
        {
            $id = (int) $this->getRequest()->getParam('id');
            $model = Mage::getModel('dsnews/category')->load($id);
            Mage::register('current_category', $model);
    
            if (Mage::app()->getRequest()->isAjax()) {
                $this->loadLayout();
                echo $this->getLayout()->createBlock('dsnews/adminhtml_category_edit_tabs_news')->toHtml();
            }
        }
    
    }
    

В итоге при открытии формы редактирования категории, дозволено будет увидеть все новости данной категории в новой вкладке «News». Для новой категории в данной вкладке будут показываться новости, не имеющие категории. Если данное поведение не устраивает, то при добавлении новой категории, вкладку новостей дозволено не показывать, немножко подкорректировав блок табов в пункте 3:

<?php

class DS_News_Block_Adminhtml_Category_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs
{

    public function __construct(){ ... }

    protected function _prepareLayout()
    {
        $helper = Mage::helper('dsnews');
        $category = Mage::registry('current_category');

        $this->addTab('general_section', array(
            'label' => $helper->__('General Information'),
            'title' => $helper->__('General Information'),
            'content' => $this->getLayout()->createBlock('dsnews/adminhtml_category_edit_tabs_general')->toHtml(),
        ));
        if($category->getId()){
            $this->addTab('news_section', array(
                'class' => 'ajax',
                'label' => $helper->__('News'),
                'title' => $helper->__('News'),
                'url' => $this->getUrl('*/*/news', array('_current' => true)),
            ));
        }

        return parent::_prepareLayout();
    }

}

Следует иметь в виду, что айди, добавляемый для грида в пункте 2 $this->setId(‘categoryNewsGrid’), будет применяться для образования имени JavaScript переменной, следственно в айди грида дозволено применять только символы, разрешённые для наименования переменных в JavaScript (скажем, невозможно применять символ дефиса).

Также следует обратить внимание на префикс ajax_grid_ у всех полей грида в пункте 2. Так как аякс грид добавляется на форму редактирования, необходимо Дабы имена полей фильтров грида не совпадали с наименованиями полей на форме, напротив при сохранении категории значение поля фильтра может перезатереть значение поля формы.

При добавлении вкладки списка, применяется механизм аякс-приобретения данных, для чего вкладке добавляется класс ajax (данный класс непременен) и ссылка на контроллер/действие, которое будет возвращать список на аякс запрос. Эта же ссылка применяется в способе getGridUrl в классе грида для фильтрации данных аякс-запросом. При этом в параметре ссылки применяется значение array(‘_current’ => true), которое говорит, что при образовании ссылки нужно оставить нынешние параметры, таким образом сохраняется ссылка id категории при аякс запросе.

Шаг 19. Сохранение выбранных в Data Grid записей

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

  1. Изменить файл Block/Adminhtml/Category/Edit/Tabs/News.php
    <?php
    
    class DS_News_Block_Adminhtml_Category_Edit_Tabs_News extends Mage_Adminhtml_Block_Widget_Grid
    {
    
        public function __construct()
        {
            parent::__construct();
            $this->setDefaultFilter(array('ajax_grid_in_category' => 1));
            $this->setId('categoryNewsGrid');
            $this->setSaveParametersInSession(false);
            $this->setUseAjax(true);
        }
    
        protected function _prepareCollection()
        {
            $collection = Mage::getModel('dsnews/news')->getCollection();
            $this->setCollection($collection);
            return parent::_prepareCollection();
        }
    
        protected function _prepareColumns()
        {
            $helper = Mage::helper('dsnews');
    
            $this->addColumn('ajax_grid_in_category', array(
                'align' => 'center',
                'header_css_class' => 'a-center',
                'index' => 'news_id',
                'type' => 'checkbox',
                'values' => $this->getSelectedNews(),
            ));
    
            $this->addColumn('ajax_grid_news_id', array(
                'header' => $helper->__('News ID'),
                'index' => 'news_id',
                'width' => '100px',
            ));
    
            $this->addColumn('ajax_grid_title', array(
                'header' => $helper->__('Title'),
                'index' => 'title',
                'type' => 'text',
            ));
    
            $this->addColumn('ajax_grid_created', array(
                'header' => $helper->__('Created'),
                'index' => 'created',
                'type' => 'date',
            ));
    
            return parent::_prepareColumns();
        }
    
        protected function _addColumnFilterToCollection($column)
        {
            if ($column->getId() == 'ajax_grid_in_category') {
                $collection = $this->getCollection();
                $selectedNews = $this->getSelectedNews();
                if ($column->getFilter()->getValue()) {
                    $collection->addFieldToFilter('news_id', array('in' => $selectedNews));
                } elseif (!empty($selectedNews)) {
                    $collection->addFieldToFilter('news_id', array('nin' => $selectedNews));
                }
            } else {
                parent::_addColumnFilterToCollection($column);
            }
            return $this;
        }
    
        public function getGridUrl()
        {
            return $this->getUrl('*/*/news', array('_current' => true, 'grid_only' => 1));
        }
    
        public function getSelectedNews()
        {
            if (!isset($this->_data['selected_news'])) {
                $selectedNews = Mage::app()->getRequest()->getParam('selected_news', null);
                if(is_null($selectedNews) || !is_array($selectedNews)){
                    $category = Mage::registry('current_category');
                    $selectedNews = $category->getNewsCollection()->getAllIds();
                }
                $this->_data['selected_news'] = $selectedNews;
            }
            return $this->_data['selected_news'];
        }
    
    }
    
  2. Изменить контроллер controllers/Adminhtml/CategoryController.php
    <?php
    
    class DS_News_Adminhtml_CategoryController extends Mage_Adminhtml_Controller_Action
    {
    
        public function indexAction(){ ... }
    
        public function newAction(){ ... }
    
        public function editAction(){ ... }
    
        public function saveAction()
        {
            $id = $this->getRequest()->getParam('id');
            if ($data = $this->getRequest()->getPost()) {
                try {
                    $helper = Mage::helper('dsnews');
                    $model = Mage::getModel('dsnews/category');
    
                    $model->setData($data)->setId($id);
                    $model->save();
    
                    $categoryId = $model->getId();
                    $categoryNews = $model->getNewsCollection()->getAllIds();
                    if ($selectedNews = $this->getRequest()->getParam('selected_news', null)) {
                        $selectedNews = Mage::helper('adminhtml/js')->decodeGridSerializedInput($selectedNews);
                    } else {
                        $selectedNews = array();
                    }
    
                    $setCategory = array_diff($selectedNews, $categoryNews);
                    $unsetCategory = array_diff($categoryNews, $selectedNews);
    
                    foreach($setCategory as $id){
                        Mage::getModel('dsnews/news')->setId($id)->setCategoryId($categoryId)->save();
                    }
                    foreach($unsetCategory as $id){
                        Mage::getModel('dsnews/news')->setId($id)->setCategoryId(0)->save();
                    }
    
                    Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Category was saved successfully'));
                    Mage::getSingleton('adminhtml/session')->setFormData(false);
                    $this->_redirect('*/*/');
                } catch (Exception $e) {
                    Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
                    Mage::getSingleton('adminhtml/session')->setFormData($data);
                    $this->_redirect('*/*/edit', array(
                        'id' => $id
                    ));
                }
                return;
            }
            Mage::getSingleton('adminhtml/session')->addError($this->__('Unable to find item to save'));
            $this->_redirect('*/*/');
        }
    
        public function deleteAction(){ ... }
    
        public function newsAction()
        {
            $id = (int) $this->getRequest()->getParam('id');
            $model = Mage::getModel('dsnews/category')->load($id);
            $request = Mage::app()->getRequest();
    
            Mage::register('current_category', $model);
    
            if ($request->isAjax()) {
    
                $this->loadLayout();
                $layout = $this->getLayout();
    
                $root = $layout->createBlock('core/text_list', 'root', array('output' => 'toHtml'));
    
                $grid = $layout->createBlock('dsnews/adminhtml_category_edit_tabs_news');
                $root->append($grid);
    
                if (!$request->getParam('grid_only')) {
                    $serializer = $layout->createBlock('adminhtml/widget_grid_serializer');
                    $serializer->initSerializerBlock($grid, 'getSelectedNews', 'selected_news', 'selected_news');
                    $root->append($serializer);
                }
    
                $this->renderLayout();
            }
        }
    
    }
    

Как видно из пункта 1, впереди был добавлен ещё одна колонка ajax_grid_in_category, которая будет отвечать за выделение/снятие выделения с новостей, а также за фильтрацию выделенных новостей (новости нынешней категории/других категорий/все новости). По этой причине сейчас коллекция новостей загружается всецело в способе _prepareCollection, а фильтрация происходит в способе _addColumnFilterToCollection. Код $this->setDefaultFilter(array(‘ajax_grid_in_category’ => 1)); включает фильтрацию по умолчанию (Дабы показывались только новости нынешней категории). Функция getSelectedNews возвращает массив айди выделенных новостей, если имеются. Если не имеются, то сначала пытается возвратиться массив данных, пришедших POST-ом (если пользователь менял выделение), а после этого — айди новостей нынешней категории.

Контроллер тоже претерпевает довольно изменений. Исключительно это касается способа загрузки новостей. Строка $root = $layout->createBlock(…) перезатирает стержневой блок образца root, убирая все непотребные вложенные блоки. После этого создаётся блок грида, и, если запрос пришёл в 1-й раз, то ещё и блок для сериализации данных. Позже этого происходит генерация каждого образца.

При вызове функции initSerializerBlock блока сериализации, происходит привязка блока к гриду. Данная функция принимает 4 параметра: блок либо имя грида в образце, имя способа блока для приобретения выделенных элементов, имя спрятанного инпута (это имя применяется в контроллере при сохранении), и последним параметром идём наименование поля, которое применяется в способе getSelectedNews в блоке грида для приобретения изменений выделения новостей.

Шаг 20. Применение прекрасных URL

Нынешние новости выводятся на сайте достаточно уродливо — это поправимо, если есть познания HTML/CSS и немножко вкуса дизайнера. Другое дело — ссылки. Ссылки обязаны быть прекрасными, в них должен отображаться заголовок новости. Для этого сотворим свой роутер. Впрочем перед тем, как создавать свой роутер, необходимо обновить версию модуля, добавив новостям поле для ссылки.

  1. Сделать файл обновления sql/dsnews_setup/upgrade-0.0.2-0.0.3.php
    <?php
    
    echo '<h1>Upgrade DS News to version 0.0.3</h1>';
    exit;
    
    $installer = $this;
    $tableNews = $installer->getTable('dsnews/table_news');
    
    $installer->run("ALTER TABLE {$tableNews}
            ADD COLUMN `link` VARCHAR(255) AFTER `title`;");
    $installer->run("ALTER TABLE {$tableNews}
            ADD UNIQUE KEY (`link`);");
    
    foreach (Mage::getModel('dsnews/news')->getCollection() as $news) {
        try {
            $news->load($news->getId())->setDataChanges(true)->save();
        } catch (Exception $e) {
            $news->setId($news->getId())->setLink($news->getId())->save();
        }
    }
    
    $installer->endSetup();
    
  2. Изменить файл конфигурации модуля etc/config.xml
    <?xml version="1.0" ?>
    <config>
        <modules>
            <DS_News>
                <version>0.0.3</version>
            </DS_News>
        </modules>
        ...
    </config>
    
  3. Изменить хелпер Helper/Data.php
    <?php
    class DS_News_Helper_Data extends Mage_Core_Helper_Abstract
    {
    
        public function getImagePath($id = 0){ ... }
    
        public function getImageUrl($id = 0){ ... }
    
        public function getCategoriesList(){ ... }
    
        public function getCategoriesOptions(){ ... }
    
        public function prepareUrl($url)
        {
            return trim(preg_replace('/- /', '-', preg_replace('/[^a-z0-9]/sUi', '-', strtolower(trim($url)))), '-');
        }
    
    }
    
  4. Изменить модель новости Model/News.php
    <?php
    
    class DS_News_Model_News extends Mage_Core_Model_Abstract
    {
    
        protected function _construct(){ ... }
    
        protected function _afterDelete(){ ... }
    
        protected function _beforeSave()
        {
            $helper = Mage::helper('dsnews');
    
            if (!$this->getData('link')) {
                $this->setData('link', $helper->prepareUrl($this->getTitle()));
            } else {
                $this->setData('link', $helper->prepareUrl($this->getData('link')));
            }
            return parent::_beforeSave();
        }
    
        public function getImageUrl(){ ... }
    
    }
    
  5. Добавить поле ссылки на форму редактирования новости Block/Adminhtml/News/Edit/Tabs/General.php
    <?php
    
    class DS_News_Block_Adminhtml_News_Edit_Tabs_General extends Mage_Adminhtml_Block_Widget_Form
    {
    
        protected function _prepareForm()
        {
            ...
    
            $fieldset->addField('link', 'text', array(
                'label' => $helper->__('Link'),
                'name' => 'link',
            ));
    
            ...
        }
    }
    
  6. Изменить файл конфигурации модуля etc/config.xml
    <?xml version="1.0" ?>
    <config>
        ...
        <global>
            ...
            <events>
                <controller_front_init_routers>
                    <observers>
                        <dsnews>
                            <class>DS_News_Controller_Router</class>
                            <method>initControllerRouters</method>
                        </dsnews>
                    </observers>
                </controller_front_init_routers>
            </events>
            ...
        </global>
    </config>
    
  7. Сделать файл Controller/Router.php
    <?php
    
    class DS_News_Controller_Router extends Mage_Core_Controller_Varien_Router_Abstract
    {
    
        public function initControllerRouters($observer)
        {
            $front = $observer->getEvent()->getFront();
            $front->addRouter('dsnews', $this);
        }
    
        public function match(Zend_Controller_Request_Http $request)
        {
            $identifier = trim($request->getPathInfo(), '/');
            $cmd = explode('/', $identifier);
    
            if ($cmd[0] == 'news') {
                if (count($cmd) == 1) {
                    return $this->_fillRequest($request);
                } else {
                    $model = Mage::getModel('dsnews/news')->load($cmd[1], 'link');
                    if ($model->getId()) {
                        $params = array(
                            'id' => $model->getId()
                        );
                        return $this->_fillRequest($request, $params, 'index', 'view');
                    }
                }
            }
            return false;
        }
    
        protected function _fillRequest($request, $cmd = array(), $controller = 'index', $action = 'index')
        {
            $request->setModuleName('news')
                    ->setControllerName($controller)
                    ->setActionName($action)
                    ->setParam('is_routed', 1);
            if (is_array($cmd) && count($cmd)) {
                foreach ($cmd as $key => $value) {
                    $request->setParam($key, $value);
                }
            }
    
            $request->setAlias(Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, $request->getPathInfo());
            return true;
        }
    
    }
    

Если при обновлении окна сайта в браузере видна надпись Upgrade DS News to version 0.0.3, значит всё типично — дозволено закомментировать надпись, и установить обновление. Пункты с 1 по 5 — это обновление модуля: добавление поля ссылки для новостей. Сам роутер создаётся в пункте 7, а в пункте 6 прописывается на событие инициализации роутеров сайта. Сейчас на страницу новости дозволено перейти по ссылкеhttp://site.com/news/{news-link}

По большей части данный код достаточно примитивен. В файле конфигурации создаётся слушатель для события инициализации роутеров controller_front_init_routers, во время которого к списку обработчиков добавляется объект самого роутера $this$front->addRouter(‘dsnews’, $this). Данный объект должен наследоваться от класса Mage_Core_Controller_Varien_Router_Abstract, в котором будет вызываться способmatch. Для того, Дабы не создавать лишнюю модель обсервера, слушатель события реализуется в том же самом объекте. Каждая основная логика работы происходит в способе match, тот, что занимается разбором URL. Здесь дозволено делать всё, что желательно. Если данный способ возвращает true, то последующая обработка прекращается, возвращение значения false ведёт к продолжению обработки другими роутерами. Способ _fillRequest — это легко вспомогательный код, вынесенный отдельно для сокращения всеобщего объёма кода.

Помощь крассивых ссылок для категорий, а также ссылки на новости с учётом категорий, остаётся как независимая работа.

Исходные коды для всякого шага доступны здесь

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB