LaravelとReactを利用してインスタの一覧を掲載

対象の環境

PCMacBook Pro (13-inch, 2017)
PHP7.3
Laravel8.21.0
React16.14.0

やったこと

InstagramグラフAPIをPHPのフレームワークであるLaravelで呼び出して、最新のインスタ投稿6件をReactに掲載します。フレームワークを導入すればWebアプリケーションの構築をルールに沿って進めていくことになり開発効率や保守性が上がるためとてもおすすめです。

Laravelは利用していませんが、WordPressにインスタ投稿を埋め込む記事を書いています。良かったらご覧ください。

進め方

PHPのフレームワークであるLaravelの環境構築をした後に、サーバー側やフロント側の設定やプログラミングを行なっていきます。

やること目的
PHP、composer、Reactの環境構築これがないとLaravelの構築とReactによるUIが用意できません
Laravelの環境構築PHPのフレームワークであるLaravelの設定を行います
Laravelの動作確認Laravelのデフォルト画面が掲載されることを確認します
InstagramグラフAPIの情報取得アクセストークンとビジネスアカウントIDが必要になります
サーバー側の設定Reactとの連携、API呼び出しの処理、
フロント側の設定Reactのプログラミングを行います
動作確認最後に動作確認を行います。

これから複数のファイルを編集、新規作成しますので表にまとめておきます。

新規作成/編集ファイル名格納先
編集welcome.blade.php[プロジェクトのフォルダ]/resources/views
コマンドで作成後に編集InstagramController.php[プロジェクトのフォルダ]/app/Http/Controllers
編集web.php[プロジェクトのフォルダ]/routes
新規作成App.js[プロジェクトのフォルダ]/resources/js/components
新規作成MyInstagram.js[プロジェクトのフォルダ]/resources/js/components
新規作成MyInstagram.css[プロジェクトのフォルダ]/resources/js/components

※[プロジェクトのフォルダ]はこの記事の例だとlaravel_reactです。

PHP、composer、Reactの環境構築

Homebrewを利用してPHP、composer、Reactの環境構築を行いました。

Homebrewのインストール
https://qiita.com/zaburo/items/29fe23c1ceb6056109fd
こちらが分かりやすいです。

Reactのインストール
https://qiita.com/EZ_Denta/items/9e6a47f330b5a01806ae
こちらが分かりやすかったです。

composerのインストール
Homebrewがインストールされていれば、以下のコマンドでインストールが可能です。

$ brew install composer

Laravelの環境構築

PHPのフレームワークであるLaravelの環境構築を行います。
https://reffect.co.jp/laravel/laravel6-react-router
こちらを参照して進めましたが、React-RouterやMySQLなど今回利用しないものは割愛しています。

Laravelのインストール
コマンドラインでアプリケーションを構築する適当な場所に移動して以下のコマンドを打ってください。このコマンドでlaravel_reactという名前のアプリケーションを作成します。

$ composer create-project --prefer-dist laravel/laravel laravel_react

Laravelのバージョンの確認
インストールしたLaravelのバージョンを確認するには、上記のコマンドでlaravel_reactというフォルダが直下にできているので、そこに移動して確認します。

$ cd laravel_react
$ php artisan --version
Laravel Framework 8.21.0

Laravel/uiのインストール
Laravel 6 からCSSに関する部分が別れたため、別にインストールが必要です。laravel_reactフォルダ上で以下のコマンドを実行します。

$ composer require laravel/ui

Reactの設定
Laravelは標準ではReactを利用するようになっていないので、Reactの設定を行います。下記のコマンドを実行するとLaravelのログイン機能とReactをインストールすることができます。
筆者の環境では、続けて”npm install && npm run dev”というコマンドを打てとメッセージが表示されたので続けて実行しました。

$php artisan ui react --auth
React scaffolding installed successfully.
Please run "npm install && npm run dev" to compile your fresh scaffolding.
Authentication scaffolding generated successfully.
$ npm install && npm run dev
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
...

Laravelの動作確認

ここまできたらLaravelのデフォルトの画面をブラウザでみることができます。
以下のコマンドをコマンドラインで実行します。

$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Sun Jan 10 17:42:39 2021] 127.0.0.1:56574 [200]: /favicon.ico
[Sun Jan 10 17:43:07 2021] 127.0.0.1:56590 [200]: /favicon.ico
[Sun Jan 10 17:47:38 2021] 127.0.0.1:56877 [200]: /favicon.ico
・・・

