16
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React/Redux + Laravelでインスタグラムライクな方法でWebアプリを作った

Last updated at Posted at 2018-05-11

#■はじめに
ReactとPHPの連携で検索するとcomponentDidMountのときにfetchでデータを取ってくる方法が多いように思いますが、これじゃない感がありました。
たまたまインスタグラムのページのソースコードでも眺めてみようと思い、見ていると、reactを使っているらしいことがわかりましたが、それより注目したのがwindow._sharedDataというJSONデータが埋め込まれていることでした。
フレームワークでページを動的に生成しているものと思われます。
そこでこのインスタグラムライクなwindow._sharedDataを埋め込む方法でアプリを作成してみました。

#■1. create-react-app
まずReactでアプリを作成します。

create-react-app instagram_like
cd instagram_like
npm start

#■2. インストール
reduxのインストール

cd instagram_like
npm install redux
npm install react-redux
npm install redux-devtools
npm install redux-thunk
npm install redux-logger

react-router-domのインストール

cd instagram_like
npm install react-router
npm install history
npm install react-router-dom
npm install react-router-redux

#■3. ディレクトリ
instagram_like配下に次のディレクトリを作成

actions
components
constants
containers
reducers

#■4. window._sharedDataの埋め込み
まずReact開発環境でアプリを作成するため、後でPHPで埋め込むデータwindow._sharedDataのテストデータをpublic/index.htmlに埋め込んでおきます。

