Edited at

Movable Type 7 のコンテンツタイプ+Data API Ver4 で、レシピサイトのウェブアプリを開発してみる

More than 1 year has passed since last update.

前回の記事の続きです。 

前回作成したレシピサイトをベースに、Movable Type 7 に実装予定の「Data API Ver.4」を利用して、コンテンツタイプのデータと連携したアプリを作ってみます。

※ 2018年4月4日時点、MT7ベータ3を前提に作成・記述しています。仕様の変更に伴い、動作が異なる場合があります。変更がありましたら随時修正します。


Data API Ver 4 のエンドポイント

Movable Type 7 の Data API のエンドポイントは、Ver3 までと変わらず

http://your-mt/mt-data-api.cgi/v4/sites/サイトID/

となります。


  • Data API のCGIファイル

  • APIのバージョン

  • sites

  • サイトID

の順番に指定してきます。

コンテンツタイプのデータを操作する場合、キャメルタイプで「contentTypes」と入力し、合わせてコンテンツタイプのIDを指定します。

例として


  • サイトIDが1

  • コンテンツタイプのIDが2

  • コンテンツタイプに登録されているコンテンツのデータ一覧を取得したい

この場合、エンドポイントは以下のようになります。

http://your-mt/mt-data-api.cgi/v4/sites/1/contentTypes/2/data


  • サイトIDが3

  • コンテンツタイプのIDが4

  • コンテンツタイプに登録されているコンテンツデータのIDが5

  • 5の詳細データを取得したい

この場合、エンドポイントは以下のようになります。

http://your-mt/mt-data-api.cgi/v4/sites/3/contentTypes/4/data/5

Data API経由でデータの操作を行う場合、環境変数でCORS(Cross-Origin Resource Sharing) の設定を適切に行う必要があります。

環境変数は以下のようなものがあります。


Data API Ver4 でコンテンツタイプのデータを取得する際の注意

Data APIから取得できるデータの順番は、コンテンツタイプの設定画面で登録した順番に準じます。

今回の例では、コンテンツタイプを


  1. レシピ名

  2. 写真

  3. 材料

  4. 作り方

の順番に登録しました。この場合、Data API Ver4で取得できるデータサンプルは以下のようになります。

サンプルJSON

{

"author": {
"displayName": "tosanai",
"userpicUrl": null
},
"basename": "d02298c174f3b63275de13c6b071b8753eff6c74",
"blog": {
"id": "1"
},
"createdDate": "2018-04-04T14:59:51+09:00",
"data": [
{
"data": "味たまご",
"id": "8",
"label": "レシピ名",
"type": "single_line_text"
},
{
"data": [
"3"
],
"id": "9",
"label": "写真",
"type": "asset_image"
},
{
"data": [
"たまご",
"めんつゆ",
"みりん"
],
"id": "10",
"label": "材料",
"type": "list"
},
{
"data": "めんつゆとみりん、水の比率が 1:1:2 で混ぜ合わせ、鍋で一度沸騰させ、冷まします。\r\n鍋でお湯を沸かします。\r\n常温にした卵をお湯に入れ中火で6分半煮ます。\r\n卵を冷水などで冷やし、卵の殻をむきます。\r\n冷ました付け汁に卵を入れ、冷蔵庫で1日以上置きます。",
"id": "11",
"label": "作り方",
"type": "multi_line_text"
}
],
"date": "2018-04-04T14:58:31+09:00",
"id": 3,
"label": "味たまご",
"modifiedDate": "2018-04-04T14:59:51+09:00",
"permalink": "http://example.com/d02298c174f3b63275de13c6b071b8753eff6c74.html",
"status": "Publish",
"updatable": false
}

コンテンツタイプの設定画面で、以下のように登録順序を変えた場合


  1. 写真

  2. 材料

  3. 作り方

  4. レシピ名

取得できるJSONデータは以下のように変更になります。

サンプルJSON



{
"author": {
"displayName": "tosanai",
"userpicUrl": null
},
"basename": "d02298c174f3b63275de13c6b071b8753eff6c74",
"blog": {
"id": "1"
},
"createdDate": "2018-04-04T14:59:51+09:00",
"data": [
{
"data": [
"3"
],
"id": "9",
"label": "写真",
"type": "asset_image"
},
{
"data": [
"たまご",
"めんつゆ",
"みりん"
],
"id": "10",
"label": "材料",
"type": "list"
},
{
"data": "めんつゆとみりん、水の比率が 1:1:2 で混ぜ合わせ、鍋で一度沸騰させ、冷まします。\r\n鍋でお湯を沸かします。\r\n常温にした卵をお湯に入れ中火で6分半煮ます。\r\n卵を冷水などで冷やし、卵の殻をむきます。\r\n冷ました付け汁に卵を入れ、冷蔵庫で1日以上置きます。",
"id": "11",
"label": "作り方",
"type": "multi_line_text"
},
{
"data": "味たまご",
"id": "8",
"label": "レシピ名",
"type": "single_line_text"
}
],
"date": "2018-04-04T14:58:31+09:00",
"id": 3,
"label": "名前がありません。",
"modifiedDate": "2018-04-04T14:59:51+09:00",
"permalink": "http://example.com/d02298c174f3b63275de13c6b071b8753eff6c74.html",
"status": "Publish",
"updatable": false
}

data オブジェクトの順番が入れ替わっていることが分かります。

Ver 4 では、コンテンツタイプのエンドポイントからは、画像などのアイテムの詳細情報が取得できません。このため、画像の詳細データを取得する場合


  • コンテンツタイプのJSONからアイテムのIDを取得

  • アイテムIDを元に、再度Data APIに問い合わせて、詳細情報を取得

という手続きを踏む必要があります。

例として、下記のようなコンテンツタイプデータだった場合



"data": [
{
"data": "味たまご",
"id": "8",
"label": "レシピ名",
"type": "single_line_text"
},
{
"data": [
"3"
],
"id": "9",
"label": "写真",
"type": "asset_image"
},
{
"data": [
"たまご",
"めんつゆ",
"みりん"
],
"id": "10",
"label": "材料",
"type": "list"
},
{
"data": "めんつゆとみりん、水の比率が 1:1:2 で混ぜ合わせ、鍋で一度沸騰させ、冷まします。\r\n鍋でお湯を沸かします。\r\n常温にした卵をお湯に入れ中火で6分半煮ます。\r\n卵を冷水などで冷やし、卵の殻をむきます。\r\n冷ました付け汁に卵を入れ、冷蔵庫で1日以上置きます。",
"id": "11",
"label": "作り方",
"type": "multi_line_text"
}
],

写真のアイテムIDは「3」です。このため、アイテムのデータを取得するエンドポイントに対して、アイテムIDの番号[3]を指定して必要なデータを取得する必要があります。


レシピ一覧のページをData API Ver 4 で生成する

以上を踏まえて、Data API Ver4を利用してレシピコンテンツのアプリを作ってみます。


アプリの仕様

アプリの仕様は以下とします。



  • 前回の記事で作成したレシピサイトのコンテンツタイプを利用する

apli_gaiyou.png


  • トップページ、詳細ページの2枚のhtmlで構成

  • Data API Ver4 からJSONデータを取得して、JavaScript でレンダリングする

  • トップページのURL



  • 詳細ページのURL




  • 前回の記事 で作成したウェブサイトと同じデザインとする

以下


  • レシピの一覧データの表示

  • レシピの詳細データの表示

の順に記述します。

最初は、レシピの一覧データの操作について記述します。


一覧画面の開発

最初は、前回のトップページに相当する一覧ページを作ります。

コンテンツタイプの一覧データのJSONを取得し、axiosで通信を行い、Vue.js でレンダリングします。


  • コンテンツタイプの一覧データを Data API へ問い合わせ

  • 登録されたデータの数だけ for 文でループ処理を行い、以下の3つを含むオブジェクトを生成


    • コンテンツタイプに登録されたデータのID

    • 写真のアイテムID

    • レシピ名



  • 生成したオブジェクトをPromise チェーンで次の関数へ引き渡す

  • オブジェクトを元に再度ループ。写真のサムネイルデータを取得。以下のデータをVue.js のインスタンスへ格納する。


    • データのIDを含むリンク文字列

    • サムネイルのURL

    • レシピ名



以下、コードです。


<!DOCTYPE html>
<html lang="ja" xml:lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,user-scalable=no,maximum-scale=1" />
<title>レシピブック</title>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://your-site/style.css">
</head>
<body id="topPage" class="headerBg">

<div class="wrapper">

<div id="TopHeader">
<h1>レシピブック</h1>
</div>

<h2>新着一覧</h2>

<div id="entries" class="listWrapper">

<div class="list" v-for="item in json">
<div class="thum">
<a v-bind:href="item.link">
<img :src="item.thumbnailURL">
<div class="listDescription"><span class="listDescriptionTxt">{{ item.name }}</span></div>
</a>
</div>
</div>

</div>
</div>

<script>

"use strict";

const list = axios('https://your-mt/mt-data-api.cgi/v4/sites/サイトID/contentTypes/コンテンツタイプID/data');

const vm = new Vue({
el: '#entries',
data: {
json: {},
},
});

Promise.resolve(list).then(function(response) {

let json = {};

for (let i in response.data.items) {

let ContentData = {};

ContentData.id = Number(response.data.items[i].id);
ContentData.ImageID = Number(response.data.items[i].data[1].data);
ContentData.name = response.data.items[i].data[0].data;

json[i] = ContentData;

}

return json;

}).then(json => {

for (let i in json) {

axios('https://your-mt/mt-data-api.cgi/v4/sites/サイトID/assets/' + json[i].ImageID + "/thumbnail?width=220&square=true")

.then(res => {

let result = {};

result.link = "./detail.html?id=" + json[i].id;
result.thumbnailURL = res.data.url;
result.name = json[i].name;

Vue.set(vm.json, i, result);

});
}
});

</script>

</body>
</html>


レシピ詳細画面の開発

次に、レシピの詳細画面を開発します。

トップページからリンクされたページには、パラメータとして「id=**」という文字列が付与されています。このIDを元に、Data API Ver.4 へ問い合わせを行います。

コンテンツタイプの詳細データJSONを取得し、Vue.js でレンダリングします。

以下のような考え方で作ってみました。


  • コンテンツタイプのIDを元に Data API へ問い合わせ

  • レシピ名、レシピの写真、材料、作り方のJSONデータを取得

  • 写真のIDを元に、Data API へ再問い合わせ

  • 最初のJSONデータと、写真のIDをもとに取得したJSONデータを合わせて、オブジェクトを生成

  • オブジェクトをVue.js のインスタンスへ格納、レンダリングを実行する

以下、コードです。


<!DOCTYPE html>
<html lang="ja" xml:lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,user-scalable=no,maximum-scale=1" />
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://your-site/style.css">
<title>レシピブック</title>

</head>
<body id="entryPage" class="headerBg">

<div id="vm" class="wrapper">
<div class="detailWrapper">
<h1 class="mainTitle" v-if="label">{{label}}</h1>
<div class="detail">

<div class="mainImg" v-if="imgsrc"><img :src=imgsrc ></div>

<div class="detailInner">
<h2>材料</h2>

<div v-if="ingredients" v-for="item in ingredients">
<p class="mb5">{{item}}</p>
</div>

<h2>作り方</h2>

<div v-if="recipe">
{{recipe}}
</div>

</div>
</div>
<div class="backBtn">
<a href="./index.html">トップへ戻る</a>
</div>
</div>
</div>

<script>

"use strict";

// コンテツタイプのデータIDを取得するため、GETで渡された文字列を解析する関数 [getUrlParams] を定義

function getUrlParams() {
const query = window.location.search.substring(1);
const vars = query.split('&');
let args = {};
for (let i = 0; i < vars.length; i++) {
let tmp = vars[i].split('=');
args[tmp[0]] = tmp[1];
}
return args;

}

// 関数 [getUrlParams] を使って、GETの引数を変数 params に格納

const params = getUrlParams();

// Vue.js のインスタンスを生成

const vm = new Vue({
el: '#vm',
data: {
label : '',
ingredients : '',
recipe : '',
imgsrc : '',
},
created: function() {
this.getData();
},

methods: {

getData: function() {

axios('https://your-mt/mt-data-api.cgi/v4/sites/サイトID/contentTypes/コンテンツタイプID/data/' + params.id).then(res => {

const json = res.data;

vm.label = json.data[0].data;
vm.ingredients = json.data[2].data;
vm.recipe = json.data[3].data;

return json;

}).then(json => {

const AssetEndpoint = 'https://your-mt/mt-data-api.cgi/v4/sites/サイトID/assets/' + json.data[1].data;
axios(AssetEndpoint).

then (AssetResponse => {
this.imgsrc = AssetResponse.data.url;
});

}).catch(function(error) {
console.log(error);
});
}
},
})
.catch(function(error) {

console.log(error);

});

