こんにちは、山本です。

引き続き、プラグイン作成初級編の二回目となります。

前回の終わりに、フィルターも絡めてと書かせて頂きました。
また、前回の中でフィルターを幾度か使用してますが、解説していなかったのでそれも含めてということで、今回は画像サイズを増やすプラグインを作成してみましょう。

WordPressでは、画像をアップロードすると大、中、小で画像を切り出しますが、もっと多くのパターンで画像を切り出したい場合があります。

ですので、画像サイズを追加するプラグインを、以下の仕様で作成します。

  • プラグイン有効時(インストール)テーブルを作成する。
  • 自由に追加、編集、削除が出来るようにする。
  • 追加可能数は可変とする。
  • プラグイン無効時(アンインストール)に作成したテーブルを削除する。

フィルターについて

まずフィルターとは何か?といった疑問があるのではと思います。

ですので、そこから始めようと思うのですが、端的に説明するのが難しい為、他力にすがります。

はい、別サイトから引用です。

フィルタとは、濾過器、こし器、濾過する、こす、などの意味を持つ英単語。ITの分野では、一定の条件に基づいてデータなどを選別・加工・排除する機能を持ったソフトウェアやハードウェアのことを指す。
フィルタとは|filter|フィルター – 意味/解説/説明/定義 : IT用語辞典:

そうですか。。。

……、以下の順番で処理が行われているとします。

A -> B

ここで、AとBの間に処理を何らかの処理を追加したいと仮定します。
AかBの処理を直接、変更することでも可能ではあるのですが、今回はフィルターの説明なのでフィルター挟んでみます。

挟んだ時の処理が以下になります。

A -> A’ -> B

ここのA’がフィルターに当たるわけですね。

フィルターを処理の間に仕込む事により、特定の処理、値に介入することが出来ます。

これにより、AとBを変更することなく、求めた処理を行うことが可能になります。

とても便利な機能なのですが、よくよく考えてみるとなぜA’のフィルターをAとBとの間に挟めるのか、といった疑問が出てきます。

理由は単純で、AとBとの間にフィルターを追加出来るように事前に準備されているからです。

幸いにして、WordPressには後からフィルターを追加出来るように、各所に用意がされています。

では、実際に使用しながらどのようなものかを理解して頂ければと思います。

ベースとなるプラグインを用意する

新しく、プラグインのスケルトンを作成します。

//addImageSizePlugin.php
<?php
/*
Plugin Name: addImageSizePlugin
Plugin URI: http://www.adjust.ne.jp
Description: 画像サイズ追加プラグイン
Author: ADjust Co.,Ltd.
Author URI: http://www.adjust.ne.jp
Version: 1.0
*/

プラグイン有効時にテーブルを作成する

有効、無効時に処理を挟む

有効、無効時に処理を追加したいので、そのタイミングで動作するフィルターを確認します。

まずは、コアファイルでどのように定義されているかを確認します。

//wp-admin/includes/plugin.php

// 有効時
do_action( 'activate_' . $plugin, $network_wide );

// 無効時
do_action( 'deactivate_' . $plugin, $network_deactivating );

プラグインの有効、無効それぞれで「do_action」が使用されています。
これは、フィルターフックを作成する関数です。

関数リファレンス/do action – WordPress Codex 日本語版

「$plugin」の中は、今回のプラグインでは「addImageSizePlugin/addImageSizePlugin.php」になります。

つまり、プラグインの有効化、無効化に以下の名前のフィルターフックが実行されているわけです。

// 有効時
activate_addImageSizePlugin/addImageSizePlugin.php

// 無効時
deactivate_addImageSizePlugin/addImageSizePlugin.php

さて、フィルターフックですがこれはフィルターフック実行時に同名のフィルターが登録されていれば、それを実行してくれるものです。
これにより、該当箇所を直接編集することなく、外部から調整を行うことが出来ます。

フィルターの登録方法ですが、「add_filter」で行うことが出来ます。
(「add_action」もありますがこれは「add_filter」のエイリアスです)

テーブル作成

では、有効時にテーブルを作成するようにフィルターを作成します。

//addImageSizePlugin.php
$addImageSizePlugin = new addImageSizePlugin;

add_filter('activate_'.$addImageSizePlugin->getPluginBasename(), array($addImageSizePlugin, 'install'));

class addImageSizePlugin
{
  const TABLE_NAME = 'addImageSizePlugin';

  public function getPluginBasename()
  {
    return plugin_basename(__FILE__);
  }

  public function getTableName()
  {
    global $wpdb;

    return $wpdb->prefix.self::TABLE_NAME;
  }