public/index.html
    <script>
    window._sharedData= {
        "entry_data": {
            "PostPage":[ {
                "graphql": {
                    "shortcode_media": {
                        "__typename":"GraphImage",
                        "id":"1418168299602868211",
                        "shortcode":"BOuWI4BjHfz",
                        "dimensions": {
                            "height": 719, "width": 1080
                        }
                        ,
                        "media_preview":"ACob1zGkqYPzcYyeprElgMMnyD5e/t/n/wCvW0uFQDtgD9Kb5qqxzg5GccflUvVgZDQM3J6euD/OqsgYdRnH+cd6157lYjs+7GTwePzA7j8fpmocxiQCfJDcLgdSfbFTZjMQncMjtSYX1q6YcFgV2+nvyeegqMW3HVfzphsXkupN6oxymOmM9B7c46fXNVmnlLuc/KnVf9jOOPw5NRiRgNoOACcfmf8APNQRE/Oe4Rv6VduvkG1iWXcuC2flbap5wQMFdvsB/k1caYq/3wACCfz79wB6YHaqisXADchSuB6c1sGyhMTOV+ZupyeefrVPXQnYz7y4OFwQ2c4I5Hv9ar7F7gZ78mkdAblIv4BgAfhn69akkjXceO5/nWb8il3Z/9k=",
                        "display_url":"https://scontent-nrt1-1.cdninstagram.com/vp/6e66efbd25293a921cd22e47c17764e0/5B9C768B/t51.2885-15/e35/15624585_764711637019057_7120075205669552128_n.jpg",
                        "display_resources":[ {
                            "src": "https://scontent-nrt1-1.cdninstagram.com/vp/495c961e30443a6f995090a6bead1c4a/5B856AEA/t51.2885-15/s640x640/sh0.08/e35/15624585_764711637019057_7120075205669552128_n.jpg", "config_width": 640, "config_height": 426
                        }
                        ,
                        {
                            "src": "https://scontent-nrt1-1.cdninstagram.com/vp/0fc749d0cee710512e1960efdfd30cbc/5B93CCC7/t51.2885-15/s750x750/sh0.08/e35/15624585_764711637019057_7120075205669552128_n.jpg", "config_width": 750, "config_height": 499
                        }
                        ,
                        {
                            "src": "https://scontent-nrt1-1.cdninstagram.com/vp/6e66efbd25293a921cd22e47c17764e0/5B9C768B/t51.2885-15/e35/15624585_764711637019057_7120075205669552128_n.jpg", "config_width": 1080, "config_height": 719
                        }
                        ],
                        "is_video":false,
                        "edge_media_to_caption": {
                            "edges":[ {
                                "node": {
                                    "text": "\u3042\u3051\u307e\u3057\u3066\u304a\u3081\u3067\u3068\u3046\u3054\u3056\u3044\u307e\u3059\ud83c\udf8d\ud83c\udf8d\n\u307f\u306a\u3055\u3093\u3069\u3046\u304a\u904e\u3054\u3057\u3067\u3057\u3087\u3046\u304b\uff01\uff1f\n\uff12\uff10\uff11\uff17\u5e74\u3082\u30a4\u30ed\u30e0\u30af\u3092\u4f55\u5352\u3088\u308d\u3057\u304f\u304a\u9858\u3044\u3044\u305f\u3057\u307e\u3059\ud83c\udf05\n\u5199\u771f\u306f\u884c\u304f\u305e\u30fc\u7684\u306a\u5199\u771f\uff01"
                                }
                            }
                            ]
                        }
                        ,
                        "edge_media_to_comment": {
                            "count":1,
                            "page_info": {
                                "has_next_page": false, "end_cursor": null
                            }
                            ,
                            "edges":[ {
                                "node": {
                                    "id":"17846398948162135",
                                    "text":"\u3042\u3051\u307e\u3057\u3066\u304a\u3081\u3067\u3068\u3046\u3054\u3056\u3044\u307e\u3059\ud83c\udf8d\uff012016\u5e74\u306f\u30a4\u30ed\u30e0\u30af\u306e\u97f3\u697d\u306b\u51fa\u4f1a\u3063\u3066\u3001\u30e9\u30a4\u30d6\u306b\u3082\u884c\u3051\u3066\u7d20\u6575\u306a\u5e74\u3067\u3057\u305f\u263a\ud83d\udc972017\u5e74\u3082\u30e9\u30a4\u30d6\u884c\u3051\u307e\u3059\u3088\u3046\u306b\uff01\u3053\u308c\u304b\u3089\u3082\u5fdc\u63f4\u3057\u3066\u307e\u3059(\u0e51\uff65\u0311\u25e1\uff65\u0311\u0e51)\uff01\uff01\uff01",
                                    "created_at":1483345890,
                                    "owner": {
                                        "id": "2977883515", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/e247d3d990af2efd5d3b2ee5c05b63c7/5B97A5D0/t51.2885-19/s150x150/23823421_518036085236629_1235652405907947520_n.jpg", "username": "krn___0111"
                                    }
                                }
                            }
                            ]
                        }
                        ,
                        "taken_at_timestamp":1483278857,
                        "edge_media_preview_like": {
                            "count":54,
                            "edges":[ {
                                "node": {
                                    "id": "1318483545", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/4448ff89af1aef43c7cdea2fd455bfa4/5B866180/t51.2885-19/s150x150/28766206_152048362133455_3871769183184224256_n.jpg", "username": "nnm813"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "282654007", "profile_pic_url": "https://scontent-frt3-1.cdninstagram.com/vp/856b9478629f7c2f4ae549c4c8cc5dd7/5B94597A/t51.2885-19/11906329_960233084022564_1448528159_a.jpg", "username": "ryusin5"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "354626907", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/694dbc7b80d476d200ca69a206ed910a/5B77E51F/t51.2885-19/s150x150/17662411_915294138573097_6100425398091251712_a.jpg", "username": "nnea26"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "3427312785", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/efdadbd7736b2ae2c04f0d4a3973cd81/5B83AFE2/t51.2885-19/s150x150/13636244_148622568894353_2026179178_a.jpg", "username": "mino_mucho"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "2680071939", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/fa7da14e359fa1e176a6a58485e62ba5/5B97062B/t51.2885-19/s150x150/30953906_165210784163203_5439584193577222144_n.jpg", "username": "b___chi03"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "2167399340", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/cc76a23f4b750da84e5da10632d72080/5B8E80E4/t51.2885-19/s150x150/30085659_182081129268861_5548356254688083968_n.jpg", "username": "rktsiii"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "1958134017", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/eb1b0e11fd332179a9d49f7b278b221b/5B7750C9/t51.2885-19/s150x150/26156903_135638420441941_4769086334918721536_n.jpg", "username": "akurahotam3201"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "1184178809", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/5ec0bfc77242987241b0947fd1156a3a/5B791B06/t51.2885-19/s150x150/29089751_1615548411892122_7820419247334490112_n.jpg", "username": "_kairi_t"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "1253575861", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/9570723853d74db6d434113223c5750c/5B771A90/t51.2885-19/s150x150/30603984_1631773820263962_1485500506171244544_n.jpg", "username": "goootrrr"
                                }
                            }
                            ,
                            {
                                "node": {
                                    "id": "1783453030", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/ed5f2ad408bd21b34d3c37840c6d8aeb/5B95ABD2/t51.2885-19/s150x150/27579913_1952399085076934_8398136771493232640_n.jpg", "username": "iro_mana_muku"
                                }
                            }
                            ]
                        }
                        ,
                        "owner": {
                            "id": "4159115721", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/dea5194bedde9964e31601f976b7f7c4/5B920B2D/t51.2885-19/s150x150/14719801_1675576302772916_3410902154188161024_a.jpg", "username": "iromuk", "blocked_by_viewer": false, "followed_by_viewer": false, "full_name": "\u30a4\u30ed\u30e0\u30af\u516c\u5f0f\u30a4\u30f3\u30b9\u30bf\u30b0\u30e9\u30e0", "has_blocked_viewer": false, "is_private": false, "is_unpublished": false, "is_verified": false, "requested_by_viewer": false
                        }
                    }
                }
            }
            ]
        }
        ,
        "hostname":"www.instagram.com"
    };
    </script>

