插件升级:小部件设置


9

我尝试对此进行一些研究,但尚未发现任何可靠的方法。我有一个正在使用的插件,在上一个版本和新版本之间,我们对小部件进行了一些更新,以更改某些设置名称(在后端),并且在创建升级例程来执行此操作时遇到了麻烦。

到目前为止,我所做的似乎(大部分)工作是这样的:

$widget = get_option( 'widget_name' );

if( is_array( $widget ) && ! empty( $widget ) ) {
    foreach( $widget as $a => $b ) {
        if( ! is_array( $b ) ) {
            continue;
        } 

        foreach( $b as $k => $v ) {
            $widget[$a]['setting1'] = $widget[$a]['oldsetting1'];
            $widget[$a]['setting2'] = $widget[$a]['oldsetting2'];
        }
    }

    update_option( 'widget_name', $widget );
}

在我的大多数测试中,都可以,但是问题是旧的窗口小部件不再显示其输出。仅显示小部件的标题。我可以通过保存每个单独的小部件来解决此问题,然后它可以正常工作,但是我不想让我的用户这样做。

我认为这样可能有效:

$settings = $widgets->get_settings();

foreach( $settings as $s ) {

    $s['setting1'] = $s['oldsetting1'];
    $s['setting2'] = $s['oldsetting2'];

    $widgets->save_settings( $s );

}

但是似乎save_settings()调用一定是错误的,因为这会完全删除小部件。

我在为类似的事情找到任何标准时遇到麻烦,并且想听听您可能要做的类似事情,想法或链接。

在此先感谢您的帮助。

编辑:

这实际上不是有关跟踪未在WP repo上托管的许可证密钥或升级插件的问题。更重要的是,当用户升级时,将在插件的两个版本之间更新设置。

例:

1.0.0版有一个设置字段 name

在1.1.0版中,我们决定需要名字和姓氏,因此我们将旧设置更改为“ be” first_name,然后添加新设置last_name

如果将这些选项保存为自定义帖子类型的帖子元,则转移这些选项没有问题:

$old_name = get_post_meta( $post->ID, 'name', true );
$first_name = update_post_meta ( $post->ID, 'first_name', true );
delete_post_meta( $post->ID, 'name' );

因此,这部分很容易。我遇到的麻烦似乎并不容易,但是对于WIDGET设置却要做同样的事情。

希望这可以消除任何混乱,并有助于使其更易于回答。

编辑2:

echo '<pre>' . print_r( $widget, true ) . '</pre>';上面第一个代码块的结果:

Array
(
[2] => Array
    (
        [title] => Class Schedule
        [id] => 23
        [display_type] => grid
        [order] => asc
        [display_title_text] => Events on
        [paging] => 1
        [list_max_num] => 7
        [list_max_length] => days
        [list_start_offset_num] => 0
        [list_start_offset_direction] => back
        [gce_per_page_num] => 7
        [gce_events_per_page] => days
    )

[3] => Array
    (
        [title] => Examples
        [id] => 24
        [display_type] => grid
        [order] => asc
        [display_title_text] => Events on
        [paging] => 1
        [list_max_num] => 7
        [list_max_length] => days
        [list_start_offset_num] => 0
        [list_start_offset_direction] => back
        [gce_per_page_num] => 7
        [gce_events_per_page] => days
    )

[_multiwidget] => 1
)

我今天刚刚在Tutsplus上看到了这篇文章,我什至都没看完,但这似乎是你的盟友。创建许可证控制的主题和插件更新系统
OnethingSimple 2015年

@OnethingSimple感谢您的答复,但这与我想要的完全不一样。我将更新问题以使其更加清楚。
Nick Young

我们有可能会转储您正在阅读的小部件设置结构的外观(即使您必须更改某些值)。这可能有助于弄清问题所在。例如echo“ <pre>”。print_r($ widget,true)。“ </ pre>”;
私有者

@Privateer现在附加到OP的底部。
尼克·杨

Answers:


3

我对更改选项进行了快速测试,它似乎可以正常工作。

