VueおよびOracle Content ManagementでのGraphQLの使用

イントロダクション

GraphQLは、APIの問合せ言語であり、既存のデータを使用してこれらの問合せを履行するためのランタイムです。

このチュートリアルでは、Oracle Content Managementに接続されているVueでGraphQLを使用する方法を示します。特に、GraphQLを使用して必要なデータを問い合せて、「最低限のサンプル・サイト」の既存の「人」ページに焦点を当てます。他のページ(「ホーム」および「問合せ先」)は現在REST APIを使用しています。

前提条件

このチュートリアルを進める前に、まず次の情報を確認することをお勧めします。

このチュートリアルに従うには、次のものが必要です。

オラクルが構築しているもの

Vueに組み込まれている最小限のサイトで、Oracle Content Managementリポジトリからイメージやその他のコンテンツを簡単に取得できます。

オラクルが構築している内容を確認するには、チュートリアルの終了状態、Oracle Content Managementのコンテンツを使用する基本的なビューの最小サイトを次に示します。

https://headless.mycontentdemo.com/samples/oce-vue-minimal-sample

このチュートリアルでは、このサイトの「担当者」ページのみに焦点を当てます。

このチュートリアルの最後にある「People」ページは次のようになります:

この画像は、Vue最小サイトの「連絡先」ページを示しています。

続行するには、Oracle Content Managementへのアクティブなサブスクリプションを持ち、コンテンツ管理者ロールでログインしている必要があります。

タスク1: Peopleページ分類の理解

ダウンロード可能なアセット・パックには、最小メインGraphQLというアセットがスラグ'minimalmainggraphql'とともに含まれています。

「最小メインGraphQL」アセットには、「個人」と呼ばれるPeoplePageタイプのページが含まれます。これは、次のような内容です。

このイメージは、メインのGraphQL分類の最小値を示しています。

「人」アセットのタイプはPeoplePageで、個人のサブタイプが含まれています。これは、次のような内容です。

この図は、「People」ページの分類を示しています。

peopleアセットの例を次に示します(「属性」パネルのすべてのメタデータに注意してください)。

このイメージは、Peopleタクソノミを示しています。

タスク2: VueでのGraphQLとのインタフェース

このチュートリアルの最初の目的は、vue-apolloライブラリを使用して、Oracle Content ManagementのGraphQLインタフェースとVue最小サンプル・アプリケーションの間で便利なインタフェースを提供することでした。残念ながら、このサンプルが記述された時点では、vue-apollo実装はVue 2.xアプリケーションでのみ使用でき、最小のVueサンプルではVue 3.xが使用されていました。Vue 3でapolloをサポートする作業は進行中ですが、このサンプルが作成された時点でアルファレベルの開発中でもあります。このため、このサンプルはクロスフェッチを使用して記述されました。このライブラリは、サーバー側とクライアント側の両方で使用するためのフェッチ実装を提供するライブラリです。GraphQL問合せは最終的にはPOSTリクエストのみであるため、これは引き続き機能しますが、vue-apollo実装が必要な場合よりも、若干低レベルのデータ操作が必要です。

既存のアプリケーション・レイアウト

データの取得

元の最低限のVueサンプルは、ホーム・ページおよび一意のスラグ値によって参照された動的ページという2つのページに移入するためにRESTコールを使用しました。どちらのページにも、ページに表示するHTMLブロックを表す1つ以上のセクション・エントリが含まれています。この共通レイアウトのため、両方のページで同じページ・テンプレートPage.vueを使用してデータを表示できます。サンプルで追加される「People」ページには、非常に類似した構造がありますが、GraphQLを使用して取得されたデータに基づいて構築されています。

ページ・ルーティング

アプリケーションのページのルーティングは、Vueルーター拡張機能を使用して処理されます。これにより、ホーム ページと、一意のスラグ値によって参照されるページ数の2つの特殊なケースにページを分割できます。これにより、アプリケーションのメイン・アセットに参照を追加し、前述のようにPeopleアセットを定義することで、アプリケーションに子ページをさらに追加できます。

VueX状態管理

ページに表示されるデータはすべて、VueXストアを使用して管理されます。これは、各ページのコンテンツの取得に使用されるコンテンツSDKのコールを担当します。

