Как я Magento изменял, или Меняем базовый функционал на простом примере

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

    Давайте рассмотрим конкретный пример. В большинстве магазинов копейки сейчас никто не использует, но по умолчанию — благодаря локали — они отображаются. Встала задача их убрать. Как выяснилось, можно сделать это простым редактированием компонента библиотеки Zend, но, во-первых, это противоречит первому предложению данного поста, а, во-вторых, мы же не ищем легких путей. :)



    Итак, нам необходимо сделать следующее:
    — переопределить класс, отвечающий за вывод форматированной цены;
    — создать модуль, который будет этот класс содержать;
    — сконфигурировать модуль так, чтобы переопределенный класс вызывался в случаях, при которых ранее активировался оригинальный класс;
    — активировать новый модуль в системе.

    Все приведенные пути относительны директории, в которую установлен Magento.

    Как я уже говорил, Magento представляет хорошие возможности для разработки собственных модулей и расширения базового функционала. Основная часть рабочего кода системы (за исключением библиотек и фреймворков, на основе которых он написан) расположена в директории app. Если мы заглянем внутрь, то увидим там следующее содержимое:

    app
    |----- Mage.php
    |----- code
    |----- design
    |----- etc
    |----- locale

    Mage.php — модуль, описывающий основной класс-хаб системы — Mage
    code — весь код
    design — как видно из названия, здесь располагаются описания дизайна: именно логика и шаблоны вывода блоков; описания непосредственно css-стилей, скрипты и картинки вынесены в отдельное место
    etc — конфигурационные файлы
    locale — базовые языковые файлы, или, другими словами, локализация вывода; под конкретный интерфейс локализованный вывод так же может быть частично или полностью переопределен в описаниях собственного интерфейса в поддиректориях design.

    Для данной задачи нас интересует директория code. В ней мы видим три стандартные папки: core — содержит код основных модулей системы, а так же community и local, которые изначально пусты и предназначены для установки сторонних модулей (community) либо для разработанных самостоятельно (local). На самом деле различий между папкими никаких нет, но для удобства собственные модули будем складывать в папку local.

    Итак, для начала нам нужно создать папку, которая будет содержать наши модули. Название этой папки будет являться названием пакета (package) модулей, которые мы разработам. Т.к. я разрабатывал модули под свой проект, папка у меня носит имя проекта — Cifrum.

    # pwd
    /<Path To Magento>/app/code
    # ll -a
    total 10
    drwxrwxr-x 5 vlad www 512 18 апр 09:37 .
    drwxrwxr-x 6 vlad www 512 29 апр 19:30 ..
    drwxrwxr-x 3 vlad www 512 29 апр 19:30 community
    drwxrwxr-x 4 vlad www 512 18 апр 09:37 core
    drwxrwxr-x 3 vlad www 512 27 апр 02:37 local
    # ll -a local
    total 6
    drwxrwxr-x 3 vlad www 512 27 апр 02:37 .
    drwxrwxr-x 5 vlad www 512 18 апр 09:37 ..
    drwxrwxr-x 6 vlad www 512 29 апр 23:48 Cifrum


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

    Создайте следующую структуру директорий:

    # ll -1aR /<Path To Magento>/app/code/local/
    .
    ..
    Cifrum

    /<Path To Magento>/app/code/local/Cifrum:
    .
    ..
    CoreC

    /<Path To Magento>/app/code/local/Cifrum/CoreC:
    .
    ..
    Block
    Helper
    Model
    controllers
    etc
    sql


    Приведенный в конце список папок — стандартная структура модуля. Я не буду здесь подробно останавливаться на описании, если необходимо, вы можете ознакомиться с книгой php|architect’s Guide to Programming with Magento.

    В данном случае нас будут интересовать две директории — это etc, в которой мы расположим файл config.xml, и Model, где и будет располагаться описание нашего класса.

    Выводом форматированной цены занимается модуль Core, а конкректо класс Store, метод formatPrice(). Нам необходимо создать новый класс — наследник Mage_Core_Model_Store и переопределить его метод.

    Давайте создадим файл Model/Store.php со следующим содержимым:

    <?php

    /*****

     Trying to rewrite Core_Model_Store

    */

    // Описываем новый класс, наследующий стандартный класс, контролирующий работу с ценой
    // app/code/core/Mage/Core/Model/Store.php

    class Cifrum_CoreC_Model_Store extends Mage_Core_Model_Store
    {
      

     /**
     *
     * formatPrice without decimals, for rubles only for right now
     *
     */

     // Переопределяем функцию, форматирующую вывод

     public function formatPrice($price, $includeContainer = true)
     {
      if($this->getCurrentCurrency()) {
       $priceReturn = $this->getCurrentCurrency()->format($price, array(), $includeContainer);
        
       //Not the cleanest method but the fastest for now…
       if(preg_match('/руб/i', $priceReturn)) {
        return $this->getCurrentCurrency()->format($price, array('precision' => 0), $includeContainer);
       } else {
        return $priceReturn;
       }
      }

      return $price;
     }
      
    }

    ?>


    * This source code was highlighted with Source Code Highlighter.



    Как видно, мы берем код стандартной фукнции Mage_Core_Model_Store::formatPrice() и дописываем проверку на вхождение в строку подстроки “руб”. Не уверен, что будет работать на всех локалях (быть может, где-то фигурирует просто “р"), однако у меня работает.

    Теперь нам необходимо указать, что же именно нужно делать с созданным нами классом. Для этого создаем etc/config.xml и наполняем его следующим:

    <?xml version="1.0"?>
    <config>
     <modules>

      <!-- Описание нашего модуля - название, версия, зависимости -->
      <Cifrum_CoreC>
       <version>0.0.1</version>
       <depends>
        <!-- no dependencies -->
       </depends>
      </Cifrum_CoreC>
      <!-- -->

     </modules>
     <global>
      <models>

       <!-- а вот здесь мы переопределяем -->
       <!-- стандартный класс на наш с помощью тэга <rewrite> -->
       <core>
      <rewrite>
       <store>Cifrum_CoreC_Model_Store</store>
      </rewrite>
       </core>
       <!-- -->

      </models>
      <resources></resources>
       <blocks></blocks>
      <corec>
       <!-- config values -->
      </corec>
     </global>
     <adminhtml>
      <menu></menu>
      <acl></acl>
      <events></events>
      <translate></translate>
     </adminhtml>
     <frontend>
      <routers></routers>
      <events></events>
      <translate></translate>
      <layout></layout>
     </frontend>
     <default>
      <config_vars>
       <!-- config values -->
      </config_vars>
     </default>
    </config>


    * This source code was highlighted with Source Code Highlighter.



    Опять-таки, структура файла стандартная, нам нужно не все. Важные моменты я выделил комментариями. Вначале описываем название и версию нашего модуля, а ниже переопределяем с помощью тега <rewrite> системный вызов класса store модуля core.

    Однако это не все. Нам нужно указать системе, что у нас есть новый модуль, активировать его.
    Как вы помните, системные конфиги лежат в app/code/etc. Создаем и открываем файл app/etc/modules/Cifrum_All.xml, который в моем случае содержит описание всех модулей пакета Cifrum.

    <?xml version="1.0"?>
    <config>
     <modules>
      <Cifrum_CoreC>
       <active>true</active>
       <codePool>local</codePool>
      </Cifrum_CoreC>
     </modules>
    </config>


    * This source code was highlighted with Source Code Highlighter.



    На этом, собственно, все. Обновив страницу магазина, мы увидим, что десятые части рублей исчезли.

    Если есть вопросы или уточнения — пишите, с удовольствием пообщаюсь.

    P.S. Прошу прощения за дикую смесь русских и английских комментариев — русские добавил по ходу написания статьи, для себя писал английские.
    Поделиться публикацией

    Похожие публикации

    Комментарии 4

      +2
      Наконец-то появился первый пост на хабре о модулях для магенты! Очень рад :)
      У меня несколько комментариев:
      — не всегда обязательно делать свой модуль, в вашем случае, например, достаточно продублировать структуру папок app/code/core/Mage/Core/Model/ в app/code/local/Mage/Core/Model/ — магента сама подключит модель
      — «На этом, собственно, все. Обновив страницу магазина...» — не забываем про кэш (в большинстве случаев приходится его чистить), у меня при разработке всегда выключен
      — если модуль содержит админовский функционал (конфиг, например), то при включении/выключении модуля необходимо перелогиниться админом, т.к. магента хранит в сессии параметры установленных модулей
      — очень некрасиво хардкодить «руб» в модель (как на счет кодировки вашего php-файла? utf/ansi? )
      — ну и название поста не совсем корректно, вы не изменяли магенту, вы ее расширяли (extends)
        +2
        Согласен со всем вышеприведенным.

        Про кэш, каюсь, забыл упомянуть — т.к. во время разработки он и у меня выключен.
        Про хардкодинг, опять же, не могу не согласиться, но другого решения не нашел и не смог придумать. Как вариант, можно вообще убрать проверку, но это плохо скажется на многовалютных магазинах. Кодировка — utf.
        О возможности простого дублирования структуры — не знал, спасибо!

        А вообще, основной целью было показать на элементарном примере то, насколько просто изменить базовую функциональность и дописать свою.
          +1
          порылся с валютами, нашел корректную замену вашему preg_match(руб)
          if ($this->getCurrentCurrencyCode() == 'RUB')… дальше по тексту…
          таким образом вы не будете привязаны к локализации
          соответственно, для белорусских рублей, например, код валюты BYR
            +1
            ну и еще более красивый вариант — повесить observer на евент core_locale_set_locale
            config.xml:
                <frontend>
                    <events>
                        <core_locale_set_locale>
                            <observers>
                                <mycompany_mymodule_locale_observer>
                                    <type>singleton</type>
                                    <class>mymodule/observer</class>
                                    <method>currencyPrecision</method>
                                </mycompany_mymodule_locale_observer>
                            </observers>
                        </core_locale_set_locale>
                    </events>
                </frontend>
            

            app/code/local/Mycompany/Mymodule/Model/Observer.php:
            class Mycompany_Mymodule_Model_Observer extends Mage_Core_Model_Abstract 
            {
                public function currencyPrecision(Varien_Event_Observer $observer)
                {
                    $observer->getLocale()->currency('RUB')->setFormat(array('precision'=>0));
                    $observer->getLocale()->currency('BYR')->setFormat(array('precision'=>0));
                }
            }
            

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое