WordPress × Angular Universal StarterでWEBアプリっぽいブログを作成する。

最近プライベートでプロダクトを作ったり、イベントに参加したりすることが多いので、何か形に残そうと思いブログを作成することにしました。
ただ普通に、WordPressでブログを作成しても面白くないので、Angularを使ってwebアプリっぽくすることにします。

ステップとしては以下の3つ。

レンタルサーバーにWordPressを配置する。
1. WordPressをREST API化。
2. Angularで作成したWEBアプリからAPIを呼ぶ。
3. 作成したWEBアプリをコンパイルし、レンタルサーバーに配置する。

1.レンタルサーバーにWordPressを配置する。

さくらサーバーを契約していたので、サーバーコントロールパネルから設置します。
ボタン1つで設置できるので便利。

2.WordPressをREST API化。

設置したWordPressにログインし、諸々の初期設定を実施します。
REST API化するには「WordPress REST API」というプラグインを入れると良いとのことなので、インストールして有効化してみます。
https://www.tam-tam.co.jp/tipsnote/cms/post9688.html

設置後、http://[your-site-name].com/wp-json/wp/v2/posts?_embedとブラウザで打ち込んでみると、無事jsonが返却されていることが確認できました。

GETのパラメターとして _embed を付加すると、アイキャッチ画像やコメントなど、付随する情報も全て取得できるらしいです。
ものの数分でAPIサーバーができてしまったと考えるとすごい。。

3. Angularで作成したWEBアプリからAPIを呼ぶ。

AngularでWEBアプリを作成するため、下記のプロジェクトをベースに使用します。
Angular Universal Starter
Angular純正のコンパイル方法の方が今後のバージョンアップのことを考えると良い気がするので、cli版を選択。

Server Side Renderingや静的コンテンツとしての出力などにも対応しているので、 AngularでWEBアプリを作成するにはとても便利です。

AngularのClientModuleで「WordPress REST API」で作成したAPIを利用するにあたり、返却されるjsonの形式&項目名がブサイクだったので、Mapperを作成して美しい名称&キャメルケースにマッピングしてjsonデータを作成することにしました。
これによりAPI側の仕様変更にも強くなるはず。
こんなイメージ。

// 元のjsonデータ
{
  "id":1,
  "title": {"rendered":"Hello world!"},
  "content": {"rendered":"WordPressへようこそ"},
  "_embedded": {
    "wp:featuredmedia": {
      "media_type":"image",
      "source_url": "http:\\hogehoge.com\wp-content\uploads\2017\10\hoge-150x150.jpg"}
        :
    },
      :
  },
    :
}
// こんな感じのモデルにマッピング!
class Content {
  rendered: string = null;
}

class Media {
  // 'media_type'がmediaTypeにマッピングされる
  @JsonProperty('media_type')
  mediaType: string = null;
  @JsonProperty('source_url')
  sourceUrl: string = null;
      :
}

class Info {
  @JsonProperty({ clazz: Media, name: 'wp:featuredmedia' })
  media: Media[] = null;
      :
}

export class PostModel {
  id: number = null;
  @JsonProperty({ clazz: Content })
  title: Content = null;
  @JsonProperty({ clazz: Content })
  content: Content = null;
  @JsonProperty({ clazz: Info, name: '_embedded' })
  info: Info = null;
      :
}

参考にしたの以下のはJson Mapper。
http://cloudmark.github.io/Json-Mapping/

ちなみにAPIを呼ぶプログラムはこんな感じになります。

// APIを呼ぶサービス
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpRequest, HttpParams } from '@angular/common/http';
import 'rxjs/Rx';

/**
 * Api Service
 *
 * @export
 * @class ApiService
 */
@Injectable()
export class ApiService {
  /**
   * Creates an instance of ApiService.
   *
   * @param {HttpClient} httpClient
   *
   * @memberOf ApiService
   */
  constructor(
    private httpClient: HttpClient
  ) {
    this.httpClient = httpClient;
  }

  /**
   * Http get from api
   *
   * @param {string} url
   * @param {Object} [params]
   * @returns
   *
   * @memberOf ApiService
   */
  public get(url: string, params?: Object): Promise {
    const self = this;
    let httpParams = new HttpParams();

    if (params) {
      Object.keys(params).forEach(key => {
        httpParams = httpParams.set(key, params[key]);
        console.log(httpParams);
      });
    }

    return new Promise((resolve, reject) => {
      let options = { params: httpParams };
      this.httpClient.get(url, options)
        .subscribe(
          data => resolve(data),
          err => reject(err)
        );
    });
  }
}
// BLOGデータを取得するサービス
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/toPromise';

// shared
import { MapperUtils } from '../+shared/utils/mapper-utils';
import { ApiService } from '../+shared/services/api.service';
import { PostModel } from '../+shared/models/post.model';
import { API } from '../+shared/utils/const';

@Injectable()
export class BlogService {

  constructor(
    private apiService: ApiService
  ) {}

  /**
   *
   *
   * @returns {Promise}
   *
   * @memberof BlogService
   */
  callPostsApi(page: number, id?: number): Promise {
    return new Promise((resolve, reject) => {
      const url = id ? `${API.URL.POSTS}/${id}` : API.URL.POSTS;
      const params = { _embed: null };

      this.apiService.get(url, params).then(
        result => {
          const rtn = MapperUtils.deserialize(PostModel, result);          
          resolve(rtn);
        },
        err => reject(err)
      );
    });
  }
}

これであとはAngularでかっこよく画面表示するだけ。

4. 作成したWEBアプリをコンパイルし、レンタルサーバーに配置する。

まずはコンパイルする。
Angular Universal Starterで記載されいてるコマンドnpm run build:staticで静的コンテンツを作成。
新しくページを作成している場合は、static.paths.jsに記載しないと、静的コンテンツが作成されないので気をつける必要があります。
コンパイル完了後、dist/browser配下に各種コンテンツが出力されているので、それら一式をレンタルサーバーに配置すれば作業完了です。

作成したWEBはこちら。
http://tanakas.org/blog

The following two tabs change content below.
Akihiro Tanaka

Akihiro Tanaka

Smart Contract Engineer
Since 2009, I have been a software engineer at Accenture for 9 years, managing, designing, and developing many services, mainly web and mobile apps.
In 2013, I met Bitcoin and started to work on blockchain-related development in 2018, developing an entertainment DApp for underground idols, a blockchain analysis tool, and an STO platform.
Currently, I am working as a Smart Contract Engineer at Secured Finance, developing a DeFi product.

WEB: https://tanakas.org/