AngularJS Routing 〜PHPをWeb APIにする〜

今回は、AngularJSで作成した画面(子画面)をロードするときに自分のWordPressにある記事の「カテゴリ」を取得して一覧を表示する処理を作成したいとお思います。

WebAPIとは

ここでいう「Web API」とはリクエストを受けてJSONを返却するアプリケーションのことを指します。FaceBook APIや楽天APIなどリクエストに対し(パラメータがついているものもあります。)JSONでレスポンスを返すものです。

今回作成したのはgetCategories.phpを作成してリクエストを飛ばしてやるとname(カテゴリ名)、url(リンクのURL)を返すものを作成しました。パラメータはありません。

もちろん、外部のサーバーからのリクエストでもJSONが取得できると思います。
というかテストしました。下のコマンドを叩くとJSONデータを取得できます。自分の記載しているブログのカテゴリとそのURLです。
curl https://zenryokuservice.com/tools/pgbox/getCategories.php
実行結果はこんな感じです。

Web API実装

そして、巷でよく聞く「WebAPI」の実装です。先ほど記載したように

リクエストに対してJSONでデータを返却するアプリ

しのごの言わずにコードを見て見ます。
<JSのコード>

  // 1サーバーに対してリクエストを送信
  $http({
    method: 'GET',
    url: '/tools/pgbox/getCategories.php',
    params: {} // パラメータなし
  }).then(function(response, status, headers, config){
    // レスポンス(正常)
    $scope.categories = response.data;
  }), function(data, status, headers, config) {
   // レスポンス(異常)
    alert(data);
  };

リクエストを送信、レスポンス受信部分の処理です。AngularJSで実装しているので$httpオブジェクトを使用して簡単に作成できます。
そして、PHPの実装です。
<PHP>

/** エンコード */
header("Content-Type: text/html; charset=UTF-8");
require( $_SERVER['DOCUMENT_ROOT'] . '/wp/wp-load.php' );

try {
    // カテゴリの全取得ようパラメータ
    $paramArr = array('type' => 'post', 'orderby' => 'name', 'order' => 'ASC', 'taxonomy' => 'category');
    // 引数
    $categories = get_categories($paramArr);
    $cnt = 0;

    $jsonArr = array();
    foreach($categories as $cat){
        $catLink = get_category_link($cat->term_id);
        $catName = $cat->name;
        // 連想配列にデータを追加
        array_push($jsonArr, array('name' => $catName, 'url' => $catLink));
    }
    // JSONエンコード(JSON形式に変換)して
    // JSON文字列を出力(クライアントに返却)
    echo json_encode($jsonArr);
} catch(Exception $e) {
    echo $e;
}

全体の流れ

  1. (JS)表示した画面からリクエストを飛ばす(パラメータなし)
  2. (PHP)リクエストを受信、PHPでWordPressテンプレートでカテゴリ名とリンクを取得、連想配列にセット
  3. (PHP)JSON文字列に変換して返却
  4. (JS)リクエストに対するレスポンスを取得、$scopeの「categories」にJSON(レスポンス)をセット
  5. HTML側にデータを出力

こんな感じです。これで、何かしらのWebサービスなんぞを作って見るのも面白いのでわ?

でわでわ。。。



AngularJS Routing 〜画面遷移ができた、そして〜

今回は、「$http」オブジェクトを使用してphpへの非同期リクエストを送信したいと思います。
ちなみに、前回はngRouteを指定して画面遷移に関して記載しいました。

作成中のページはこちらです。(AngularJS + BootStrap)

httpを使う