  public function install()
  {
    $this->createTable();
  }

  public function createTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $wpdb->show_errors();
    if (!$wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('CREATE TABLE %s (
        id INT(11) NOT NULL AUTO_INCREMENT,
        width INT(11) NOT NULL,
        height INT(11) NOT NULL,
        PRIMARY KEY(id)
      )', $tableName));
      $wpdb->query(sprintf('ALTER TABLE %s ADD UNIQUE (width, height)', $tableName));
    }
    $wpdb->hide_errors();
  }
}

準備が出来ましたので、プラグインを有効化してみます。

mysql> desc wp_addImageSizePlugin;

+--------+---------+------+-----+---------+----------------+
| Field  | Type    | Null | Key | Default | Extra          |
+--------+---------+------+-----+---------+----------------+
| id     | int(11) | NO   | PRI | NULL    | auto_increment |
| width  | int(11) | NO   | MUL | NULL    |                |
| height | int(11) | NO   |     | NULL    |                |
+--------+---------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

有効化でテーブルが作成出来るようになりました。

テーブル削除

次に、無効化時のテーブル削除ですが有効化と処理はほぼ同じです。

では、無効化時のフィルター追加と、それに伴う処理を追加します。

//addImageSizePlugin.php
$addImageSizePlugin = new addImageSizePlugin;

add_filter('activate_'.$addImageSizePlugin->getPluginBasename(), array($addImageSizePlugin, 'install'));
add_filter('deactivate_'.$addImageSizePlugin->getPluginBasename(), array($addImageSizePlugin, 'uninstall'));

class addImageSizePlugin
{
  const TABLE_NAME = 'addImageSizePlugin';

  public function getPluginBasename()
  {
    return plugin_basename(__FILE__);
  }

  public function getTableName()
  {
    global $wpdb;

    return $wpdb->prefix.self::TABLE_NAME;
  }

  public function install()
  {
    $this->createTable();
  }

  public function createTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $wpdb->show_errors();
    if (!$wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('CREATE TABLE %s (
        id INT(11) NOT NULL AUTO_INCREMENT,
        width INT(11) NOT NULL,
        height INT(11) NOT NULL,
        PRIMARY KEY(id)
      )', $tableName));
      $wpdb->query(sprintf('ALTER TABLE %s ADD UNIQUE (width, height)', $tableName));
    }
    $wpdb->hide_errors();
  }

  public function uninstall()
  {
    $this->dropTable();
  }

  public function dropTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $wpdb->show_errors();
    if ($wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('DROP TABLE %s', $tableName));
    }
    $wpdb->hide_errors();
  }
}

これで、無効化時にテーブルを削除することが出来ました。

登録画面

テーブルが出来ましたので、登録画面を用意します。

管理画面へのメニュー追加は前回も行いましたが、以下の関数で行います。

mixed add_options_page( string $page_title, string $menu_title, mixed string $capability, string $menu_slug [ , mixed $function = '' ] )

関数リファレンス/add options page – WordPress Codex 日本語版

ですが、これをそのまま使用してもメニューに追加することは出来ません。

//wp-admin/includes/menu.php
do_action( 'admin_menu', '' );

メニューを生成する前に、「admin_menu」のフィルターフックがありますので、このタイミングで「add_options_page」を実行します。

//addImageSizePlugin.php
$addImageSizePlugin = new addImageSizePlugin;

add_filter('activate_'.$addImageSizePlugin->getPluginBasename(), array($addImageSizePlugin, 'install'));
add_filter('deactivate_'.$addImageSizePlugin->getPluginBasename(), array($addImageSizePlugin, 'uninstall'));
add_filter('admin_menu', array($addImageSizePlugin, 'addMenu'));

class addImageSizePlugin
{
  const TABLE_NAME = 'addImageSizePlugin';

  public function getPluginBasename()
  {
    return plugin_basename(__FILE__);
  }

  public function getTableName()
  {
    global $wpdb;

    return $wpdb->prefix.self::TABLE_NAME;
  }

  public function install()
  {
    $this->createTable();
  }

  public function createTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $wpdb->show_errors();
    if (!$wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('CREATE TABLE %s (
        id INT(11) NOT NULL AUTO_INCREMENT,
        width INT(11) NOT NULL,
        height INT(11) NOT NULL,
        PRIMARY KEY(id)
      )', $tableName));
      $wpdb->query(sprintf('ALTER TABLE %s ADD UNIQUE (width, height)', $tableName));
    }
    $wpdb->hide_errors();
  }

  public function uninstall()
  {
    $this->dropTable();
  }

  public function dropTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $wpdb->show_errors();
    if ($wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('DROP TABLE %s', $tableName));
    }
    $wpdb->hide_errors();
  }

  public function addMenu()
  {
    add_options_page('addImageSizePlugin Config', '画像サイズ設定', 'manage_options', $this->getPluginBasename(), function(){
      require 'settingTemplate.php';
    });
  }
}