#■5. sharedDataをruduxする

reducers/sharedData.js

import {SET_SHARED_DATA_ACTION} from '../constants/ActionTypes'

const initialState = {
  sharedData: {}
}

const _sharedData = (state, action) => {
  switch (action.type) {
    case SET_SHARED_DATA_ACTION:
      return action.sharedData
    default:
      return state
  }
}

export const getSharedData = state =>
  state.sharedData

const sharedData = (state = initialState, action) => {
  return {
    sharedData: _sharedData(state.sharedData, action)
  }
}

export default sharedData;
actions/index.js
import * as types from '../constants/ActionTypes'

const setSharedDataAction = sharedData => ({
  type: types.SET_SHARED_DATA_ACTION,
  sharedData
})

export const setSharedData = sharedData => (dispatch, getState) => {
  dispatch(setSharedDataAction(sharedData))
}
constants/ActionTyps.js
export const SET_SHARED_DATA_ACTION = 'SET_SHARED_DATA_ACTION'
reducer/index.js
import { combineReducers } from 'redux'
import { routerReducer } from 'react-router-redux'
import sharedData, * as fromSharedData from './sharedData'

export default combineReducers({
  routing: routerReducer,
  sharedData
})

export const getSharedData = state => fromSharedData.getSharedData(state.sharedData)
store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { createLogger } from 'redux-logger'
import createHistory from 'history/createBrowserHistory'
import { syncHistoryWithStore } from 'react-router-redux'
import reducer from './reducers'

const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger())
}

const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

const _history = createHistory()
export const history = syncHistoryWithStore(_history, store)

export default store

#■6. sharedDataのセット

index.js
import React from 'react'
import ReactDOM from 'react-dom'
//import registerServiceWorker from './registerServiceWorker'
import { Provider } from 'react-redux'
import { Router, Route, Switch } from 'react-router-dom'
import store, {history} from './store'
import IndexPage from './containers/IndexPage'
import PhotoPage from './containers/PhotoPage'
import {setSharedData} from './actions'
import './index.css'

