Как не хакать сторонние классы

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

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

Принцип


Смысл в том, что нам нужно создать свой собственный класс, который будет наследовать исходный класс и заменять его методы.
Для примера заменим некоторые методы miniShop. Создаем файл /core/components/minishop/model/minishopext.class.php:
//загружаем код класса miniShop
require_once 'minishop.class.php';

// extends - это и есть расширение/наследование
class miniShopExt extends miniShop {
    // Тут мы инициализируем родительский класс
    function __construct(modX &$modx,array $config = array()) {
    	return parent::__construct($modx, $config);
    }
    
    // Если назвать метод, как в родительском класе - это будет замена
    // Если нет - добавление нового метода
    function addToCart($id, $num = 1, $data = array()) {
        // Копируем сюда исходный метод и меняем в нем, что нам нужно
    }
    function remFromCart($key) {
        // И тут. Можно хоть весь класс переписать заново.
    }
    function newMethod() {
        // А это новый метод, пишем тут свой код
    }
Мы подключили родительский класс, но не инициализировали. Запускать его мы будем через свой новый класс — miniShopExt.

Значит, нам нужен теперь сниппет, для запуска нового класса. Можно просто переименовать сниппет miniShop в miniShopExt, или скопировать.
Теперь меняем в новом сниппете инициализацию класса с miniShop на miniShopExt:
if (!isset($modx->miniShop) || !is_object($modx->miniShop)) {
  $modx->miniShop = $modx->getService('minishopext','miniShopExt', $modx->getOption('core_path').'components/minishop/model/minishop/', $scriptProperties);
  if (!($modx->miniShop instanceof miniShopExt)) return '';
}
Видите, мы запускаем класс miniShopExt из файла minishopext(.class.php) и сохраняем его в переменную $modx->miniShop, чтобы не переписывать весь сниппет.

miniShopExt унаследовал все методы от miniShop, кроме тех, которые прописаны в нем самом. Теперь нужно только заменить вызов сниппета [[!miniShop]] в корзине на [[!miniShopExt]].

Заключение


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

Это вообще очень хорошая практика: не хакать чужой код, а наследовать и расширять. В любой момент вы можете инициализировать оригинальный класс, или свой, расширенный.
Василий Наумкин
30 июня 2012, 12:21
modx.pro
33
5 073
0
Поблагодарить автора Отправить деньги

Комментарии: 28

Иван Брежнев
30 июня 2012, 17:37
0
Класс) То что я искал!
Мегареспект
    Иван Брежнев
    01 июля 2012, 00:52
    0
    Почему вот такая конструкция не работает в классе miniShopExt

    $this->config['tplPaymentRow'] и аналогичные
      Иван Брежнев
      01 июля 2012, 02:01
      0
      Разобрался, эти настройки берутся из сниппета, а у меня все аяксовые запросы выполняются через свой сниппет у которого никаких настроек не прописано.

      Сделал вот так в расширении класса miniShopExt. Работает. Если это не правильно поправьте

      function __construct(modX &$modx, array $config = array()) {
      $snippet = $modx->getObject('modSnippet',array('name'=>'miniShop'));
      $config = array_merge($snippet->getProperties(),$config);
      parent::__construct($modx, $config);
      }
        Василий Наумкин
        01 июля 2012, 03:47
        0
        Лично я переименовываю miniShop в miniShopExt, меняю вызов класса и дописываю нужные методы, а все параметры остаются.

        А ваш метод ничем не хуже, главное, чтобы сниппет miniShop никуда не делся, иначе будет fatal error.
        Ну и минимум на пару операций с БД выходит больше (достать сниппет, а потом его параметры).
          Иван Брежнев
          01 июля 2012, 06:52
          0
          А после обновления оригинального класса придется менять и измененный класс?
            Василий Наумкин
            01 июля 2012, 06:55
            0
            Нет, конечно — в этом и смысл.
              Иван Брежнев
              01 июля 2012, 06:57
              0
              Тогда я не пойму что именно вы делаете? вы спиннете переименовываете или название класса в minishop.class.php?
                Василий Наумкин
                01 июля 2012, 07:00
                0
                Я переименовываю сниппет.
                Я подключаю в переименованном сниппете расширенный класс.
                Тот в свою очередь запускает оригинальный класс.

                При обновлении перезаписываются оригинальный сниппет и оригинальный класс, а расширенные — нет.

                Оригинальный сниппет больше вообще не используется, только расширенный — после каждого обновления его можно просто удалять.
          Иван Брежнев
          01 июля 2012, 06:54
          0
          Глянул что в базе данных настройки сниппетов хранятся в сериализованном виде, можно их и одним запросом достать, и не будет объекта modSnippet. Но тут получается зачем нам тогда API MODX

          Хочется делать сразу более менее оптимально
          Иван Брежнев
          01 июля 2012, 07:03
          0
          Кстати задумался на счет двух операций с БД. Мне кажется что все таки создается объект modSnippet со всеми полями из БД. А уже метод getProperties работает с этим объектом, а не с БД.
          Или я гоню?
            Василий Наумкин
            01 июля 2012, 07:07
            0
            Да, скорее всего.
              Иван Брежнев
              01 июля 2012, 07:12
              0
              А есть ли метод чтобы узнать размер памяти занимаемый объектом?
                Василий Наумкин
                01 июля 2012, 07:16
                0
                memory_get_usage, она показывает память, занимаемую скриптом.

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

                php.net/manual/en/function.memory-get-usage.php
                  Иван Брежнев
                  01 июля 2012, 07:19
                  0
                  Вот спасибо. Буду экспериментировать))
                  Еще раз спасибо
      Иван Брежнев
      01 июля 2012, 07:29
      0
      Получается что вот это кусок кода
      // Получаем сниппет miniShop, если не можем — ошибка
      if (!$snippet = $modx->getObject('modSnippet',array('name'=>'cart'))) {return 'Error: can`t found miniShop snippet';}

      // Нам нужны параметры сниппета
      $scriptProperties = $snippet->getProperties();
      Кушает 650 кб
        Михаил
        13 февраля 2016, 11:59
        0
        Спасибо!
          Михаил
          13 февраля 2016, 22:32
          0
          Подскажите пожалуйста как расширить класс minishop2. В minishop2 нету сниппета minishop. Правильно ли я понимаю что нужно копировать все сниппеты minishop2(msOrder, msCart и прочие) и в них уже менять инициализацию класса с miniShop на свой, наследованный от miniShop?

          Сделал свой класс
          1. Создал core/components/minishop2/model/minishop2/minishop_my.class.php и там прописал класс

          <?php
          //загружаем код класса miniShop2
          require_once 'minishop2.class.php';
          
          // extends - это и есть расширение/наследование
          class miniShop_my extends miniShop2 {
              // Тут мы инициализируем родительский класс
              function __construct(modX &$modx,array $config = array()) {
              	return parent::__construct($modx, $config);
              }
          }
          2. Скопировал сниппеты msOrder и msCart в msCart_my и msOrder_my соответсвенно

          В новых сниппетах заменил

          $miniShop2 = $modx->getService('minishop2');
          на

          if (!isset($miniShop2) || !is_object($miniShop2)) {
            $miniShop2 = $modx->getService('minishop_my','miniShop_my', $modx->getOption('core_path').'components/minishop2/model/minishop2/', $scriptProperties);
            if (!($miniShop2 instanceof miniShop_my)) return '';
          }
          И заменил в корзине вызовы сниппетов msCart и msOrder на свои msCart_my и msOrder_my

          [[!msCart_my? 
              &tplOuter = `tpl.msCart.outer_my`
              &tplRow = `tpl.msCart.row_my`
          ]]
          [[!msOrder_my?
              &tplOuter=`tpl.msOrder.outer_my`
              &tplSuccess=`tpl.msOrder.success_my`
          ]]
          Но Корзина выдает «Ошибка сервера 500»
        Andrei D.
        17 сентября 2016, 17:24
        0
        одна из самых полезных статей на ресурсе… почему я раньше ее не видел(
          Кирилл Киселев
          22 октября 2018, 12:48
          0
          А как расширить скрипт на фронте дополнения?
            Дмитрий
            22 октября 2018, 13:10
            0
            Как правило, у большинства дополнений есть системная настройка с адресом до JS-файла на фронте. Меняете ее, создаете копию файла и изменяете его как душе угодно.
              Кирилл Киселев
              22 октября 2018, 13:20
              0
              Я имею ввиду, сделать копию компонента. Основное дополнение свою работу выполняет, а копия выполняет другую работу. Если в php есть extends, то как с js быть?
            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
            28