これで、登録画面が追加されました。

では、これから以下の仕様を満たすように機能を拡張していきます。

  • 自由に追加、編集、削除が出来るようにする。
  • 追加可能数は可変とする。

画像サイズの追加

// settingTemplate.php
<?php
  if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_REQUEST['addImage']))
  {
    $addImageSizePlugin = new addImageSizePlugin;
    $addImageSizePlugin->setSize($_REQUEST['addImage']);
  }
?>
<script src="//cdn.jsdelivr.net/jsrender/1.0pre35/jsrender.min.js"></script>
<script id="sizeTemplate" type="text/x-jsrender">
  <tr>
    <td class="form_width"><input name="addImage[{{:id}}][width]"></td>
    <td class="form_height"><input name="addImage[{{:id}}][height]"></td>
    <td><button type="button" class="delete">削除</button></td>
  </tr>
</script>
<script type="text/javascript">
var formId = 0;
jQuery(function () {
  jQuery('#add').on('click', function(){
    jQuery('#table').append(jQuery('#sizeTemplate').render({
      id : formId++
    }));
    jQuery('.delete').off('click').on('click', function(){
      jQuery(this).parent().parent().remove();
    });
  });
}); 
</script>
<div class="wrap">
<h2>画像サイズ設定</h2>
<form method="post" action="<?php echo $_SERVER['REQUEST_URI']?>">
  <table id="table">
  <tr>
    <th>width</th>
    <th>height</th>
  </tr>
  </table>
  <p><button id="add" type="button">追加</button></p>
  <p><button id="submit" type="submit">確定</button></p>
</form>
</div>
//addImageSizePlugin.php
  public function setSize($list)
  {
    if (is_array($list))
    {
      foreach ($list as $size)
      {
        $this->save($size);
      }
    }
  }

  public function save($size)
  {
    global $wpdb;
    $tableName = $this->getTableName();

    if ($this->isValidSize($size))
    {
      $result = $wpdb->query($wpdb->prepare("INSERT INTO `$tableName` VALUES (NULL, %s, %s)", array($size['width'], $size['height'])));
    }
  }

  public function isValidSize($size)
  {
    if (empty($size['width']) || empty($size['height']))
    {
      return false;
    }

    global $wpdb;
    $tableName = $this->getTableName();
    $row = $wpdb->get_row(
      $wpdb->prepare("SELECT * FROM `$tableName` WHERE width = %s AND height = %s", array($size['width'], $size['height']))
    );

    return empty($row);
  }

追加出来るサイズ数を可変とし、登録出来るように拡張しました。

plugin_005

これで登録してみます。

mysql> select * from wp_addImageSizePlugin;
+----+-------+--------+
| id | width | height |
+----+-------+--------+
|  1 |   100 |    200 |
|  2 |   150 |    200 |
|  3 |   450 |    600 |
|  4 |  1200 |    200 |
+----+-------+--------+
4 rows in set (0.00 sec)

無事、登録出来ました。

編集、削除

無事登録が出来るようになりました。

ですが、これではまだ編集と削除が出来ませんので、今度はこれを拡張します。

まずは、管理画面で登録したサイズを確認出来るようにします。

// settingTemplate.php
<?php
  $addImageSizePlugin = new addImageSizePlugin;

  if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_REQUEST['addImage']))
  {
    $addImageSizePlugin->setSize($_REQUEST['addImage']);
  }
?>
<script src="//cdn.jsdelivr.net/jsrender/1.0pre35/jsrender.min.js"></script>
<script id="sizeTemplate" type="text/x-jsrender">
  <tr>
    <td class="form_width"><input name="addImage[{{:id}}][width]" value="{{:width}}"></td>
    <td class="form_height"><input name="addImage[{{:id}}][height]" value="{{:height}}"></td>
    <td><button type="button" class="delete">削除</button></td>
  </tr>
</script>
<script type="text/javascript">
var formId         = 0;
var registeredSize = jQuery.parseJSON('<?php echo json_encode($addImageSizePlugin->getSizeAll())?>');

var addSizeField = function(setting){
  var data = {
    id : formId++
  };

  jQuery.extend(data, setting);

  jQuery('#table').append(jQuery('#sizeTemplate').render(data));
};