</script>

</body>
</html>


jQuery 版

Vue.js で書いた内容を、jQueryでも書いてみました。

一覧画面

<!DOCTYPE html>

<html lang="ja" xml:lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,user-scalable=no,maximum-scale=1" />
<title>レシピブック</title>
<link rel="stylesheet" href="https://your-site/style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body id="topPage" class="headerBg">

<div class="wrapper">

<div id="TopHeader">
<h1>レシピブック</h1>
</div>

<h2>新着一覧</h2>

<div id="entries" class="listWrapper">
</div>

</div>

<script>

"use strict";

const list = $.get('https://your-mt/mt-data-api.cgi/v4/sites/サイトID/contentTypes/コンテンツタイプID/data');

Promise.resolve(list).then(response => {

let json = {};

for (let i in response.items) {

let ContentData = {};

ContentData.id = Number(response.items[i].id);
ContentData.ImageID = Number(response.items[i].data[1].data);
ContentData.name = response.items[i].data[0].data;

json[i] = ContentData;

}

return json;

}).then(json => {

for (let i in json) {

$.get('https://your-mt/mt-data-api.cgi/v4/sites/サイトID/assets/' + json[i].ImageID + "/thumbnail?width=220&square=true")

.then(res => {

let result = '';

result += '<div class="list"><div class="thum">';
result += '<a href="./detail.html?id=' + json[i].id + '">';
result += '<img src="' + res.url + '" />';
result += '<div class="listDescription"><span class="listDescriptionTxt">' + json[i].name + '</span></div>';
result += '</a>';
result += '</div></div>';

$("#entries").append(result);

});
}
})

.catch(function(error) {

console.log(error);

});

</script>

</body>
</html>

詳細画面


<!DOCTYPE html>
<html lang="ja" xml:lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,user-scalable=no,maximum-scale=1" />

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<title>レシピブック</title>

<link rel="stylesheet" href="https://your-site/style.css">

</head>
<body id="entryPage" class="headerBg">

<div id="detail" class="wrapper">
<div class="detailWrapper">
<h1 class="mainTitle"></h1>
<div class="detail">

<div class="mainImg"></div>

<div class="detailInner">
<h2>材料</h2>

<div id="ingList">

</div>

<h2>作り方</h2>

<div id="recipe">

</div>

</div>
</div>
<div class="backBtn">
<a href="./index.html">トップへ戻る</a>
</div>
</div>
</div>

<script>

"use strict";

// コンテツタイプのデータIDを取得するため、GETで渡された文字列を解析する関数 [getUrlParams] を定義

function getUrlParams() {
const query = window.location.search.substring(1);
const vars = query.split('&');
let args = {};
for (let i = 0; i < vars.length; i++) {
let tmp = vars[i].split('=');
args[tmp[0]] = tmp[1];
}
return args;
}

// 関数 [getUrlParams] を使って、GETの引数を変数 params に格納

const params = getUrlParams();

$.get('https://your-mt/mt-data-api.cgi/v4/sites/サイトID/contentTypes/コンテンツタイプID/data/' + params.id)
.then(res => {

const label = res.data[0].data;
const ingredients = res.data[2].data;
const recipe = res.data[3].data;

$(".mainTitle").text(label);

for (let i in ingredients) {
$("#ingList").append('<p class="mb5">' + ingredients[i] + '</p>');
}

$("#recipe").text(recipe);

return res;

}).then(res => {

const AssetEndpoint = 'https://your-mt/mt-data-api.cgi/v4/sites/サイトID/assets/' + res.data[1].data;

$.get(AssetEndpoint)
.then(AssetResponse => {

$(".mainImg").html('<img src="' + AssetResponse.url + '" />');

});

})

.catch(function(error) {

console.log(error);

});

</script>

</body>
</html>

以上、Data API Ver 4を利用した、コンテンツタイプのサンプルを考えてみました。


免責

ここで記述しているコードはサンプルコードとなり、サポートは致しかねる旨、ご理解とご了承ください。