お手軽に管理しやすいWordPressテーマを目指すとっても簡単なTips

これは前に書いたブログの記事を再編集したものです。

こんにちは。 簡単なphpコードとWordPressの関数を使用すれば、とっても管理しやすい Wordpressのテーマが出来上がることを知っていますか? あまりphpの詳しくない方でも簡単に始めるモダンなWordPress環境への手引きを記載していきたいと思います。

ちなみに自分はfunctions.phpに1万行も書かれているような案件には配属されたことなくて、せいぜい1000行くらいだったきがする。(もう書くことは殆ど無いけど)

1. home.phpなどのテンプレートファイルにWP_Queryとかたくさん書いてあってグチャグチャ

何でもかんでもテンプレートファイルに書くのはやめて、functions.phpに書こう

この書き方をすることによって得られるメリットはご覧になって分かる通り、 home.phpのコード量が減るだけでなく、関数名を付けることが必要なので「このWP_Queryが何を取得しているのか」を非常に把握しやすくなる。

before

<?php

// home.php

$query = new WP_Query(array(
  'post_type' => 'post',
  'tax_query' => array(
    'relation' => 'OR',
    array(
      'taxonomy' => 'category',
      'field'    => 'slug',
      'terms'    => array( 'quotes' ),
    ),
    array(
      'relation' => 'AND',
      array(
        'taxonomy' => 'post_format',
        'field'    => 'slug',
        'terms'    => array( 'post-format-quote' ),
      ),
      array(
        'taxnoomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'wisdom' ),
      ),
    ),
  ),
));

if ( $query->have_posts() ): $query->the_post();
?>

after

<?php
  // home.php
  $query = getMainLoop();

  if ($query->hava_posts() ) : $query->the_post();
?>
<?php 
// functions.php
function getMainLoop()
{
  return new WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
      'relation' => 'OR',
      array(
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'quotes' ),
      ),
      array(
        'relation' => 'AND',
        array(
          'taxonomy' => 'post_format',
          'field'    => 'slug',
          'terms'    => array( 'post-format-quote' ),
        ),
        array(
          'taxnoomy' => 'category',
          'field'    => 'slug',
          'terms'    => array( 'wisdom' ),
        ),
      ),
    ),
  ));
}

WP_Queryの設定値が配列地獄になってしまうのはどうしようもないので、後方互換性を切り捨てた、次世代型のWP_Queryに期待しましょう。

2. 今度はfunctions.phpがカオスになってきてしまった。

各役割ごとにファイルを分けてfunctions.phpを管理する

具体的なコード例

<?php
  // functions.php
  require_once('/lib/functions/admin.php');
  require_once('/lib/functions/model.php');
<?php
   // /lib/functions/admin.php
   //管理画面に関する処理....
<?php
// /lib/functions/model.php

  //WP_Queryを中心とした、データを取得・操作する処理....

  function getCategories()
  {
      return WP_Query(array(
          //....
      ));
  }

別にルールとかはなんでもいいのだけど、 1ファイルに全部まとまらないように書いていくのが重要。

ちなみに僕は小規模なテーマであれば、クラスとかは使わないで データを取得するロジックは lib/functions/model.php, 管理画面に機能を追加するロジックは lib/functions/admin.phpに … と言った感じにざっくり分けて書いている。

そんなに大規模なものでないならクラス設計しないで、そのまま雑にさっさと書いたほうが早いし、簡単。

個人的にはWordPressMVCみたいなのをいちいち書くのはアホらしいと感じてて、そこまでするんだったら、さっさとWordPress捨てて、何かフレームワーク使ったほうが絶対に幸せなのにな。っていつも思ってる。(事情があって使っているのはわかるけれども…)

無名関数を使ってフィルターイベントを書く

※ これはphp5.3以降で使用できるTipsなので、使用する場合はphpのバージョンを確認してから使用するようにしましょう。

これはWordPressのhook処理を追加する時に重宝するTipsです。 関数名が他のものと被ってしまうと処理がうまく行かなかったりするかもしれません。 この書き方ではそれを解決し、それぞれのadd_filterを追加する時に関数名がぶつかるかどうかを気にする必要がないことがメリットです。

ただしこれを使用すると、他の部分で使いまわすことができなくなるので、いろいろなhookで同じ処理を使い回したいときは、 この書き方をするべきではないでしょう。 他のHook処理等に使いまわしていない処理はこのように積極的に無名関数で書いていって良いと思います。

before

<?php

add_filter('the_excerpt_rss','add_thumbnail');

function add_thumbnails($content){
  global $post;
  if(has_post_thumbnail($post->ID)) {
      $content = '<p>' . get_the_post_thumbnail($post->ID) . '</p>' . $content;
  }
  return $content;
}