var deleteSizeField = function(){
  jQuery('.delete').off('click').on('click', function(){
    jQuery(this).parent().parent().remove();
  });
};

jQuery(function () {
  for (var i in registeredSize){
    addSizeField(registeredSize[i]);
  }
  deleteSizeField();

  jQuery('#add').on('click', function(){
    addSizeField();
    deleteSizeField();
  });
}); 
</script>
<div class="wrap">
<h2>画像サイズ設定</h2>
<form method="post" action="<?php echo $_SERVER['REQUEST_URI']?>">
  <table id="table">
  <tr>
    <th>width</th>
    <th>height</th>
  </tr>
  </table>
  <p><button id="add" type="button">追加</button></p>
  <p><button id="submit" type="submit">確定</button></p>
</form>
</div>
//addImageSizePlugin.php
  public function getSizeAll()
  {
    global $wpdb;
    $tableName = $this->getTableName();

    return $wpdb->get_results(
      $wpdb->prepare("SELECT * FROM `$tableName`", null)
    );
  }

これで、登録したサイズを確認出来るようになりました。

次に編集が出来るように調整します。

idを見て、適切にレコードをUPDATEすることも出来るのですが、今回は簡単に洗い替えを行います。

洗い替えですので、同時に削除も対応できます。

//addImageSizePlugin.php

  public function setSize($list)
  {
    $this->setEmpty();

    if (is_array($list))
    {
      foreach ($list as $size)
      {
        $this->save($size);
      }
    }
  }

  public function setEmpty()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $row = $wpdb->get_row(
      $wpdb->prepare("TRUNCATE `$tableName`", null)
    );
  }

初期化用の処理を追加し、既にある登録時の処理でそれを呼び出すように調整ました。

plugin_006

既存サイズの変更と削除を行います。

mysql> select * from wp_addImageSizePlugin; 

+----+-------+--------+
| id | width | height |
+----+-------+--------+
|  1 |   100 |    200 |
|  2 |   850 |    600 |
|  3 |  1200 |    200 |
+----+-------+--------+
3 rows in set (0.00 sec)

無事、変更と削除が出来るようになりました。

登録したデータで、画像サイズを増やす。

管理機能が完成しましたので、最後に登録したデータを使用して画像の切り出しサイズを増やします。

//addImageSizePlugin.php

$addImageSizePlugin = new addImageSizePlugin;

add_filter('plugins_loaded', array($addImageSizePlugin, 'init'));

class addImageSizePlugin
{
  const TABLE_NAME = 'addImageSizePlugin';

  public function init()
  {
    add_filter('activate_'.$this->getPluginBasename(), array($this, 'install'));
    add_filter('deactivate_'.$this->getPluginBasename(), array($this, 'uninstall'));
    add_filter('admin_menu', array($this, 'addMenu'));

    foreach ($this->getSizeAll() as $size)
    {
      add_image_size(sprintf('%sx%s', $size->width, $size->height), $size->width, $size->height, true);
    }
  }

  public function getPluginBasename()
  {
    return plugin_basename(__FILE__);
  }

  public function getTableName()
  {
    global $wpdb;

    return $wpdb->prefix.self::TABLE_NAME;
  }

  public function install()
  {
    $this->createTable();
  }

  public function createTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $wpdb->show_errors();
    if (!$wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('CREATE TABLE %s (
        id INT(11) NOT NULL AUTO_INCREMENT,
        width INT(11) NOT NULL,
        height INT(11) NOT NULL,
        PRIMARY KEY(id)
      )', $tableName));
      $wpdb->query(sprintf('ALTER TABLE %s ADD UNIQUE (width, height)', $tableName));
    }
    $wpdb->hide_errors();
  }

  public function uninstall()
  {
    $this->dropTable();
  }

  public function dropTable()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    if ($wpdb->get_var(sprintf('SHOW TABLES LIKE "%s"', $tableName)))
    {
      $wpdb->query(sprintf('DROP TABLE %s', $tableName));
    }
  }

  public function addMenu()
  {
    add_options_page('addImageSizePlugin Config', '画像サイズ設定', 'manage_options', $this->getPluginBasename(), function(){
      require 'settingTemplate.php';
    });
  }

  public function setSize($list)
  {
    $this->setEmpty();

    if (is_array($list))
    {
      foreach ($list as $size)
      {
        $this->save($size);
      }
    }
  }

  public function save($size)
  {
    global $wpdb;
    $tableName = $this->getTableName();

    if ($this->isValidSize($size))
    {
      $result = $wpdb->query($wpdb->prepare("INSERT INTO `$tableName` VALUES (NULL, %s, %s)", array($size['width'], $size['height'])));
    }
  }

  public function isValidSize($size)
  {
    if (empty($size['width']) || empty($size['height']))
    {
      return false;
    }

    global $wpdb;
    $tableName = $this->getTableName();
    $row = $wpdb->get_row(
      $wpdb->prepare("SELECT * FROM `$tableName` WHERE width = %s AND height = %s", array($size['width'], $size['height']))
    );

    return empty($row);
  }

  public function getSizeAll()
  {
    global $wpdb;
    $tableName = $this->getTableName();

    return $wpdb->get_results(
      $wpdb->prepare("SELECT * FROM `$tableName`")
    );
  }

  public function setEmpty()
  {
    global $wpdb;
    $tableName = $this->getTableName();
    $row = $wpdb->get_row(
      $wpdb->prepare("TRUNCATE `$tableName`")
    );
  }
}