GraphQLサポートを含めるための更新

インストールの依存関係

最初のステップは、GraphQLサポートに必要なライブラリをインストールすることです。必要に応じてプロキシサポートを提供するために、クロスフェッチをインストールしてブラウザとサーバー側のネットワークアクセスの両方を提供し、https-proxy-agentを指定する必要があります。次のコマンドを実行して実行します。

npm install cross-fetch https-proxy-agent

GraphQL問合せを実行するための新規コードの追加

次に、src/scripts/graphql-service.jsという新しいファイルを作成します。このファイルは、GraphQL問合せを実行し、結果から有用なフィールドを抽出します。これは、Oracle Content ManagementへのPOSTコールを行うためのクロスフェッチ問合せを作成することによって行われます。GraphQLデータは、POSTコールの本文で渡されます。

export default async function fetchPeopleData(peopleSlug) {
      try {
        const channelToken = `${process.env.CHANNEL_TOKEN}`;
        const fetchParams = {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            query: `
          {
            getPeoplePage(slug: "${peopleSlug}", channelToken: "${channelToken}" ) {
              id
              slug
              name
              fields {
                announcement {
                  id
                  fields {
                    type: fieldType
                    heading
                    body
                    actions
                    image {
                      ...sectionImages
                    }
                  }
                }
                people {
                  id
                  fields {
                    fullname
                    title
                    biodata
                    file {
                      metadata {
                        width
                        height
                      }
                    }
                    renditions {
                      name
                      format
                      file {
                        url
                        metadata {
                          height
                          width
                        }
                      }
                    }
                  }
                }
              }
            }
          }
          fragment sectionImages on image {
            id
            fields {
              file {
                metadata {
                  width
                  height
                }
              }
              renditions {
                name
                format
                file {
                  url
                  metadata {
                    height
                    width
                  }
                }
              }
            }
          }
          `,
          }),
        };
    
        // Figure out if we need a proxy. Only needed on server-side render
        if (typeof window === 'undefined' && typeof process === 'object') {
          const proxyServer = process.env.SERVER_URL.startsWith('https')
            ? process.env.oce_https_proxy : process.env.oce_https_proxy;
          if (proxyServer) {
            const proxy = createHttpsProxyAgent(proxyServer);
            fetchParams.agent = proxy;
          }
        }
    
        const response = await fetch(`${process.env.SERVER_URL}/content/published/api/v1.1/graphql`, fetchParams);
        const queryResult = await response.json();
        return extractRequiredPeopleFields(queryResult);
      } catch (error) {
        console.log(error);
        return ('');
      }
    }

また、コールから返されるJavaScriptオブジェクトを単純化するために使用される小さいユーティリティ関数もあります。その結果、データの不要なネストが減少します。

function extractRequiredPeopleFields(queryResult) {
      const result = { announcement: {}, people: [] };
    
      result.slug = queryResult.data.getPeoplePage.slug;
      result.name = queryResult.data.getPeoplePage.name;
      const base = queryResult.data.getPeoplePage.fields;
      if (base.announcement) {
        result.announcement = base.announcement.fields;
      }
      if (base.people) {
        result.people = base.people;
      }
      return result;
    }

VueXストアの問合せコードの統合

これでデータを取得できるようになったため、次のステップは、アプリケーションがデータを管理するために使用するVueXストアに格納することです。これは、次のユーティリティ関数およびストレージを追加して、src/vuex/index.jsで行われます。

    state.peoplePageData // storage for the object data
        // get the data for the People Page given its slug
        fetchPeoplePage({ commit }, pageSlug) {
          return fetchPeopleData(pageSlug).then((data) => {
            commit('setPeoplePageData', data);
          });
        },
        // setter function used to update the state
        setPeoplePageData(state, data) {
          state.peoplePageData = data;
        },

個人情報ページを表示するコードの分岐