after

<?php

add_filter('the_excerpt_rss',function($content){
  global $post;
  if(has_post_thumbnail($post->ID)) {
      $content = '<p>' . get_the_post_thumbnail($post->ID) . '</p>' . $content;
  }
  return $content;
});

配列の短縮構文をちゃんと使おう

※ これはphp5.4以降で使用できるTipsなので、使用する場合はphpのバージョンを確認してから使用するようにしましょう。

ちゃんとサポートされているバージョンで開発しているのであれば、積極的に利用しましょう。 php 5.4以下をサポートする必要がないのに、使わない理由はまったくないと思ってください。 arrayなんでタイプしている暇があればもっと他のことに時間を使いましょう。

PHP: 配列 - Manual

before

<?php 
function getMainLoop()
{
  return new WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
      'relation' => 'OR',
      array(
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'quotes' ),
      ),
      array(
        'relation' => 'AND',
        array(
          'taxonomy' => 'post_format',
          'field'    => 'slug',
          'terms'    => array( 'post-format-quote' ),
        ),
        array(
          'taxnoomy' => 'category',
          'field'    => 'slug',
          'terms'    => array( 'wisdom' ),
        ),
      ),
    ),
  ));
}

after

<?php 
function getMainLoop()
{
  return new WP_Query([
    'post_type' => 'post',
    'tax_query' => [
      'relation' => 'OR',
      [
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => [ 'quotes' ],
      ],
      [
        'relation' => 'AND',
        [
          'taxonomy' => 'post_format',
          'field'    => 'slug',
          'terms'    => [ 'post-format-quote' ],
        ],
        [
          'taxnoomy' => 'category',
          'field'    => 'slug',
          'terms'    => [ 'wisdom' ],
        ],
      ],
    ],
  ]);
}

FSSMで特定条件のLinux環境で使えないバグを修正してプルリクエストを投げた

いきなりまとめ

  • Linux環境のGemのrb-inotifyがインストールされている環境で使用した場合、落ちるエラーを修正した。
  • まだ反応がなくMergeされていないので、GithubからFork版を一旦使ってほしい。 github.com
  • PRはこちら github.com

そもそもFSSMとは?

Rubyでファイルを監視してなにかしらの処理を行うためのGem。 ファイルを監視して、変更を検知した場合にSlackで何か通知したり、ファイルバックアップデータを取ったりするスクリプトをささっと書くために利用されている。 FSSM自体に関してはここではあまり言及しないので下記サイトをご参照してもらえればと。 www.virment.com

どんな不具合があったのか

Gemのrb-inotifyがインストールされており、Linux環境だった場合に下記エラーを吐いて落ちてしまうというものでした。