ブラウザで127.0.0.1:8000にアクセスすると以下のような画面が表示されていると思います。

これでLaravelの動作確認は一旦完了です。php artisan serveを止めるときはコマンドライン上でCtrl + Cを押してください。

InstagramグラフAPIの情報取得

InstagramグラフAPIをサーバのPHPで呼び出すには、Instagramの有効期限無制限のアクセストークンInstagramビジネスアカウントIDが必要です。以下のサイトを参照して取得してください。
https://navymobile.co.jp/instagram-graph-api

サーバー側の設定

今回はMac上で動作確認することができますが、本来はサーバーで動く処理について、ここから設定していきます。

フロントのトップページの設定
[プロジェクトのフォルダ]/resources/views/welcome.blade.phpを編集してApp.jsがトップページになるようにします。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">

        <!-- Styles -->
        <style>
            body {
                font-family: 'Nunito';
            }
        </style>
        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>

        <!-- Fonts -->
        <link rel="dns-prefetch" href="//fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>

外部APIを叩くための準備
今回、LaravelのPHPからInstagramグラフAPIを叩きます。外部のAPIを叩くにはLaravelにGuzzleを導入します。以下のサイトが参考になりました。
https://selegee.com/528/
https://yaba-blog.com/laravel-call-api/

laravel_reactフォルダ上で次のコマンドを実行することでGuzzleを導入できます。

$ composer require guzzlehttp/guzzle
Using version ^7.2 for guzzlehttp/guzzle
./composer.json has been updated
Running composer update guzzlehttp/guzzle
Loading composer repositories with package information

外部APIのコントローラーを作成
以下のコマンドを実行することで、InstagramグラフAPIにアクセスする処理を記載するPHPファイルが作成されます。

$ php artisan make:controller InstagramController
Controller created successfully.

[プロジェクトのフォルダ]/app/Http/ControllersにInstagramController.phpという名前で格納されます。

InstagramグラフAPIの呼び出し処理を記載
先程作成されたInstagramController.phpにInstagramグラフAPIを呼ぶ処理を記載します。ここで先程用意したInstagramの有効期限無制限のアクセストークンInstagramビジネスアカウントIDが必要になります。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class InstagramController extends Controller
{
    CONST USERID = '[InstagramビジネスアカウントID有効期限無制限のアクセストークン]';
    CONST POST_COUNT = '6';
    CONST RECENT_FIELDS = 'id,media_type,media_url,permalink';
    CONST ACCESS_TOKEN = '[有効期限無制限のアクセストークン]';

    //
    public function my_instagram_request() {
        $base_url = 'https://graph.facebook.com/v9.0/';
        $path = self::USERID.'?fields=name,media.limit('.self::POST_COUNT.'){media_type,caption,like_count,media_url,thumbnail_url,permalink,timestamp,username,comments_count}&access_token='.self::ACCESS_TOKEN;
         $client = new \GuzzleHttp\Client( [
          'base_uri' => $base_url,
        ] );
        $response = $client->request( 'GET', $path,[] );
        $response_body = (string) $response->getBody();
        echo $response_body;
    }

}

ルーティングファイルの編集
先程記入したInstagramグラフAPIの呼び出し処理をLaravel内で利用できるように[プロジェクトのフォルダ]/routes/web.phpに記載が必要になります。InstagramController.phpに記載した関数とURLを紐付けます。以下のサイトが参考になります。
https://qiita.com/yousan/items/2a4d9eac82c77be8ba8b
https://awesome-linus.com/2019/04/06/laravel-api-php-csrf/

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

//InstagramController.phpに記載した関数とURLを紐付ける
Route::get('/my_instagram_request', 'App\Http\Controllers\InstagramController@my_instagram_request');

APIの動作確認
ここまで準備できるとブラウザでAPIの実行結果を確認することができます。php artisan serveを実行後にブラウザで確認してください。

$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Sun Jan 10 17:42:39 2021] 127.0.0.1:56574 [200]: /favicon.ico
[Sun Jan 10 17:43:07 2021] 127.0.0.1:56590 [200]: /favicon.ico
[Sun Jan 10 17:47:38 2021] 127.0.0.1:56877 [200]: /favicon.ico
・・・