次に、アプリケーションが「Person(個人)」ページをレンダリングするためのコードをコールできる統合ポイントを作成します。これを行うには、Slug値に基づいて条件付きでレンダリングされるようにPage.vue内の既存のコードを変更します。これは、src/pages/Page.vueにあります。


    <!-- Component for the Page. -->
    // The template will conditionally render different sections depending 
    // on the value of the unique slug of the current page. 
    <template>
      <div v-if="data.hasError">
        <Error :errorObj="data" />
      </div>
      <div v-else-if="data.slug === 'people'">
        <Person :gqldata ="data"/>
      </div>
      <div v-else>
        <section :class="`content ${section.fields.type}`" :key="section.id"
          v-for="(section) in data.fields.sections">
          <Section :section="section" />
        </section>
      </div>
    </template>
    
    ....
    // Likewise, the fetch data call will seek out different data from the VueX store depending on 
    // the desired slug
    methods: {
        fetchData() {
          let data = {};
          // People is a special case and is handled by GraphQL in the store
          if (this.slug === 'people') {
            data = this.$store.dispatch('fetchPeoplePage', this.slug);
          } else {
          // return the Promise from the action
            data = this.$store.dispatch('fetchPage', this.slug);
          }
          return data;
        },
      },

Peopleページ・コンテンツをレンダリングするコードの追加

次に、「個人情報」ページのコンテンツを生成するコンポーネントを作成します。このコードは、ソース/コンポーネント/Person.vueにあります。レンダリングはかなり簡単です。コンポーネント内の実行可能コードは、渡されたページ・スラグ(この場合は常に「人」)を取得し、VueXキャッシュから対応するデータを取得します。これにより、次のテンプレートで使用される単純なJavaScriptオブジェクトが返されます。

<template>
      <div >
        <section  class="announcement">
            <div>
          <picture v-if="gqldata.announcement.image && renditionURLs">
          <source type="image/webp" :srcSet="renditionURLs.srcset" />
          <source :srcSet="renditionURLs.jpgSrcset" />
          <img
            id="header-image"
            :src="renditionURLs.large"
            alt="Company Logo"
            :width="renditionURLs.width"
            :height="renditionURLs.height"
          />
          </picture>
              <div class="textcontent">
                <h1>{{gqldata.announcement.heading}}</h1>
              </div>
              <div class="text">
                <div v-html=cleanContent></div>
              </div>
            </div>
        </section>
          <div class="all_people">
          <div v-for="person in gqldata.people" :key="person.id" >
            <section className="person">
              <img className="profile_picture"
              :src="person.fields.renditions[0].file.url" :alt="person.fields.fullname" />
              <div className="profile">
                <div className="profile_name">{{person.fields.fullname}}</div>
                <div className="profile_position">{{person.fields.title}}</div>
                <div className="profile_description">{{person.fields.biodata}}</div>
              </div>
            </section>
          </div>
        </div>
      </div>
    </template>

コンポーネントの残りのコードは、VueXストアからデータをプルするために使用されます。

新規CSSの追加

最後に、必要なCSSスタイルをアプリケーションに追加します。これはsrc/assets/global.cssにあります。

/*
    * Styles for the people cards
    */
    .all_people {
      margin: 0 auto;
      max-width: 1000px;
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 50px;
      margin-top: 50px;
      margin-bottom: 50px;
    }
    
    .person {
      margin: 0 auto;
      width: 300px;
      box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 2px 4px rgba(0, 0, 0, 0.5), 0px -2px 2px rgba(0, 0, 0, 0.15);
      height: 100%;
    }
    
    .profile {
      padding: 20px 15px;
    }
    
    .profile_picture {
      width: 100%;
      height: 200px;
      object-fit: cover;
      display: block;
    }
    
    .profile_name {
      text-align: center;
      font-size: 16px;
      font-weight: bold;
    }
    
    .profile_position {
      text-align: center;
      font-size: 12px;
      color: rgba(0, 0, 0, 0.7);
      margin-bottom: 18px;
    }
    
    .profile_description {
      font-size: 14px;
      display: flex;
      justify-content: center;
    }

まとめ

このチュートリアルでは、GraphQLを使用して「最低限必要なサンプル・サイト」に「人」ページを追加して、そのページのデータを取得しました。他のページ(「ホーム」および「問合せ先」)では、引き続きREST APIが使用されます。

Oracle Content ManagementのGraphQLの詳細は、ドキュメントを参照してください。