/home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:58:in `>': comparison of String with 0 failed (ArgumentError)
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:58:in `rb_inotify?'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:9:in `usable_backend'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:25:in `backend'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm.rb:54:in `const_missing'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/monitor.rb:4:in `initialize'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm.rb:67:in `new'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm.rb:67:in `monitor'
    from app.rb:15:in `<main>'

中身自体は至って単純で、 下記コミットログを参照してもらうとわかるけど、rb-inotifyのINotify::VERSIONがArrayからStringに変更されていた。(ちょっとコミットログが長い…) github.com

なので、下記PRにて、対応をざっくりと行ってとりあえず動くようにしておいた。 github.com

正直修正内容に関しても筋がちょっと悪いかも…と思ってて、 本来はGem::Versionで比較するべきだよなぁ…と思ったのだが、 とにかく動かないことに対して不満だったので、今回はこれで投げて様子を見ることにした。

その後

投げて2日位たったけど、作者や他のメンテナー(いるのか?)から特に反応がない。 もう少しして特に反応がない場合は、メンション付きのコメントをして反応があるかを見てみようと思う。

日本郵便での日本からのEMS配送料を算出するAPIを公開してみた。

前回の記事に引き続き、 過去の作ったものでブログを書いてみるシリーズの第2弾です。

前回はこっち。 webuilder240.hatenablog.com

前にこの記事を書きましたが、 webuilder240.hatenablog.com

これをAPIで公開しました。 Productionで使われることはそこまで気にしていないんで、HerokuのHobbyインスタンスで適当にやっています。 自前で欲しい場合はHerokuButtonもないので、公開しているリポジトリから適当にやってください。 他の404も手抜きです。今後使う人が増えてくればやろうかなぁと言う感じ。

API: https://from-jp-ems.herokuapp.com

Repo: github.com

使い方

curl -X GET https://from-jp-ems.herokuapp.com?area=asia&weight=100 | jq

Response

{
  "result": 200,
  "area": "asia",
  "weight": 100,
  "amount": 1400
}

ざっくり仕様

  • weightは数値型で1〜30000のグラム単位の値。
  • area は “asia”, “oceania”, “north_america”, “middle_east”, “central_america”, “europa”, “africa”, “south_america"のいずれか。

料金表についてはをここを参照してください。(エンドポイントを追加しようかな。) www.post.japanpost.jp

ちょっと工夫した点

ほとんど算出するロジックはGemで実装しているし、ほとんどやることは無かったと言っても過言ではない。 まぁでも若干苦労したところがあったので、簡単に書いておく。

weightが文字列だったときの対応

このAPIの場合、weightに渡すパラメータは数値型、Integerである必要がある。 なのでざっくりIntegerに変換する方法をとった。 ちなみにto_iだったら、"100kg"がパラメータとして渡ったときに100と返してしまうので使えなかった。 Integerで変換して文字列だったときはrescueでキャッチして、数値を0にするようにした。(もっと他にありそう。)

  def weight=(value)
    begin
      value = Integer(value)
    rescue TypeError, ArgumentError
      value = 0
    end
    @weight = value
  end

そのうち何らかの形でkg単位のAPIなんかもサポートしたいと思っている。

実行できるメソッド

areaで渡ってきたパラメータをそのままバリデーションして実行したかったので、 ライブラリで定義されているクラスメソッドの一覧を取得して、そのリストでValidateするようにした。 特異メソッドなので、singleton_methodsで取得。 ソースコードで言うとこんなところ。

  ALLOW_AREAS = JpEmsFee.singleton_methods.map(&:to_s)
  validates :area, inclusion: { in: ALLOW_AREAS }

あとはなるべくControllerに処理を書かないようにしたくらい。 本当はDecorator層とかに書けよってものもModelに書いてるけど、 本当に小さなAPIなので気にしない。

反省点・今後のTODO

Docker

今回は面倒だったのでやらなかった。 これは今後Herokuで動かせるようにしつつやっつける。

Rails API

API用途だけなので、Rails API使ってみればよかった。 他の不要なGemを掃除できたのでそのうち対応してみる。

Request Rspec

まだ書いてない。正直書くボリュームでもないかな。とも。

Swagger

使ってみればよかった…

最後に

僕の働いているオシロ株式会社ではエンジニアを募集しています。

手前味噌ですが、面白い福利厚生や会社制度もありますし、 普通のWebサービスではあまりないような課題があるので、Railsをバリバリ書ける環境で、やりがいがあるんじゃないかなと思っています。 Railsを覚えたい他の言語でのご経験のあるエンジニアさんも大歓迎です!!

まずは話を聞いていただけるだけでもとても嬉しいので、まずはWantedlyの募集ページを見てみてください! 何卒よろしくお願いいたします。

www.wantedly.com

PHPのシンプルなファイルキャッシュの仕組みを作ってみた。

大げさな事を言っているけど、単にAPI何かのレスポンスをキャッシュしたいときに雑に扱えるものを作った。という感じ。 類似ライブラリはあるっちゃあるけど、このくらいの実装だったら自分でメンテしたほうが良いと思ってる。 公開も面倒なので雑にGistにした。使い方もGistを参照して欲しい。

simple file base cache for php

Expireは?

expireは用途によってExpireするタイミングとかって違うだろうし、メンテも面倒なので気が向いたら書くか〜という感じ。

Packgistに公開は?

現状していない。ぶっちゃけ自分でPHP書く案件はとても小さな案件が多いだろうし、Composerすらいれるのが面倒なのである。(主にWordPressだったり素のPHP案件) とはいえ、このライブラリで市民権を得たいという気持ちもあるので、テストが書けたら1.0.0としてリリースを検討している。

まとめ

  • こういったとってもシンプルなライブラリを作っておく or 類似ライブラリを見つけることで自分の小さいサービスを作るときに多いに役立つ。
  • FBやインスタなんかのAPIを叩いて作るライブラリなんかはこういうのを雑に導入しておくととても効率が良い。
  • そのうちRuby版作ってみる。

最後に

僕の働いているオシロ株式会社ではエンジニアを募集しています。

メインで触っているのはRubyだったりするのですが、少しPHPの業務もあったりするし、 Rubyをやってみたくて悶々としているPHPエンジニアさんも大歓迎です!

まずは話を聞いていただけるだけでもとても嬉しいので、まずはWantedlyの募集ページを見ていただけると幸いです。 何卒よろしくお願いいたします。

www.wantedly.com

決済サービスが用意している定期課金機能でWebサービスの定期課金の仕組みを作るべきでない事例をまとめた

ということなんで、書いてみました。

決済サービスが用意してくれている定期課金の仕組みは利用しないほうがいいと個人的には思っている。 1年以上この仕組みでやってきたが、なかなか弊社案件ではちょっとつらいものがあって、今後のために色々書いておこうと思ったので書いた。

先に書いておきますが、利用している決済サービスに対しては概ね満足しており、 特定の決済サービスへの批判ということではないということをご了承いただきたいです。

いきなりまとめ

  • サービスロックインが心配
  • イレギュラーな事例への対応ができない(しにくい)から。
  • 複数決済サービスの利用時に期間や挙動に細かい差異の吸収ができない。
  • 要件がかっちり決まっており、仕様変更が未来永劫ないか、今後実装変更のために時間を利用できる場合は自前実装はしなくてもいい。

ということで、理由をざっくり説明したいと思う。

1. サービスロックイン

これは最近あった事例だけど、決済サービスが大手企業がバックに付いていても某社の様にサービスがなくなるかわからない。 スタートアップ企業とはそういうものだし、そんなときに定額課金を各サービスに依存している場合、相当苦労することだろう。 移行先の決済サービスに定額課金の機能が存在していても、(殆どのサービスのところは存在しているはず)

細かい挙動(次の決済日時算出ロジック)が違ったりして、意図しない課金日に設定されたりということがあるいうことを考えるとそのまま全く同じに移行して動くという保証はない。

こういったリスクを最小限に抑える場合は、自前で定期課金の仕組みを実装するべきである。 当然再実装は必要になる部分はどっちにしろあるけど、殆どの場合で同じように動かすのは容易だと思う。

大きく決済周りの実装を要件の変更に対応することもあるだろうから、 その際に、「特定のサービスに依存していない」というのは強みになると思う。

特に弊社では決済周りの実装がカスタマーによって細かく異なるケースも存在する可能性もあることから、 サービスロックインは無い方が良いのでは?と思い始めているというのも理由の一つだと思う。 

2. イレギュラーに対応できない。

イレギュラーといっても色々あるが、簡単にいえば決済サービスが定期課金のAPIで実装されていないけどやりたいことの全部である。 日割り計算が決済サービスの定額課金APIでなければそれだし、 他にも複数のプランがあって、途中のプランのアップグレードやダウングレードを考慮するときに、 「差分だけ支払ってアップグレードする」ということが難しいというか、定期課金のAPIで実装されてない場合。仕組み上不可能に近い。

他にも「XXXXできますか?」「XXXに対応しないといけない」という特殊な要望もあるだろう。 それを今後対応する場合でも最初から見据えて実装するべきである。 これらに対して柔軟に対応したい、ということを考えると自前定期課金の仕組みを実装するべきだと思う。

3. 複数決済サービスの利用時の期間や挙動に細かい差異が生じる

課金を取り扱うサービスの場合、複数の決済サービスを利用することが可能性としてあがってくる事がある。 クレジットカード決済でも審査の都合からJCBが使えないというトラブルもあり、Paypalをイレギュラーに利用する場合もあるだろう。

他にも銀行振込の決済サービス・携帯のキャリア決済・コンビニ払い、さらには外貨サービスは別かも…と、 多種多様の支払い方法があり、それらに対してすべての支払い方法を対応するようなケースも想定される。

当然すべての支払い方法がカバーされている決済サービスを利用すれば良いのだが、 手数料やビジネス上の問題から、それぞれの支払い方法で最適な決済サービスを選ぶこともあるだろう。

会社・サービスが異なると、基準となるタイムゾーンなどが異なる場合もあり、決済サービスと自分たちの意図している課金日がずれてしまったりすることも存在し、 課金日を厳格にアプリケーション側で管理したいような性質のサービスの場合は、決済サービスで自前で定期課金の仕組みを作った方が圧倒的に気楽である。

まとめ

簡単ではあるが、定期課金処理について少し困ったことを吐き出してみた。

決済サービスに依存した定期課金処理は実装もラクで便利ではあるが、 使いどころを間違えると後で痛い目に遭うので、導入には大いに時間をかけて検討するべきである。 この記事を決済処理を実装する前の自分におくりたかった…

やっぱり弊社案件では定期課金に対しても柔軟性が必要になるケースがあるかもなので、 これから時間を作って、定期課金の実装を自前のものに載せ替えることを検討している。

参考記事とか

www.bokukoko.info www.bokukoko.info

最後に

僕の働いているオシロ株式会社ではエンジニアを募集しています。

手前味噌ですが、面白い福利厚生や会社制度もありますし、 普通のWebサービスではあまりないような課題があるので、やりがいがあるんじゃないかな。と思っています。 まずは話を聞いていただけるだけでもとても嬉しいので、まずはWantedlyの採用ページを見てみてください。 何卒よろしくお願いいたします。

www.wantedly.com