// Note: window._sharedData  はpublic/index.htmlでセットされる
let sharedData = window._sharedData
store.dispatch(setSharedData(sharedData))

const baseUrl = process.env.PUBLIC_URL

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <Switch>
        <Route name="index" exact path={baseUrl + "/"}
          render={props =>
            <IndexPage photoUrl={baseUrl + "/photo"} {...props} />
          }
        />
        <Route name="photo" exact path={baseUrl + "/photo/:shortcode"} component={PhotoPage} />
      </Switch>
    </Router>
  </Provider>,
  document.getElementById('root')
);
//registerServiceWorker();

今回の方法ではregisterServiceWorkerは切っておいた方がいいです。切っておかないとServiceWorkerが静的ページを返してしまい、PHPフレームワークにリクエストが来ない現象が発生しました。

#■7. ページの作成

container/App.js
import React from 'react';
import PropTypes from 'prop-types'
//import logo from './logo.svg';
import './App.css';

const App = ({children}) => (
  <div className="App">
    <header>
      Instagram Like
    </header>
    <hr/>
    {children}
    <hr/>
    <header>
      (c)2018 ryujimiya
    </header>
  </div>
)

App.propTypes = {
  children: PropTypes.node.isRequired
}

export default App;
container/IndexPage.js
import React from 'react'
import PropTypes from 'prop-types'
import App from './App'

const IndexPage = ({photoUrl}) => (
  <App>
    <a href={photoUrl + "/BOuWI4BjHfz"}>あけましておめでとうございます🎍🎍
みなさんどうお過ごしでしょうか!?
2017年もイロムクを何卒よろしくお願いいたします🌅
写真は行くぞー的な写真!」</a>
  </App>
)

IndexPage.propTypes = {
  photoUrl: PropTypes.string.isRequired
}

export default IndexPage
container/PhotoPage.js
import React from 'react'
import App from './App'
import PhotoContainer from './PhotoContainer'

const PhotoPage = () => (
  <App>
    <PhotoContainer />
  </App>
)

export default PhotoPage
container/PhotoContainer.js
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { getSharedData } from '../reducers'

const PhotoPageContainer = ({sharedData}) => {
  const entryData = sharedData.entry_data
  const postPage = entryData.PostPage
  const graphQl = postPage[0].graphql
  const shortcodeMedia = graphQl.shortcode_media
  const shortcode = shortcodeMedia.shortcode
  const displayResources = shortcodeMedia.display_resources
  const imgSrc = displayResources[0].src
  const mediaToCaption = shortcodeMedia.edge_media_to_caption
  const caption = mediaToCaption.edges[0].node.text
  return (
    <div>
      <div><img src={imgSrc} alt={caption} /></div>
      <div>{caption}</div>
    </div>
  )
}

PhotoPageContainer.propTypes = {
  sharedData: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
  sharedData: getSharedData(state)
})

export default connect(
  mapStateToProps,
  {}
)(PhotoPageContainer)

#■8. ビルド
package.jsonにホスティングするURLパスを記述します。

package.json
  "homepage": "/instagram_like",

ビルドします。

npm run build

#■9. デプロイ
ここからはサーバーの話になります。

生成されたbuildフォルダの中身をサーバーのLaravelアプリの
public/instagram_like
配下に格納します。

#■10. .htaccessとindex.php

Laravelで処理できるように.htaccessとindex.phpをpublic/instagram_like/に格納します。

public/instagram_like/.htaccess
<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # RewriteBaseの設定が必要
    RewriteBase /

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Handle Front Controller...
    #ディレクトリ除外をコメントアウト
    #RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

index.phpは一つ上の階層(/public)のLaravelのindex.phpを読み込むようにします。

public/instagram_like/index.php
<?php

include realpath(dirname(__FILE__) . '/../index.php');

これでLaravelを通してHTMLが出力されるようになります。

