はじめてのPHPアプリ作成〜Spotify Web APIとElement UIを使って、Herokuデプロイまで〜

これまでPHPで記述したアプリケーションをWEB上に公開したことがなかったので、この度ポートフォリオ提出用に公開しました。

今回作成したポートフォリオの詳細を述べることも兼ねて、Qiitaの記事として投稿します。

私のエンジニアとしての経歴は、「半年間ほど渋谷の自社開発企業でフロントチーム（使用言語はHTML, SCSS, JavaScript, jQuery）のメンバーとしてインターンシップ歴あり」程度のもので、普段は経済学部生として社会保障分野の研究をしています。

# アプリケーションの概説
Spotify Web APIを利用して、適当なアーティストを入力するとその関連アーティストが表示されます。
加えて、ワンクリックでSpotifyへと遷移することができ、すぐに該当アーティストの楽曲を聞くことが可能です。

<img width="1440" alt="spotify_app1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/48b616a1-3429-f1b5-ebcd-8847d80a2450.png">

<img width="1440" alt="spotify_app2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/21a9d7f7-494c-493b-73ac-cbbe36331149.png">

<img width="1440" alt="spotify_app3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/eb892047-e7c5-a897-79f6-e2be6d220881.png">

<img width="1440" alt="spotify_app4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/7a9e851b-e4a4-7099-88b2-00291660b885.png">


# ユーザーエージェントによる切り分け
レスポンシブデザインは採用せず、PCかSPかを識別して読み込むページを変更する方法を採っています。

```php:index.php
<?php
	$ua = $_SERVER['HTTP_USER_AGENT'];
	$browser = ((strpos($ua, 'iPhone') !== false) || (strpos($ua, 'iPod') !== false) || (strpos($ua, 'Android') !== false));
    if ($browser == true) {
		$browser = 'sp';
	}
	if ($browser == 'sp') {
        //SPの場合に読み込むファイル
        require('index_sp.php');
    } else {
        //PCの場合に読み込むファイル
        require('index_pc.php');
    }
?>

```