ブラウザで127.0.0.1/my_instagram_requestにアクセスすると、以下のようにAPIのレスポンスが表示されると思います。

フロント側の設定

フロントページを新規作成
Reactでフロントページを用意していきます。[プロジェクトのフォルダ]/resources/js/components/App.jsを新規作成して、以下のように記述します。今回は、MyInstagramタグを呼び出すようにしています。

import React from 'react';
import ReactDOM from 'react-dom';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
import 'react-tabs/style/react-tabs.css';
import MyInstagram from './MyInstagram.js'

function App() {
    return (
      <MyInstagram />
    )
}

if (document.getElementById('app')) {
    ReactDOM.render(<App />, document.getElementById('app'));
}

MyInstagram.jsの新規作成
続いて[プロジェクトのフォルダ]/resources/js/components/MyInstagram.jsを新規作成してInstagraグラフAPIのレスポンスを掲載します。以下のサイトが参考になりました。
https://stackoverflow.com/questions/56953440/uncaught-typeerror-this-state-map-is-not-a-function/56953546

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import './MyInstagram.css';

export default class MyInstagram extends Component {
    constructor() {
        super();

        this.state = {
            gets: []
        };
    }
    componentDidMount() {
        axios
            //.get('/ogipan_request')
            .get('http://127.0.0.1:8000/my_instagram_request')
            .then(response => {
                console.log("request is ------", response);
                this.setState({ gets: response.data.media.data, });
            })
            .catch(() => {
                console.log('通信に失敗しました');
            });
    }

    rendergets() {
        return this.state.gets.map(get => {
        //for (const get of this.state.gets) {
            //let type = get.media_type;
            //console.log(type);
            //let caption = get.caption;
            //   $caption = mb_convert_encoding($caption, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
            //caption = caption.replace('/\n/', '<br>');
            return (
                <div className="instagram-item">
                  <a className="instagram-card" href={get.permalink} target="_blank" rel="noopener noreferrer">
                    {(() => {
                      if (get.media_type == 'VIDEO') {
                        //return <img className="instagram-card__img" src={get.thumbnail_url} alt={get.caption.replace('/\n/', '<br>')} />
                        return (
                          <img className="instagram-card__img" src={get.thumbnail_url} alt={get.caption.replace('/\n/', '<br>')} />
                        );
                      }
                      else {
                        //return <img className="instagram-card__img" src={get.media_url} alt={get.caption.replace('/\n/', '<br>')} />
                        return (
                          <img className="instagram-card__img" src={get.media_url} alt={get.caption.replace('/\n/', '<br>')} />
                        );
                      }
                    })()}
                  </a>
                </div>
            );
        //}

        });
    }

    render() {
        return (
            <div className="instagram-container">
                {this.rendergets()}
            </div>
        );
    }

}

MyInstagram.cssを新規作成
最後にCSSの設定を行います。[プロジェクトのフォルダ]/resources/js/components/MyInstagram.CSSを新規作成して以下のように記載します。

.instagram-container{
  display: flex;
  flex-wrap: wrap;
  margin: 0 -1px;
}

.instagram-item{
  width: 50%;
  padding: 1px;
}

@media screen and (min-width: 768px){
  .instagram-item {
      width: 30%;
  }
}

a.instagram-card{
  display: block;
  position: relative;
  margin-bottom: 16px;
}

.instagram-card__img{
  max-width: 100%;
  height: auto;
  display: block;
}

動作確認

これで準備完了です。動作確認をするために、npm run watchコマンドを実行します。このコマンドを実行しておくと、今まで編集、新規作成したJSファイルをコンパイルして利用可能にしてくれます。

$ npm run watch

> watch
> mix watch

npm run watchは実行したままになるので、別のコマンドラインを立ち上げてphp artisan serveを実行してブラウザで動作確認します。

$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Sun Jan 10 17:42:39 2021] 127.0.0.1:56574 [200]: /favicon.ico
[Sun Jan 10 17:43:07 2021] 127.0.0.1:56590 [200]: /favicon.ico
[Sun Jan 10 17:47:38 2021] 127.0.0.1:56877 [200]: /favicon.ico
・・・

ブラウザで127.0.0.1:8000にアクセスすると以下のように自分のインスタの投稿が6件掲載されれば成功です!npm run watchもphp artisan serveと同様にCtrl + Cで止めることができます。

タイトルとURLをコピーしました