#■11. Laravelのルーティング

routes/web.php
<?php

Route::get('/', 'InstagramLikeController@index');
Route::get('/photo', 'InstagramLikeController@photo');

#■12. Laravelのコントローラーの作成
php artisan make:controller InstagramLikeController

app/Http/Controllers/InstagramLikeController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\InstagramLikeService;

class InstagramLikeController extends Controller
{
    public function index() {
        $svc = new InstagramLikeService();
        $sharedData = $svc->reqIndex();
        $sharedDataJson = json_encode($sharedData, JSON_UNESCAPED_UNICODE);
        return view('instagram_like.index', ['sharedData' => $sharedDataJson]);
    }
    
    public function photo($shortcode) {
        $svc = new InstagramLikeService();
        $sharedData = $svc->reqPhoto($shortcode);
        $sharedDataJson = json_encode($sharedData, JSON_UNESCAPED_UNICODE);
        return view('instagram_like.photo', ['sharedData' => $sharedDataJson]);
    }
}

#■13. sharedDataの実装
sharedDataを生成するサービスクラスを用意します。
ここにロジックを書くことになります。
今回はテストデータを返すだけになっています。

app/InstagramLikeService.php
<?php

namespace App;

class InstagramLikeService
{
    public function reqIndex() {
        $sharedData = array('name' => 'sharedData');
        return $sharedData;
    }
    
