Laravel(PHP)×TwitterAPIで画像や動画をツイートしてみた

# はじめに
最近、TwitterAPIで画像や動画をツイートしたのでそのまとめの記事です。
初学者の方であっても分かりやすい記事にしたいという思いがあるので、出来るだけ詳しく書けるよう頑張ります。
※経験が浅く、所々間違っていたり、もっと良い方法がある場所があるかと思いますが、優しくご指摘いただけますと幸いです><

# 実現したいこと
webアプリからTwitter認証 -> 認証したアカウントでwebアプリから画像や動画をツイートする。

参考：https://twitteroauth.com/
※↑これ読めばほぼ解決します・・・！めちゃくちゃ便利でしたmm
公式ドキュメント：https://developer.twitter.com/ja/docs

# 準備
## 0.Twitter Developerサイトで手続き
手順は割愛しますが、以下を入手できればOKです。
```
Consumer Keys：Consumer_keyとConsumer_secret
Access Token and Secret：Access_tokenとAccess_token_secret
```

## 1.composerを使ってインストール
ターミナルで下記コマンドを入力します！
```
composer require abraham/twitteroauth
```
インストール後に下記メッセージが表示されていれば、成功です！
```
Package manifest generated successfully.
```
## 2..envにDeveloperサイトで取得したKeyやTokenを書く
``` .env
TWITTER_API_KEY=*****
TWITTER_API_KEY_SECRET=****
TWITTER_ACCESS_TOKEN=****
TWITTER_ACCESS_TOKEN_SECRET=*****
//ローカルで開発するときは、Developerサイトでもcallback_urlを下記のように設定します
TWITTER_CALL_BACK_URL=http://localhost:8083/hogehoge
```

## 3.app/configディレクトリに.envで書いた定数を呼び出す処理を書く
ここでは、envに記述したKeysやTokenをconfigを通して取得するように設定します。
.envから直接呼び出すのは良くないようなので、、、