# フロントエンドについて
コンポーネントのほとんどは[Element UI](https://element.eleme.io/#/en-US)を活用しました。
Element UIを用いるためにVue.jsの環境構築をする必要がありましたが、お手軽CDNで済ませました。
>[VueのCDN版について](https://jp.vuejs.org/v2/guide/installation.html#CDN)

Element UIを使った理由は、インターン先で少しだけ使ったときにおもしろいなと感じていたこと、便利ツールのできることとできないことを知りたかったということが挙げられます。
ふわっとした動機です。

ちなみに、Element UIを使ってみて感じたことは、以下のとおりでした。
・ 単純におもしろい
・ リッチなコンポーネントがすぐに使えて便利
・ 細かいレイアウトを変更したいときに不便（`el-hoge`はひとつのタグに見えても、開発ツールでコンパイル後は複数のタグかつ複雑な構造になっていたりするため）
・ 大規模な開発には向かなそう

# バックエンドについて
バックエンドについては以下のページを大いに参考にさせていただきました。
>[PHPでSpotify APIを使う](https://qiita.com/kmtym1998/items/291c0e85e3879d8a41b8)

>[Spotify APIを使ってLINEBotであなたにおすすめプレイリストを全自動で作ってもらう](https://qiita.com/momochan/items/459c95d075f12c6e6b77)

本稿では、以下の機能を実装するための関数について紹介します。
1. 入力されたアーティストの情報
2. 入力アーティストのアルバムの情報
3. 関連アーティストのトップトラックの情報
4. 関連アーティストの情報

また、コード内のTODOコメントは今後改善したいポイントであります。

## 0.下準備
[Spotify for Developer](https://developer.spotify.com/)にアクセスしてあれこれすること、Spotify Web APIをPHPで利用するための[ライブラリ](https://github.com/jwilsson/spotify-web-api-php)をダウンロードすることについては、上述の参考サイトに丸投げします。

上記ライブラリを利用するために、以下の関数を切り分け。
（今回はユーザー情報を用いないので、`REDIRECT_URI`の記述は省略しました。）

```php:spotify.php
<?php
    require_once('vendor/autoload.php');
    $CLIENT_ID = getenv('CLIENT_ID');
    $CLIENT_SECRET = getenv('CLIENT_SECRET');
    $session = new SpotifyWebAPI\Session(
        "{$CLIENT_ID}",
        "{$CLIENT_SECRET}"
    );
    $api = new SpotifyWebAPI\SpotifyWebAPI();
    $session->requestCredentialsToken();
    $accessToken = $session->getAccessToken();
    $api->setAccessToken($accessToken);
?>
```

`CLIENT_ID`, `CLIENT_SECRET`については、Spotify for Developerのダッシュボードから確認してください。
また、`CLIENT_ID`, `CLIENT_SECRET`については、環境変数ENVを参照させる形をとっています。
この際、以下のサイトを参考にさせていただきました。

>[HerokuでWebアプリ開発を始めるなら知っておきたいこと(5) 環境変数ENV](http://blog.ruedap.com/2011/05/03/ruby-heroku-web-app-development-tips-5)

>[HerokuでPHPを使うときに気を付けるところ](https://qiita.com/hiro_y/items/bc631a27c9701f81a4f9)

```terminal
$ heroku config:add CLIENT_ID='hogehoge' --app 'hugahuga'
$ heroku config:add CLIENT_SECRET='foo' --app 'bar'

// こちらで設定した環境変数については、以下のコマンドで閲覧可能です。
$ heroku config
```

`CLIENT_ID='hogehoge'`のように、左辺と右辺との間にスペースを空けずに入力してください。

`CLIENT_ID`, `CLIENT_SECRET`を取得したい場合は、`getenv()`で取得可能です。

-----


以下、私が作成した関数についての紹介となりますが、関数については[SpotifyWebAPI.md](https://github.com/jwilsson/spotify-web-api-php/blob/master/docs/method-reference/SpotifyWebAPI.md)を見ながら作成しました。
豊富なメソッドがあるので眺めているだけでもおもしろいです。

## 1.入力されたアーティストの情報を持ってくる関数

引数に渡されたアーティスト名を利用し、ライブラリで用意されているメソッドを通じ、そのアーティストの情報を入手します。
これにより、入力されたアーティストの情報を動的に表示させることができるようになりました。

```php:function.php
function artistSearch($artistName) {
    global $api;
    $artistInfo = $api->search($artistName, 'artist', array('limit' => 1));
    if (isset($artistInfo->artists->items)) {
        foreach ($artistInfo->artists->items as $data) {
            $artistData = array(
                'id' => $data->id,
                'artist_name' => $data->name,
                // TODO: イメージが複数あるならランダムで表示されるようにしてもいいかも
                'image' => $data->images[0]->url,
                'artist_url' => $data->artist[0]->external_urls,
            );
        }
        return $artistData;
    } else {
        // $artistInfo->artists->items がヒットしないとき
        return false;
    }
}
```
`global $api;`としているのは、本関数の中で`spotify.php`にて記述した$apiという変数を用いるためです。


## 2.入力アーティストのアルバムの情報をもってくる


```php:function.php
function relatedArtistTopAlbum($artistId) {
    global $api;
    $relatedArtistAlbum = $api->getArtistAlbums($artistId, array('country' => 'JP'))->items;
     // 取得するアルバムは人気があるものとは限らない
     $relatedArtistTopAlbum = array(
         'artist_url' => $relatedArtistAlbum[0]->artists[0]->external_urls->spotify,
         'artist_name' => $relatedArtistAlbum[0]->artists[0]->external_urls->name,
         'album_image' => $relatedArtistAlbum[0]->images[0]->url,
         'album_name' => $relatedArtistAlbum[0]->name
     );
     return $relatedArtistTopAlbum;
}
```

## 3.関連アーティストの情報を持ってくる
`1.入力されたアーティストの情報を持ってくる関数`で得たアーティストの`id`を引数に、そのアーティストに関連するアーティストの情報を入手します。

```php:function.php
function relatedArtistSearch($artistId) {
    global $api;
    $relatedArtist = $api->getArtistRelatedArtists($artistId)->artists;
    $relatedArtistSelect = array();
    // 関連アーティストをいくつ持ってくるかを定義
    $countNum = 6;
    if (count($relatedArtist) >= $countNum) {
        $selectionNum = $countNum;
    } else {
        $selectionNum = count($relatedArtist);
    }
    for ($i = 0; $i <= $selectionNum - 1; $i++) {
        $relatedArtistData = array(
            'id' => $relatedArtist[$i]->id,
            'name' => $relatedArtist[$i]->name,
            'images' => $relatedArtist[$i]->images[0]->url
        );
        // 関連アーティストを$countNumだけ取得
        array_push($relatedArtistSelect, $relatedArtistData);
     }
     return $relatedArtistSelect;
}

```

## 4.関連アーティストのトップトラックの情報を持ってくる
`3.関連アーティストの情報を持ってくる`で得た関連アーティストの`id`を引数に、そのアーティストのトップトラック（人気曲）の情報を入手します。

関連アーティストが`$countNum`(今回は`$countNum=6`）つ収録された変数`$relatedArtistSelect`は配列ですので、foreachで関連アーティスト各々についての情報を入手します。

ここで用いている`getArtistTopTracks()`というメソッドは大変面白く、本アプリケーションの核といってもよい重要なものです。

```php:function.php
function relatedArtistTopTracks($relatedArtistSelect) {
    global $api;
    $topTracksSelect = array();
    foreach ($relatedArtistSelect as $data) {
        $topTracks = $api->getArtistTopTracks($data['id'], array('country' => 'JP'))->tracks;
        $topTracksData = array(
            'track_id' => $topTracks[0]->id,
            'track_url' => $topTracks[0]->external_urls->spotify,
            'artist_name' => $topTracks[0]->artists[0]->name,
            'artist_url' => $topTracks[0]->artists[0]->external_urls->spotify,
            'album_name' => $topTracks[0]->album->name,
            'album_image' => $topTracks[0]->album->images[0]->url,
            'album_url' => $topTracks[0]->album->external_urls->spotify,
        );
        if (isset($topTracks)) {
            // 関連アーティスト各々に対してトップトラックを取得
            array_push($topTracksSelect, $topTracksData);
        }
    }
    return $topTracksSelect;
}
```

# 1.〜4.の関数をどう使ったのか
以上の４つの関数を以下のように用いました。

```php:index_pc(_sp).php
<?php
// TODO: 公開前に0にする
ini_set('display_errors', 0);

// spotify web api 使用
require('spotify.php');

// 切り出した関数読み込み
require('function.php');

// アーティスト情報取得
// TODO: 空欄はエラーなので空欄のときはとりあえずなにかいれておく
if ($_POST['artistName'] === "")  {
    $_POST['artistName'] = "アルベム";
}

// 入力されたものをサニタイズ
$artistName = htmlspecialchars($_POST['artistName'], ENT_QUOTES, "UTF-8");

//入力されたアーティストの情報を取得
$artistData = artistSearch($artistName);

// 入力されたアーティストのアルバム取得
$relatedArtistAlbum = relatedArtistTopAlbum($artistId);

// 関連アーティスト取得
$artistId = $artistData['id'];
$relatedArtistSelect = relatedArtistSearch($artistId);

// 関連アーティストのトップトラック取得
$topTracksSelect = relatedArtistTopTracks($relatedArtistSelect);
?>
```

少々話が逸れてしまいますが、サニタイズについての理解が疎いため問題点などございましたらご指摘いただきたいです。（もちろんその他なんでも結構でございます）

# Herokuにデプロイ
さて、望んでいた挙動をしてくれるようになったので、外部に公開しようということで、今回はHerokuにデプロイして見たいと思います。
本当はAWSを使ってあれやこれやしたかったのですが、またの機会に。

デプロイ方法は[こちらのQiitaの記事](https://qiita.com/Teaki/items/294b0f1a8ef55e53b04f)を参考にさせていただきました。

下手にDocker-Composeを使ってphpの環境構築をしていたため、その作業ディレクトリからHeroku用のディレクトリにまるまるファイルやフォルダをコピーしまして、上記サイトの手順に従ってデプロイを行いました。

以前挫折したHerokuでしたが、上記の記事のおかげもあって、特につまずきもなくスムーズにデプロイが完了しました。

追記：
herokuとgithubが連携できるとは知らず、面倒な方法をとってしまいました。
[HerokuとGitHubを連携させて自動デプロイ環境を作ろう！](https://j-hack.gitbooks.io/deploy-meteor-app-to-heroku/content/step4.html)を参考にすれば、もっとカンタンでした。



# 今後やりたいこと
ひとまず最低限のものをデプロイ出来てホッとしています。
今後はAWS, Docker, CI/CDなどの技術を盛り込んでみたいと考えています。

また、フロントのポジションでインターンシップに参加していたこともあり、phpを書くのは久々でしたので良い復習になりました。

以上となります。ありがとうございました。