    public function reqPhoto($shortcode) {
        $json = <<< 'EOM'
        {
            "name": "sharedData",
            "entry_data": {
                "PostPage":[ {
                    "graphql": {
                        "shortcode_media": {
                            "__typename":"GraphImage",
                            "id":"1418168299602868211",
                            "shortcode":"BOuWI4BjHfz",
                            "dimensions": {
                                "height": 719, "width": 1080
                            }
                            ,
                            "media_preview":"ACob1zGkqYPzcYyeprElgMMnyD5e/t/n/wCvW0uFQDtgD9Kb5qqxzg5GccflUvVgZDQM3J6euD/OqsgYdRnH+cd6157lYjs+7GTwePzA7j8fpmocxiQCfJDcLgdSfbFTZjMQncMjtSYX1q6YcFgV2+nvyeegqMW3HVfzphsXkupN6oxymOmM9B7c46fXNVmnlLuc/KnVf9jOOPw5NRiRgNoOACcfmf8APNQRE/Oe4Rv6VduvkG1iWXcuC2flbap5wQMFdvsB/k1caYq/3wACCfz79wB6YHaqisXADchSuB6c1sGyhMTOV+ZupyeefrVPXQnYz7y4OFwQ2c4I5Hv9ar7F7gZ78mkdAblIv4BgAfhn69akkjXceO5/nWb8il3Z/9k=",
                            "display_url":"https://scontent-nrt1-1.cdninstagram.com/vp/6e66efbd25293a921cd22e47c17764e0/5B9C768B/t51.2885-15/e35/15624585_764711637019057_7120075205669552128_n.jpg",
                            "display_resources":[ {
                                "src": "https://scontent-nrt1-1.cdninstagram.com/vp/495c961e30443a6f995090a6bead1c4a/5B856AEA/t51.2885-15/s640x640/sh0.08/e35/15624585_764711637019057_7120075205669552128_n.jpg", "config_width": 640, "config_height": 426
                            }
                            ,
                            {
                                "src": "https://scontent-nrt1-1.cdninstagram.com/vp/0fc749d0cee710512e1960efdfd30cbc/5B93CCC7/t51.2885-15/s750x750/sh0.08/e35/15624585_764711637019057_7120075205669552128_n.jpg", "config_width": 750, "config_height": 499
                            }
                            ,
                            {
                                "src": "https://scontent-nrt1-1.cdninstagram.com/vp/6e66efbd25293a921cd22e47c17764e0/5B9C768B/t51.2885-15/e35/15624585_764711637019057_7120075205669552128_n.jpg", "config_width": 1080, "config_height": 719
                            }
                            ],
                            "is_video":false,
                            "edge_media_to_caption": {
                                "edges":[ {
                                    "node": {
                                        "text": "\u3042\u3051\u307e\u3057\u3066\u304a\u3081\u3067\u3068\u3046\u3054\u3056\u3044\u307e\u3059\ud83c\udf8d\ud83c\udf8d\n\u307f\u306a\u3055\u3093\u3069\u3046\u304a\u904e\u3054\u3057\u3067\u3057\u3087\u3046\u304b\uff01\uff1f\n\uff12\uff10\uff11\uff17\u5e74\u3082\u30a4\u30ed\u30e0\u30af\u3092\u4f55\u5352\u3088\u308d\u3057\u304f\u304a\u9858\u3044\u3044\u305f\u3057\u307e\u3059\ud83c\udf05\n\u5199\u771f\u306f\u884c\u304f\u305e\u30fc\u7684\u306a\u5199\u771f\uff01"
                                    }
                                }
                                ]
                            }
                            ,
                            "edge_media_to_comment": {
                                "count":1,
                                "page_info": {
                                    "has_next_page": false, "end_cursor": null
                                }
                                ,
                                "edges":[ {
                                    "node": {
                                        "id":"17846398948162135",
                                        "text":"\u3042\u3051\u307e\u3057\u3066\u304a\u3081\u3067\u3068\u3046\u3054\u3056\u3044\u307e\u3059\ud83c\udf8d\uff012016\u5e74\u306f\u30a4\u30ed\u30e0\u30af\u306e\u97f3\u697d\u306b\u51fa\u4f1a\u3063\u3066\u3001\u30e9\u30a4\u30d6\u306b\u3082\u884c\u3051\u3066\u7d20\u6575\u306a\u5e74\u3067\u3057\u305f\u263a\ud83d\udc972017\u5e74\u3082\u30e9\u30a4\u30d6\u884c\u3051\u307e\u3059\u3088\u3046\u306b\uff01\u3053\u308c\u304b\u3089\u3082\u5fdc\u63f4\u3057\u3066\u307e\u3059(\u0e51\uff65\u0311\u25e1\uff65\u0311\u0e51)\uff01\uff01\uff01",
                                        "created_at":1483345890,
                                        "owner": {
                                            "id": "2977883515", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/e247d3d990af2efd5d3b2ee5c05b63c7/5B97A5D0/t51.2885-19/s150x150/23823421_518036085236629_1235652405907947520_n.jpg", "username": "krn___0111"
                                        }
                                    }
                                }
                                ]
                            }
                            ,
                            "taken_at_timestamp":1483278857,
                            "edge_media_preview_like": {
                                "count":54,
                                "edges":[ {
                                    "node": {
                                        "id": "1318483545", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/4448ff89af1aef43c7cdea2fd455bfa4/5B866180/t51.2885-19/s150x150/28766206_152048362133455_3871769183184224256_n.jpg", "username": "nnm813"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "282654007", "profile_pic_url": "https://scontent-frt3-1.cdninstagram.com/vp/856b9478629f7c2f4ae549c4c8cc5dd7/5B94597A/t51.2885-19/11906329_960233084022564_1448528159_a.jpg", "username": "ryusin5"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "354626907", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/694dbc7b80d476d200ca69a206ed910a/5B77E51F/t51.2885-19/s150x150/17662411_915294138573097_6100425398091251712_a.jpg", "username": "nnea26"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "3427312785", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/efdadbd7736b2ae2c04f0d4a3973cd81/5B83AFE2/t51.2885-19/s150x150/13636244_148622568894353_2026179178_a.jpg", "username": "mino_mucho"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "2680071939", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/fa7da14e359fa1e176a6a58485e62ba5/5B97062B/t51.2885-19/s150x150/30953906_165210784163203_5439584193577222144_n.jpg", "username": "b___chi03"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "2167399340", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/cc76a23f4b750da84e5da10632d72080/5B8E80E4/t51.2885-19/s150x150/30085659_182081129268861_5548356254688083968_n.jpg", "username": "rktsiii"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "1958134017", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/eb1b0e11fd332179a9d49f7b278b221b/5B7750C9/t51.2885-19/s150x150/26156903_135638420441941_4769086334918721536_n.jpg", "username": "akurahotam3201"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "1184178809", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/5ec0bfc77242987241b0947fd1156a3a/5B791B06/t51.2885-19/s150x150/29089751_1615548411892122_7820419247334490112_n.jpg", "username": "_kairi_t"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "1253575861", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/9570723853d74db6d434113223c5750c/5B771A90/t51.2885-19/s150x150/30603984_1631773820263962_1485500506171244544_n.jpg", "username": "goootrrr"
                                    }
                                }
                                ,
                                {
                                    "node": {
                                        "id": "1783453030", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/ed5f2ad408bd21b34d3c37840c6d8aeb/5B95ABD2/t51.2885-19/s150x150/27579913_1952399085076934_8398136771493232640_n.jpg", "username": "iro_mana_muku"
                                    }
                                }
                                ]
                            }
                            ,
                            "owner": {
                                "id": "4159115721", "profile_pic_url": "https://scontent-nrt1-1.cdninstagram.com/vp/dea5194bedde9964e31601f976b7f7c4/5B920B2D/t51.2885-19/s150x150/14719801_1675576302772916_3410902154188161024_a.jpg", "username": "iromuk", "blocked_by_viewer": false, "followed_by_viewer": false, "full_name": "\u30a4\u30ed\u30e0\u30af\u516c\u5f0f\u30a4\u30f3\u30b9\u30bf\u30b0\u30e9\u30e0", "has_blocked_viewer": false, "is_private": false, "is_unpublished": false, "is_verified": false, "requested_by_viewer": false
                            }
                        }
                    }
                }
                ]
            }
            ,
            "hostname":"www.instagram.com"
        }
EOM;
        $sharedData = json_decode ($json, true, 512, JSON_BIGINT_AS_STRING|JSON_OBJECT_AS_ARRAY);
        return $sharedData;
    }
}