参考サイトによると、大雑把に下のようなコードでリクエストをサーバー側に送るようです。

  $http({
    method: 'GET',
    url: 'phpへのURL',
    params: { name: $scope.name }
  })
  // 2成功時の処理(ページにあいさつメッセージを反映)
  .success(function(data, status, headers, config){
    $scope.result = data;
  })
  // 3失敗時の処理(ページにエラーメッセージを反映)
  .error(function(data, status, headers, config){
    $scope.result = '通信失敗!';
  }

なので、非同期通信用(リクエストに対してJSONを返却するPHP)を作成する必要があります。
今回は、サイトマップの画面を作成しようと思いますので、WPのカテゴリ一覧を JSONで返却する処理を作成したいと思います。

PHP処理を作成する

こちらのサイトによると下のようなコードで行けるようです。

<?php
  $category = get_the_category();
  $cat_id   = $category[0]->cat_ID;
  $cat_name = $category[0]->cat_name;
  $cat_slug = $category[0]->category_nicename;
?>

シンプルに引数なしの一覧を返すようです。
しかしこれに、URLも欲しいのでもう1つ処理を呼び出します。PHPドキュメント参照

<?php
    // 指定したカテゴリーの ID を取得
    $category_id = get_cat_ID( 'カテゴリー名' );

    // このカテゴリーの URL を取得
    $category_link = get_category_link( $category_id );
?>

<!-- このカテゴリーへのリンクを出力 -->
<a href="<?php echo esc_url( $category_link ); ?>" title="カテゴリー名">カテゴリー名</a>

そして、複数のカテゴリ一覧を取得するにはget_categoriesを使用するみたいです。
まとめると下のようなコードで良いことになります。

$categies = get_categories();
  $cat_id   = $categies[0]->cat_ID;
  $cat_name = $category[0]->cat_name;
  // このカテゴリーの URL を取得
  $category_link = get_category_link( $cat_id );
  // JSON文字列を出力
  echo json_encode($category_link);
  

しかし、カテゴリ一覧なので、ループ処理を入れてやる必要があります。
PHPのループ処理を入れてやればうまく動きそうです。
実装の部分は後日にします。

でわでわ。。。

AngularJS + WordPress 〜WebAPIを作る〜

タイトルに「WebAPI」と格好良いことを記載しましたが、つまるところはリクエストを飛ばしてその返り値にJSONを渡してやる。。。それだけです。

AngularからWPを呼ぶ

今回は、AngularJSからのリクエストなので「$http」オブジェクトを使用します。そして、リクエスト先は自作のphpファイルです。なので「httpls://zenryokuservice.com/XXX/」
というようなURLへのリクエストを飛ばします。

PHPの処理

PHP(WP)側はカテゴリの一覧を取得してJSONにして返却する。これだけです。

実装(PHP)

このサイトを参考にして関数の使い方を見ながら作成します。

作成したコードは下のようなコードです。(PHP)

/** エンコード */
header("Content-Type: text/html; charset=UTF-8");
/** 日付の設定 */
date_default_timezone_set('Asia/Tokyo');
/** Loads the WordPress Environment and Template */
require( dirname( __FILE__ ) . '/wp/wp-includes/pluggable.php' );

try {
    // カテゴリの全取得ようパラメータ
    $paramArr = array('type' => 'post', 'orderby' => 'name', 'order' => 'ASC', 'taxonomy' => 'category');
    // 引数
    $categories = get_categories($paramArr);
    $cnt = 0;

    $jsonArr = array();
    foreach($categories as $cat){
        $catLink = get_category_link($cat->term_id);
        $catName = $cat->name;
        $jsonArr = array_merge($jsonArr, array('name' => $catName, 'url' => $catLink));
    }
    echo json_encode($jsonArr);
} catch($e) {
    echo $e;
}

しかし、動きません。try-catchで囲んでもエラーの内容が見れません。。。

PHPは〜。。。もどかしいですが、一行ずつ実行していきますか。。。

そんなわけで、word-pressの関数が怪しいと思い、WPのインクルード方法を探します。。。

WordPress関数ロード

結局は/wp/wp-load.phpをロードすれば良いというところでした。

動かない原因

catch($e)の部分が間違っていました。catch(Exception $e)でした。このページを見るとtry-cathの書き方が書いてありました。
JSと混同していたようです。。。

結局は。。。

下のようなコードで動きました。JSサイドはまだ実装していないのであれですが。。。

/** エンコード */
header("Content-Type: text/html; charset=UTF-8");
/** 日付の設定 */
date_default_timezone_set('Asia/Tokyo');
require( '/wp/wp-load.php' );
try {
    // カテゴリの全取得ようパラメータ
    $paramArr = array('type' => 'post', 'orderby' => 'name', 'order' => 'ASC', 'taxonomy' => 'category');
    // 引数
    $categories = get_categories($paramArr);
    $cnt = 0;

    $jsonArr = array();
    foreach($categories as $cat){
        $catLink = get_category_link($cat->term_id);
        $catName = $cat->name;
        $jsonArr = array_merge($jsonArr, array('name' => $catName, 'url' => $catLink));
    }
    echo json_encode($jsonArr);
} catch(Exception $e) {
    echo $e;
}

これでサーバーサイドの処理ができたと思われるので、JSの方の実装を行います。

ただ、デバックしたときにデータが1つしか取れていないような?一抹の不安を残して今日は寝ます。

でわでわ。。。



MacにNode.js サーバーを立てる

今回は、自分のPC上でAngularJSの開発環境を作成します。今までは、単純にレンタルサーバーで色々やっていたのですが、でバックに時間がかかりそうなので、ローカルで開発するようにしようと考えた次第です。

Node.jsインストール

こちらのサイトを参考にしました、ぶっちゃけ以下の手順でいけます。Macの場合です。

  1. このサイトからNode.jsをインストール
  2. npmコマンドが使えるかどうか npm -v コマンドを叩いて確認(バージョン情報が表示されること)
  3. npm install -g http-serverとコマンドを叩いてインストール
  4. ウェブ・サーバーのルートディレクトリに移動してhttp-serverとコマンドを叩くと起動する
    アクセスするURLはhttp://localhost:8080/表示するhtmlファイルのようにブラウザに入力します。
    自分の場合は下のように入力しました。
    http://localhost:8080/bootstrapTheme.html

ちょっと簡単でしたが、こんな感じです。
詳細はGitに書いてありました。起動したときに、デフォルトでキャッシュするようになっているので改修など行ったときに変更が反映されません。
こんなときはhttp-server -c-1で起動してやるとキャッシュしないようにできるようです。
しかし、自分のローカルサーバーではうまくいかず。。。シークレットウィンドウを立ち上げてやっています。。。

ローカルサーバーの意義

レンタルサーバーなどでAgularJSなどを使用する場合は、デバックなどに時間が取られるので公開できる状態にするためにはテストなどが必要なので、ローカル(自分のPC)でテスト(起動確認)を行うたいときがあります。

AngularJSの場合

このフレームワークを使用すると楽なのですが、画面遷移などを行うときにはURLでファイルにアクセスするのでChromeなどのブラウザではセキュリティが働きファイルに直接アクセスできません。

URLとファイルパスの違い

URLはディレクトリと結びついていますが、必ずしもディレクトリ構造をそのまま表示しなくても良いのです。
具体的には以下のようになります。
<サーバーのドキュメントルート>
レンタルサーバーなどで自分がアクセスできる初めのディレクトリ(フォルダ)のことを言います。
上のhttp-serverコマンドで起動した時はこのディレクトリがコマンドを実行したディレクトリになります。

うんちく的な。。。

サーバーディレクトリ構造

例えば自分の使用するディレクトリ構成は下のようになっています。

今回表示したHTMLが「bootstrapTheme.html」なのでターミナルを開き、このディレクトリに移動してからhttp-serverコマンドを叩きます。
するとhttp://localhost:8080/bootstrapTheme.htmlとブラウザのURL部分に入力した場合は上のような画面が表示されます。そしてprojectフォルダ以下にあるファイルを参照したい場合は下のようになります。
http://localhost:8080/project/XXXX.html
そして、AngularJSの設定モジュールでlondon.htmファイルを下のように指定していますので、projectフォルダの下にlondon.htmファイルがないと表示できません。
しかし、.when("/it"), の部分で指定しているURLが来たときにはtemplateUrlで指定しているファイルを参照するというような指定方法が可能になります。
→ウェブサーバーが動いて、ファイルを表示しているのでウェブサーバーに下のように指定してやると、そのように動く、機構が「AngularJS」にはあるということです。
このようなところは使用するフレームワーク(自作のものでもできます)に依存するので「こーなんです!」と言い切ることができません。。。

エラー解消できず

しかし、ローカルサーバーを立てても自分の場合は解決しなかったので。こちらのサイトを参考にコマンドからChromeを起動するようにしました。。。。

でわでわ。。。



app.config(function($routeProvider) {
  $routeProvider
  .when("/it", {
      template: "

Template Test

" // templateUrl : "main.htm" }) .when("./london", { templateUrl : "./project/london.htm", controller : "londonCtrl" }) .when("/paris", { templateUrl : "./pages/paris.htm", controller : "parisCtrl" }); });

AngularJS Routing 〜画面遷移を行う〜

前回ログイン画面を作成しようと考えていましたが、その前にAngularJSでのページ遷移あたりが、わかっていないのでそこを学習し始めようと思います。

参考サイトにある、画面遷移のサンプルをこのページに埋め込み動かしてみようという試みです、しかし本物のファイルへのURLを表示するのはセキュリティ的にアウトなので、画面に表示するところまでは行きませんでした。
しかし、URLを指定すれば表示できルはずです、
注意点としてはURLと参照先ファイルへのパスが混同しないようにするところです。

画面遷移

参考サイトはこちらです。W3 schoolsのサイトです。
ここのページでは、英語表示なので日本語に翻訳して読みます。そして、サンプルとしてこのリンク先に試しにコードと実行結果をいじれるページがありました。

つまり

appモジュールを作る時にngRouteも追加して、routeプロバイイダーでURLを指定してやれば良いという認識を持ちました。

 let app = angular.module('モジュール名(XXApp)', ['ngRoute']);
app.controller('コントローラ名', function($scope) {
  // 処理
} 

つまづいたところ

ルーティングをやるのに設定をするところ

app.config(function($routeProvider) {
  $routeProvider
  .when("/it", {
      template: "<h1>Template Test</h1>"
//    templateUrl : "main.htm"
  })
  .when("/pages/london.htm", {
    templateUrl : "./pages/london.htm",
    controller : "londonCtrl"
  })
  .when("/paris", {
    templateUrl : "./pages/paris.htm",
    controller : "parisCtrl"
  });
});


移動する場所を指定、取得するのに自分のPCでテストする場合はローカルサーバーを立てないとブラウザのセキュリティで弾かれるのでテストが面倒というところ、Node.jsなどでWebサーバーを起動してやるとローカルでテスト(実験)しやすいです。

Routingしてみる

参考サイトにあるように「ロンドン」「パリ」のHTMファイルをロードするように実装して見ます。

app.config(function($routeProvider) {
  $routeProvider
  .when("/it", {
      template: "<h1>Template Test</h1>"
//    templateUrl : "main.htm"
  })
  .when("/london", {
    templateUrl : "./pages/london.htm",
    controller : "londonCtrl"
  })
  .when("/paris", {
    templateUrl : "./pages/paris.htm",
    controller : "parisCtrl"
  });
});

app.controller("londonCtrl", function ($scope) {
  $scope.msg = "I love London";
});
app.controller("parisCtrl", function ($scope) {
  $scope.msg = "I love Paris";
});

エラーの対応

そして、実行(表示)してみると
下のようなエラーが出ました。ちなみに無限ループしてちょっと面倒でした。

angular.js:12845 Access to XMLHttpRequest at 'file:///london' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
(anonymous) @ angular.js:12845
angular.js:12845 Access to XMLHttpRequest at 'file:///paris' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.

これの原因はHTMLに定義しているメソッドで以下のような記述がダメでした。
ng-click="headerClick({{categ.url}})
そして、HTMLはこんな感じです。

<div ng-repeat="categ in techCategories" >{{categ.categName}}</div>

色々と試した結果、ng-clickには「{{」がいらない「}}」

という答えが出ました。

結果下のようなコードになりました。

<div class="scrollmenu" style="display:{{leftSideBar_disp}}">
  <a ng-repeat="categ in techCategories" href="{{categ.url}}">{{categ.categName}}</a>
  </div>

一応はこれで表示ができました。

追伸

うまくいかなかった、画面遷移が動きました。

でわでわ。。。