我所做的是:

  1. 编写了一个只有2个字段的小部件:“标题”和“名称”。将此小部件的多个实例添加到我的侧边栏。确保它们在前端正确显示。
  2. 编辑该类以使用3个字段:“标题”和“名字”(以替换“名称”),并添加了“姓氏”。
  3. 编辑了注册小部件'widgets_init'的函数,以调用更新小部件选项的函数:

    add_action( 'widgets_init', 'my_example_widget_register' );
    
    function my_example_widget_register() {
    
      $widget_name = 'my_example_widget';  // <-- You will probably replace this
    
      $options = get_option("widget_{$widget_name}");
    
      // if the widget is not updated, run a function that updates it
      if ($options && ! get_option("is_{$widget_name}_updated")) {
          // use class below to update options
          $updater = new MyExampleWidgetUpdater($widget_name, $options);
          $updater->update();
      }
    
      register_widget('My_Example_Widget'); // <-- You will probably replace this
    }
  4. 编写了一个简单的类来更新小部件选项:

    class MyExampleWidgetUpdater
    {
    
      private $name;
      private $options;
    
      public function __construct($name, $options) {
         $this->name = $name;
         $this->options = $options;
      }
    
      public function update() {
        // loop all the options
        array_walk($this->options, function(&$option, $key) {
            if (is_array($option) && is_numeric($key)) {
              $option = $this->getOption($option);
            }
        });
        // update all options in DB
        update_option("widget_{$this->name}", $this->options);
        // set the widget as updated
        update_option("is_{$this->name}_updated", 1);
      }
    
      private function getOption($options) {
        if (!isset($options['name'])) {
           return $options;
        }
        $options['first_name'] = $options['name'];
        $options['last_name'] = '';
        unset($options['name']);
        return $options;
      }
    }
  5. 我编辑了窗口小部件类以将选项保存"is_{$widget_name}_updated"update()的方法,用这种方法更新类将永远不会被调用为新用户从未安装旧插件

    class My_Example_Widget {
    
        ...
    
        public function update($new_instance, $old_instance) {
            ...
    
            $widget_name = 'my_example_widget';
            update_option("is_{$widget_name}_updated", 1);
        }
    }
  6. 我访问了我的网站,使用新选项显示的旧选项保存的小部件没有问题。(当然,“姓氏”始终为空)。

一个好主意是用"is_{$widget_name}_updated"存储小部件实际版本的选项替换该选项,这样,下次需要更新时,它将很方便。


因此,浏览您的答案而不进行测试。似乎与我使用update_option正确的方法相似?我现在想知道钩子是否重要?我把它init钩了。将其添加到widgets_init钩子中有巨大的区别吗?我很确定他们会同时开火。谢谢你的帮助。
尼克·扬

@NickYoung钩子没有区别。但是在您的第一个代码段(与我的代码段相似)中,第二个(内部)foreach错误。
gmazzap

我只是有机会实现您编写的代码。效果很好,但我仍然遇到与以前相同的问题。它会成功传输选项,但是在运行插件的升级例程后,小部件将停止输出其主HTML,并且仅显示小部件的标题。如果我转到小部件设置,只需单击保存按钮,它就会再次弹出。有什么想法吗?
尼克·杨

添加到我的最后评论。就像选项已正确更新,但实际实例不是。那有可能吗?
尼克·杨

Nope禁用了所有缓存。还对2个具有相同问题的不同主机进行了测试。
尼克·杨

1

只是为了从另一个角度考虑问题-无需在插件更新时自动升级所有设置,只需检查“旧”设置并即时映射到“新”设置即可:

function widget( $args, $instance ) {
    if ( isset( $instance['old_setting'] ) )
         $instance = self::_version_compat( $instance );
}

static function _version_compat( $instance ) {
    $instance['new_setting'] = $instance['old_setting'];
    // etc.

    return $instance;
}

0

在我的头顶上,小部件的每个实例都被赋予某种唯一的ID。我想说的是成为小部件键的前缀。

我记得前一段时间在戳戳,但不记得确切的信息,对不起。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.