#■14. ビューの作成(sharedDataの埋め込み)
Reactでbuildしたときに生成されたindex.htmlをビューにコピーし、window._sharedDataの箇所にコントローラーから渡されたsharedDataを埋め込みます。

resources/views/layouts/instagram_like.blade.php
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="/instagram_like/manifest.json">
    <link rel="shortcut icon" href="/instagram_like/favicon.ico">
    <title>Instagram like</title>
    <link href="/instagram_like/static/css/main.29266132.css" rel="stylesheet">
</head>

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script>
        window._sharedData = {!! $sharedData !!};
    </script>

    <div class="container">
        @yield('content')
    </div>
    
    <script type="text/javascript" src="/instagram_like/static/js/main.83c4061d.js"></script>
</body>

</html>
resources/instagram_like/index.blade.php
@extends('layouts.instagram_like')

@section('content')
    <p>いんでっくす</p>
@endsection
resources/instagram_like/photo.blade.php
@extends('layouts.instagram_like')

@section('content')
    <p>ふぉと</p>
@endsection

#■15. 完成!!!!!!!
以上で完成です。
インデックスページ/instagram_like/にアクセスするとReactで作成したページにPHPで付け加えた「いんでっくす」という文字がでてきます。

20180511_Instagram_like_indexページ.jpg

次にフォトページ/instagram_like/photo/BOuWI4BjHfzにアクセスすると、sharedDataからとってきた画像とコメントが表示されます。

20180511_instagram_like_photoページ.jpg

#■ まとめ
React/ReduxとPHP Laravelをwindow._sharedDataを介して連携するというインスタグラムライクな方法でWebアプリを作りました。

16
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?