画像サイズの追加と、add_filterの微調整を行いました。

では、早速画像をアップロードしてみましょう。

plugin_007

追加した画像サイズの切り出しが行われるようになりました。

追加

ここまで、解説してきましたが、今ひとつフィルターの解説が不十分な気がしますので追加を行います。

先ほどのアップロード画像をもう一度、御覧ください。

plugin_007

不要なサイズの切り出しがありますね。

今度はこれが切り出されないように調整します。

画像のサイズは切り出しは直前に、「intermediate_image_sizes_advanced」のフィルターフックが用意されています。

// wp-admin/includes/image.php
$sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );

「apply_filters」を使用していますが、これは「do_action」と同様にフィルターフックを作成する関数です。

異なる点は、第二引数でそのフィルターを通過させる値を定義出来る事です。

関数リファレンス/apply filters – WordPress Codex 日本語版

では、値が通過するフィルターの使用方法と共に「$sizes」の中身がどのようになっているかを確認してみます。

//addImageSizePlugin.php
  public function init()
  {
    add_filter('activate_'.$this->getPluginBasename(), array($this, 'install'));
    add_filter('deactivate_'.$this->getPluginBasename(), array($this, 'uninstall'));
    add_filter('admin_menu', array($this, 'addMenu'));
    add_filter('intermediate_image_sizes_advanced', array($this, 'hookImageSize'));

    foreach ($this->getSizeAll() as $size)
    {
      add_image_size(sprintf('%sx%s', $size->width, $size->height), $size->width, $size->height, true);
    }
  }

  public function hookImageSize($sizes)
  {
    error_log(print_r($sizes, true));

    return $sizes;
  }

この様に追加するフィルターに引数を用意しておけば、値を受け取ることが出来ます。
(値は戻す必要があるので、必ずreturnします)

では、受け取った値を確認してみましょう。

このフィルターは画像の切り出し時(アップロード時)に動作するものですので、検証用に画像をアップロードし、エラーログを確認します。

[thumbnail] => Array
    (
        [width] => 150
        [height] => 150
        [crop] => 1
    )

[medium] => Array
    (
        [width] => 300
        [height] => 300
        [crop] => 
    )

[large] => Array
    (
        [width] => 1024
        [height] => 1024
        [crop] => 
    )

[100x200] => Array
    (
        [width] => 100
        [height] => 200
        [crop] => 1
    )

[850x600] => Array
    (
        [width] => 850
        [height] => 600
        [crop] => 1
    )

[1200x200] => Array
    (
        [width] => 1200
        [height] => 200
        [crop] => 1
    )

thumbnail、medium、largeといったサイズがあることが分かるかと思います。

では、それらのサイズを削除して値を返します。

//addImageSizePlugin.php
  public function hookImageSize($sizes)
  {
    unset($sizes['thumbnail']);
    unset($sizes['medium']);
    unset($sizes['large']);

    return $sizes;
  }

そして、再度画像をアップロードします。

plugin_008

unsetしたサイズの切り出しがなくなりました。

まとめ

プラグインで何か機能を追加、拡張したい場合は、ほぼフィルターを使用することになります。
ですので、今回はプラグインの作成だけではなく、フィルターの説明させて頂きましたが如何だったでしょうか?

どのタイミングで、どのようなフィルターフックが用意されているかが分かっていればプラグインで出来る事が広がるかと思います。

フィルターフックについては、codexに情報があるので、これを確認してください。

プラグイン API/フィルターフック一覧
プラグイン API/アクションフック一覧

さて、次回以降ですがどこかで応用編として、テンプレートをPC、SPで分ける、URLを加工するといった、より実践的なものを解説しようかと思います。

今回作成したプラグイン:addImageSizePlugin