参考：[【Laravel】環境変数の使い方](http://tektektech.com/laravel-environment-variable/)
参考：[【Laravel】 envメソッドで環境変数が取得できず、nullになるときの対処法を紹介！](https://public-constructor.com/laravel-env-helper-returns-null/)

ということで、config配下の``service.php``に下記のように書きました。
※``twitter.php``ファイルを作成しても良いかもしれないですが、サードパーティをまとめたくて私はserviceに書きました。
```sercive.php
...
    'twitter' => [
        'consumer_key' => env('TWITTER_API_KEY'),
        'consumer_secret' => env('TWITTER_API_KEY_SECRET'),
        'access_token' => env('TWITTER_ACCESS_TOKEN'),
        'access_secret' => env('TWITTER_ACCESS_TOKEN_SECRET'),
    ],
```

# 画像や動画をツイートする処理
上記の準備が完了したら、実際にControllerにTwitterAPIを使って認証したりツイートしたりの処理を書いていきます！
※エラーハンドリングの類は省略しています。

## 認証画面へ遷移させる処理
イメージ的には、フロント側で``Twitterに投稿する``ボタンを押したときに認証ページへ飛ばせる処理です。
※ServiceProviderやFacadeの設定をする処理もありますが、今回は解説しません。

認証する場合には ``oauth/request_token`` のエンドポイントを使います。
```
    public function authorizeTwitter(Request $request)
    {
        $twitter = new TwitterOAuth(
            config('services.twitter.consumer_key'),
            config('services.twitter.consumer_secret'),
            config('services.twitter.access_token'),
            config('services.twitter.access_secret'),
        );

        $token = $twitter->oauth('oauth/request_token', array(
            'oauth_callback' => env('TWITTER_CALL_BACK_URL')
        ));

        session(array(
            'oauth_token' => $token['oauth_token'],
            'oauth_token_secret' => $token['oauth_token_secret'],
        ));

        //認証画面へ移動させる
            // 毎回認証をさせたい場合： 'oauth/authorize'
            // 再認証が不要な場合： 'oauth/authenticate'
        $url = $twitter->url('oauth/authorize', array(
            'oauth_token' => $token['oauth_token']
        ));

        return redirect($url);
    }
```
## （認証後に）callback_urlで設定したパスへ遷移したとき
認証情報をセッションに保存しておきます
```
public function hoge(Request $request)
{
    session(array(
        'oauth_token' => $request['oauth_token'],
        'oauth_verifier' => $request['oauth_verifier']
    ));
    .
    .
    .
    return view('fuga');
}
```

## 実際にツイートする処理（画像付きツイート）
イメージ的には、認証後に設定したcallback_urlへ遷移して、そこから``ツイートする``ボタンを押した時に発火する処理です。

①Twitterへ画像・動画をアップロード
②アップロードした画像・動画のidとテキストを一緒にツイート
という流れになります。

詳細はドキュメントを確認してくださいmm
[公式ドキュメント：画像ツイート](https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload)
```
public function tweet(Request $request)
{
    //認証情報を取得
    $oauth_token = $request->session()->get('oauth_token');
    $oauth_verifier = $request->session()->get('oauth_verifier');

    $twitter = new TwitterOAuth(
        config('services.twitter.consumer_key'),
        config('services.twitter.consumer_secret'),
        config('services.twitter.access_token'),
        config('services.twitter.access_secret'),
    );

    $accessToken = $twitter->oauth("oauth/access_token", ["oauth_token" => $oauth_token,"oauth_verifier" => $oauth_verifier]);

    $oauthToken = $accessToken['oauth_token'];
    $oauthTokenSecret =$accessToken['oauth_token_secret'];
    //認証したユーザーのアカウントを使ってツイートができるようになります。
    $usersTwitter = new TwitterOAuth(
        config('services.twitter.consumer_key'),
        config('services.twitter.consumer_secret'),
        $oauthToken,
        $oauthTokenSecret
    );

    // 画像をTwitterへアップロード
    //media/uploadエンドポイントを使います。
    $media = $usersTwitter->upload('media/upload',['media' => "画像のパス"]);

    //Twitterへ投稿する
    $usersTwitter->post("statuses/update", [
        "status" => "ツイートするテキスト",
        "media_ids" => $media->media_id_string,
    ]);
}
```

## 実際にツイートする処理（動画付きツイート）
ツイートする処理までは同じなので割愛します。
```
public function fuga(Request $request)
{
    $oauthToken = $request->session()->get('users_oauth_token');
    $oauthTokenSecret = $request->session()->get('users_oauth_token_secret');

    $usersTwitter = new TwitterOAuth(
        config('services.twitter.consumer_key'),
        config('services.twitter.consumer_secret'),
        $oauthToken,
        $oauthTokenSecret
    );

    //まず動画をアップロードします。
    $upload = $usersTwitter->upload('media/upload',[
        'media' => "動画のパス",
        'media_type' => "video/mp4",
        'media_category' => 'tweet_video',
    ],true);

    //アップロード完了するまで待つ
    sleep(5);
    $usersTwitter->post("statuses/update", [
        "status" => $request->twconfirm,
        "media_ids" => $upload->media_id_string,
    ]);
}
```
### アップロードできているはずなのに、ツイートできない・・・
[こちらの記事](https://engineer.ns-it.net/comment.php?kiji_id=2287)を参考させていただいたのですが、
``動画が30秒を超えている場合は、media_category：tweet_video を指定する``必要があるようです。

また、上記で``sleep(5)``と書いてある部分について少し説明しますと、
動画をTwitterにアップロードし終わる前に、``post``の処理をしてしまうと、``Not valid video``とエラーが出ます。
もう少しデバッグすると``pending``という状態でした。

つまり、長めの動画をアップロードする場合は、アップロード処理が完全に終わるのを待って、その後にツイートする処理をしないといけません。（ここにハマりました・・・）

色んな対応方法があると思いますが、動画をアップロードする場合は少し工夫が必要そうでした・・・

# 終わりに
以上がLaravel×TwitterAPIで画像や動画付きツイートをする方法でした。
``abraham/twitteroauth``これを使うとかなり楽にツイートできるのでとても助かりました。

# 参考文献
公式ドキュメント:https://developer.twitter.com/ja/docs
abraham/twitteroauth：https://twitteroauth.com/
ITエンジニアの四方山話 またTwitterAPIで遊んでしまった：https://engineer.ns-it.net/comment.php?kiji_id=2287