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

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

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

Принцип


Смысл в том, что нам нужно создать свой собственный класс, который будет наследовать исходный класс и заменять его методы.
Для примера заменим некоторые методы 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
31
4 528
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 быть?
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.