20200601のlaravelに関する記事は13件です。

laravelでマルチドメイン対応

laravelプロジェクトでマルチドメイン対応の備忘録

概要:アンケートページのドメインとそれ以外のドメインで分けたい。

マルチドメインとは

一つのサーバーで複数のドメインを管理する運営方法。
メリット:複数のドメインを一つのサーバーで管理できるのは楽。サーバー費用が削減できる
https://sakuramarina.com/episode_11/

やり方の概要

① .envファイルにドメインを設定する
② config/app.phpでドメイン名を定義
③ ミドルウェアの作成
④ app/Http/kernel.phpでミドルウェアの登録
⑤ web.phpでrouteをミドルウェアごとにまとめる

1.envファイルにドメインを設定する

どこでもいいので、ドメインを設定します。
今回はステージングの環境なのでlocalの値です。

.env
MAIN_DOMAIN=127.0.0.1
SURVEY_DOMAIN=localhost

2.ドメインを定義する

app.php
return 
[
'main' => env('MAIN_DOMAIN', '127.0.0.1'),
'survey' => env('SURVEY_DOMAIN', 'localhost'),
]

3.ミドルウェアの作成

ミドルウェアとはリクエストをコントローラに渡す前のクッションのようなものと認識しています
https://codezine.jp/article/detail/11643

ミドルウェアの作成は以下のコマンド
php artisan make:middleware Survey

app.phpのsurveyキーはapp.survey
request()->getHost() 現在のURLのドメインを取得できる
https://stackoverflow.com/questions/39835650/how-do-you-get-the-http-host-with-laravel-5
現在のドメインがlocalhost以外だったら403を返す。

Middleware/Survey.php
<?php

namespace App\Http\Middleware;

use Closure;

class Survey
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */

    public function handle($request, Closure $next)
    {
        if (config('app.survey') !== request()->getHost())
        {
            return \abort(403);
        }
        return $next($request);
    }
}

現在のドメインが127.0.0.1以外だったら403を返す。

Middleware/Main.php
<?php

namespace App\Http\Middleware;

use Closure;

class Main
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if(config('app.main') !== request()->getHost())
        {
            return \abort(403);
        }
        return $next($request);
    }
}

.envの値はconfigを使ってあげる必要がある
https://hiroto-k.hatenablog.com/entry/2018/03/28/213000

4.app/Http/kernel.phpでミドルウェアの登録

Kernel.php
protected $routeMiddleware = [
 'domain.survey' => \App\Http\Middleware\Survey::class,
 'domain.main' => \App\Http\Middleware\Main::class,
]

5.web.phpでrouteをミドルウェアごとにまとめる

web.php
Route::group(['middleware'=>['domain.main']],function()
{
  //ここにmainで使用するルートを書く
});

Route::group(['middleware'=>['domain.survey']],function()
{
  //ここにsurveyで使用するルートを書く
});

詰まったところ

middlewareで期待した条件分岐にならなかったので、var_dump(config('app.main'))などとして中身を確認するとenvの値が反映されていなかった。app.mainはenvで設定したMAIN_DOMAIN=127.0.0.1なのに、中身は127.0.0.1:8000になっていた。最初envの設定は127.0.0.1:8000としていて途中で書き換えたが、環境変数はそのままキャッシュを持っていたためだった。
対応方法:envの値を反映させるために

php artisan config:cache

を行った。
https://qiita.com/kawax/items/ea881a6c18b8d4e94d83

ミドルウェアでテストを通してあげる場合

ミドルウェアにテスト環境でのロジックを書いていないので、このままではテストを行うと全て403で返ってきてしまします。
なので、if文に追加してあげます。

Survey.php
    public function handle($request, Closure $next)
    {
        if (
            config('app.survey') !== request()->getHost() &&
            config('app.env') !== 'testing'  //追加
        )
        {
            return \abort(403);
        }
        return $next($request);
    }
Main.php
    public function handle($request, Closure $next)
    {
        if(
            config('app.main') !== request()->getHost() &&
            config('app.env') !== 'testing'  //追加
            )
        {
            return \abort(403);
        }
        return $next($request);
    }

app.envはenvファイルで定義されています。
testingはvalue値

phpunit.xml
<php>
 <env name="APP_ENV" value="testing">
 ///省略
</php>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】php artisan migrateエラーへの対応 : 1 PDOException::("SQLSTATE[HY000] [2002] Connection refused") /Applications/MAMP/htdocs/task_test/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

【Laravel】php artisan migrateエラーへの対応 : 1 PDOException::("SQLSTATE[HY000] [2002] Connection refused") /Applications/MAMP/htdocs/task_test/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

上記エラーを対応した際の内容を記録します.

目次


動作環境

OS : macOS Mojave 10.14.6
MAMP : 5.7
MySQL : 5.7.26
PHP : 7.2.31
laravel : v6.18.16

解決法

先に解決した方法を記載します.
.envファイルにDB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sockを追加します.

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_blog
DB_USERNAME=blog_user
DB_PASSWORD=mnhgtr54
//追加
DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock

キャッシュをクリアし、再度、マイグレーション

$ php artisan cache:clear #キャッシュをクリア
$ php artisan migrate #再度実行
Migration table created successfully.

こちらの記事を参考にしました.
https://teratail.com/questions/183824

調べたこと

MAMPのページを見るとUnixソケットのパスが記載されている.

image.png

またUnixソケットを利用する場合はSocketを使用する必要があるとのこと

image.png

ここでそもそもUnixソケットとは何かについて調べたところUNIXドメインソケット通信という通信プロトコルということが分かった

UNIXドメインソケット(英: UNIX domain socket)とは、単一のオペレーティングシステム内で実行されるプロセス間でデータを交換するためのデータ通信の終点.UNIXドメインソケットは、アドレス・名前空間としてファイルシステムを使用している。これらは、ファイルシステム内のinodeとしてプロセスから参照される。これは、2つのプロセスが通信するために、同じソケットを開くことができる。しかし、コミュニケーションは、完全にオペレーティングシステムのカーネル内で発生する

詳細はこちらのページが参考になります.

 エラー原因の考察

今回のエラーの件をまとめると

MacのターミナルとMAMPのMySQLは同一のマシン上にインストールされているため,
Unixドメインソケット通信を利用して接続する必要があった.

そのため,MAMPのヘルプにあるようにドメインソケットのアドレスとなる/Applications/MAMP/tmp/mysql/mysql.sockを指定することでターミナルとMySQLで通信ができるようになった

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのタスクスケジューラを使う

前提条件

eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

Laravelでコマンドライン処理を行う
本記事は上記の内容を理解している前提で書かれています

Commandクラス作成

コマンドラインで
cd sample
php artisan make:command SampleSchedule
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します
eclipseプロジェクトを右クリック→リフレッシュ
/sample/app/Console/Commands/SampleSchedule.phpが現れます

Commandクラス修正

さきほど作成したSampleSchedule.phpを下記に修正します

SampleSchedule.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SampleSchedule extends Command
{
    protected $signature = 'sample:name2 {arg1}';

    protected $description = 'SampleSchedule';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $arg1 = $this->argument('arg1');
        $this->line('arg1:' . $arg1);
        if ($arg1 === 'a') {
            return 1;
        }
        return 0;
    }
}

Commandクラス登録

/sample/app/Console/Kernel.php修正

Kernel.php
‥‥
    protected $commands = [
     ‥‥
        ,Commands\SampleSchedule::class
    ];

‥‥

Kernel.phpに定義されている$commands配列に先ほど作成したSampleScheduleクラス名を追記します

スケジュール作成

(1) /sample/app/Console/Kernel.phpに下記use文を追記
use Illuminate\Support\Facades\Log;

(2) /sample/app/Console/Kernel.phpのscheduleメソッドを修正します

Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('sample:name2 0')
                 ->everyMinute()
                 ->appendOutputTo(dirname(dirname(dirname(__FILE__))) . '/storage/logs/SampleSchedule.log')
                 ->onSuccess(function () {
                     Log::info('成功');
                 })
                 ->onFailure(function () {
                     Log::error('エラー');
                 })
                 ;

        $schedule->command('sample:name2 a')
                 ->everyMinute()
                 ->appendOutputTo(dirname(dirname(dirname(__FILE__))) . '/storage/logs/SampleSchedule.log')
                 ->onSuccess(function () {
                     Log::info('成功');
                 })
                 ->onFailure(function () {
                     Log::error('エラー');
                 });

        $schedule->command('sample:name2 z')
                 ->dailyAt('10:00')
                 ->appendOutputTo(dirname(dirname(dirname(__FILE__))) . '/storage/logs/SampleSchedule.log')
                 ->onSuccess(function () {
                     Log::info('成功');
                 })
                 ->onFailure(function () {
                     Log::error('エラー');
                 })
                 ;

    }

command関数は実行するコマンドクラスを指定します。引数はCommandクラスのsignature変数に定義した通りに書きます。php artisanコマンドでCommandクラスを実行するときと同じものになります[Laravelでコマンドライン処理を行う]。
everyMinute関数は毎分実行する指定になります
appendOutputTo関数はCommandクラスからの出力先を指定したファイルにするものです
onSuccess関数はCommandクラスから戻り値が0の場合に実行される処理を指定します。ただし、動作確認したLaravelバージョン7.1.1ではCommandクラスをバックグラウンド実行すると戻り値が0でもonSuccessが実行されないです。バックグラウンド実行はメソッドチェーンでrunInBackground関数を指定するとできるようになります。
onFailure関数はCommandクラスから戻り値が1の場合に実行される処理を指定します。ただし、動作確認したLaravelバージョン7.1.1ではCommandクラスをバックグラウンド実行すると戻り値が何であってもonFailureが実行されます。バックグラウンド実行はメソッドチェーンでrunInBackground関数を指定するとできるようになります。
dailyAt関数は毎日決まった時間にCommandクラスを実行する指定になります
everyMinute関数やdailyAt関数といった実行日時を指定する関数はいろいろ用意されています。下記で確認できます
Laravel 7.x タスクスケジュール 繰り返しのスケジュールオプション

動作確認

コマンドラインで
cd sample
php artisan schedule:run
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します

/sample/storage/logs/SampleSchedule.logにSampleSchedule.phpからの出力が書き込まれています
/sample/storage/logs/laravel.logに/sample/app/Console/Kernel.phpのscheduleメソッド内にonSuccess、onFailureで指定したクロージャからの出力が書き込まれています

また、php artisan schedule:runを実行したウインドウを見ると
php "artisan" sample:name2 0
php "artisan" sample:name2 a
が実行されたことがわかります
さらに、実行した時刻が10:00の場合、php "artisan" sample:name2 zも実行されているでしょう

cron登録とスケジューラとしての使い方

先ほどコマンドラインで/sample/app/Console/Kernel.phpのscheduleメソッドを実行してみました
これをcronに指定します
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
/path-to-your-projectというのはご自身のLaravelプロジェクトへの絶対パスに書き換えてください
この記事であればsampleフォルダになります
このcronの設定を見ると分、時、日、月、曜すべてアスタリスクになっていることがわかります
これは1分に1回、要は毎分php artisan schedule:run実行されるという設定になります
つまり、毎分php artisan schedule:runが実行されて、その際、実行日時に合うタスクスケジュール繰り返しのスケジュールオプションが設定されているcommandだけが実行されるという仕組みです

先ほどコマンドラインでphp artisan schedule:runを実行した際、everyMinute指定したものが実行されて(ちょうど10:00に実行しなければ)dailyAt指定したものは実行されなかったと思います
これはphp artisan schedule:runが毎分実行されるようにつくられているからです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel + GraphQL】Lighthouseを使ってみる【その1:チュートリアル】

Lighthouseの設計思想が何もわからん&そもそもディレクティブ全然わからん状態を脱却したいので、練習として公式のドキュメントを上から順番にやっていくことにしました。

環境

PHP: 7.2.5
Laravel: 7.0
Lighthouse: 4.13

チュートリアル

デフォルトのスキーマを実行できるようにする

何はともかくインストールします。

composer require nuwave/lighthouse

そうしたらまずはデフォルトのスキーマを試してみましょう。

php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema

このコマンドを実行すると以下のようにusersuserスキーマが生成されます。

src/graphql/schema.graphql
"A date string with format `Y-m-d`, e.g. `2011-05-23`."
scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")

"A datetime string with format `Y-m-d H:i:s`, e.g. `2018-05-23 13:43:32`."
scalar DateTime @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")

"A datetime and timezone string in ISO 8601 format `Y-m-dTH:i:sO`, e.g. `2020-04-20T13:53:12+02:00`."
scalar DateTimeTz @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTimeTz")

type Query {
    users: [User!]! @paginate(defaultCount: 10)
    user(id: ID @eq): User @find
}

type User {
    id: ID!
    name: String!
    email: String!
    created_at: DateTime!
    updated_at: DateTime!
}

スキーマが生成されたので適当にusersテーブルにデータを入れて以下のようにcurlを叩いてみましょう。

curl --request POST \
  --url http://localhost:10080/graphql \
  --header 'content-type: application/json' \
  --data '{"query":"query {\n  users {\n    paginatorInfo {\n      count\n      currentPage\n    }\n    data {\n      id\n      name\n      email\n      created_at\n      updated_at\n    }\n  }\n}"}'

するとたったこれだけでデータベースの情報を取得することができました。
しかもペジネータも自動でついてます。

{
  "data": {
    "users": {
      "paginatorInfo": {
        "count": 1,
        "currentPage": 1
      },
      "data": [
        {
          "id": "1",
          "name": "hgoe",
          "email": "hoge@example.com",
          "created_at": "2020-05-31 14:04:07",
          "updated_at": "2020-05-31 14:04:11"
        }
      ]
    }
  }
}

CORSの有効化

CORSを有効化するのを忘れないようにしましょう。

config/cors.php
return [
-   'paths' => ['api/*'],
+   'paths' => ['api/*', 'graphql'],
    'allowed_methods' => ['*'],
    'allowed_origins' => explode(',', env('ALLOWED_CORS_ORIGINS', [])),
    ...
];

開発用ツール

Lighthouseを便利に開発するために、公式ではGraphQL Playgroundの使用を推奨しています。

しかしこのツール、Dockerを使っているとめっちゃメモリ食ってまともに動かなくなることが稀によくあります。

なので個人的にはGraphQLをサポートしているInsomniaAltair GraphQL Clientがおすすめです。

特にInsomniaは無料にも関わらず多彩な機能が搭載されており、とても便利なので現状使用している Rest Clientに不満がある方は是非とも触ってみることをおすすめします。

【RESTクライアント】Insomniaをおすすめする記事

IDEサポート

Lighthouseでは独自定義されたディレクティブが多用されているようです。
なのでこれらをIntelliJ(PhpStrom)やVS Codeでも認識できるようにするために、IDEヘルパーを導入します。

composer require --dev haydenpierce/class-finder
php artisan lighthouse:ide-helper

これによってschema-directives.graphql_lighthouse_ide_helper.phpが生成されるので、それぞれ.gitignoreに入れておきましょう。

ディレクティブ

ここからはLighthouseの主機能となるディレクティブについて説明していきます。

基本的にLaravelのModelとGraphQLのオブジェクト(type)は一対一で自動でマッピングされます。
またtypeを定義する際は主キーに対応するフィールドにはidという命名をすることが推奨されています。

type User {
  id: ID!
  name: String!
  email: String!
}

@all

@allディレクティブはEloquentのall()と同じ役割をします。

type Query {
  users: [User!]! @all
}

以下のようにスキーマを叩くと、以下のようにUserモデルに紐付けられているテーブル(今回はLaravelデフォルトのusersテーブル)の全ての情報が返ってきます。

query {
  user_all {
    id
    name
    email
  }
}
{
  "data": {
    "users": [
      {"id": "1", "name": "hoge", "email": "hoge@example.com"},
      {"id": "2", "name": "fuga", "email": "fuga@example.com"}
    ]
  }
}

@paginate

paginateディレクティブは名前の通り、ペジネーションの役割を果たします。

type Query {
    users: [User!]! @paginate
}

例えば上記のpaginateディレクティブを付与したスキーマは内部的には自動で次のように変換されます。

type Query {
  users(first: Int!, page: Int): PostPaginator
}

type PostPaginator {
  data: [User!]!
  paginatorInfo: PaginatorInfo!
}

そして以下のように叩くことができるようになります。

{
  users(first: 10) {
    data {
      id
      name
    }
    paginatorInfo {
      currentPage
      lastPage
    }
  }
}

またpaginatorには他にもいくつかの機能があります。

デフォルト数の指定

一度のリクエストで返すデフォルトのアイテム数をクエリ時にcount引数で指定することなく、デフォルトで指定できるようになります。

type Query {
    users: [User!]! @paginate(defaultCount: 10)
}

最大数の制限

ページ分割する際に要求できるアイテムの最大数を制限することができます。

type Query {
    users: [User!]! @paginate(maxCount: 10)
}

対象モデルの上書き

デフォルトではスキーマ定義で指定されたtypeと同じ名前のEloquentモデルを検索しますが、model引数を指定することでそれを上書きすることができます。

type Query {
    users: [User!]! @paginate(model: "App\\Models\\ActiveUser")
}

@find

findディレクションは渡された引数からモデル内を検索します。
Eloquentでいうwhereとfindですね。

type Query {
    user(id: ID @eq): User @find
}

注意点として複数の結果が得られた場合例外が投げられるので、確実ではない場合は@firstの使用が推奨されています。

またこちらもpaginateと同様にmodelの上書きをすることが可能です。

type Query {
    user(id: ID @eq): User @find(model: "App\\Models\\ActiveUser")
}

@eq

Eloquentクエリに等号演算子を配置するディレクティブです。

以下の例だと引数のidの値がusersテーブルのidと一致するものを検索します。

type Query {
    user(id: ID @eq): User @find
}

また引数の名前がデータベースのカラム名と異なる場合は実際の列名をkeyに渡します。

type Query {
    user(id: ID @eq(key: "user_id")): User @find
}

@create

新規のデータを登録するにはcreateディレクティブを使用することができます。

type Mutation {
    createUser(name: String!, email: String!, password: String!): User! @create
}

Mutation typeの中でスキーマを作成し、その引数に保存したいデータを指定するだけでデータベースに保存することができます。

mutation {
  createUser(
    name: "hogehoge"
    email: "hogehoge@example.com"
    password: "hogehoge"
  ) {
    id
    name
    email
    created_at
    updated_at
  }
}

ただし注意点として、作成や更新を許可するカラムに対してModelにfillableを指定する必要があります。(updateディレクティブに関しても同様)

User.php
class User extends Authenticatable
{
...
    protected $fillable = [
        'name', 'email', 'password',
    ];
}

またcreateディレクティブでも使用するmodelを変更することが可能です。

type Mutation {
    createUser(input: CreateUserInput! @spread): User! @create(model: "App\\Models\\ActiveUser")
}

独自のリクエスト型を定義する

特定のリクエスト型の定義はinputを指定することで可能です。

引数として単一のオブジェクトを使用する場合はリゾルバーに適応する前にネストした値を展開するように@spreadを使用する必要があります。

type Mutation {
    createUser(input: CreateUserInput! @spread): User! @create
}

input CreateUserInput {
    name: String!
    email: String!
    password: String!
}

参考:Lighthouse#Arg Resolvers

@update

データの更新にはupdateディレクティブを使用します。
idで指定されたデータに対して第二引数以降の値で更新します。

type Mutation {
    updateUser(id: ID!, name: String, email: String): User @update
}

ちなみに動作的には引数を指定しないと更新されず、nullや空文字を送信するとそれぞれの値で上書きされます。
上書きされたくない場合はnot nullやバリデーションを設定するようにしましょう。

またGraphQLでは一部のデータのみを更新するかをクライアント側が選択できるので、サーバ側ではオプションの引数を除く全ての引数を指定することが推奨されています。

もちろんupdateディレクティブでも独自のリクエスト型を定義して使用することが可能です。

type Mutation {
    updateUser(id: ID!, input: UpdateUserInput @spread): User @update
}

input UpdateUserInput {
    name: String
    email: String
    password: String
}

@upsert

upsertはidで指定されたデータが存在すればupdateし、なければ指定されたidで新規にデータを作成します。
またidが指定されていない場合は自動生成されたIDを使用してデータを作成します。

type Mutation {
    upsertUser(id: ID!, name: String!, email: String!, password: String!): User @upsert
}

この時DB上でnot nullなカラムをGraphQLのスキーマ上でnullableにしても新規作成時にnullの値を入れてリクエストできてしまいますが、エラーになるので注意しましょう。(updateは問題なく動きます。)

@delete

deleteはidを指定するだけで簡単にデータを削除できる、ある意味危険なディレクティブです。

type Mutation {
    deleteUser(id: ID!): User @delete
}

返り値は削除したデータがあればそれを返し、指定したidが対象となるデータがない場合はnullを返します。

複数削除

複数データを一度に削除したい場合はIDをリストにします。

type Mutation {
    deleteUser(id: [ID!]!): [User!]! @delete
}

まとめ

今回は基本的なCRUD操作に必要なディレクティブについて学習しました。
自分でスキーマを定義していると返り値や引数周りの設計で悩むことが結構多かったのですが、そこらへんがサンプルで明示的に示されていてとても勉強になりました。

ここまでやった感想としては複雑なビジネスロジックなどが必要のない簡単なアプリならSQLやModelなどを意識する必要がほとんどなく、ほとんどサーバの技術を触ったことがない人でもさっくり作れそうな印象を受けました。

流石に複雑なロジックを入れようとするとディレクティブだけでは難しいのでResolverを使っていくことになりますが、それでもペジネータなどが準備されているのは嬉しいですね。
(キャッシュ周りに関して把握してないので、どのようにデータを持つのかは気になりますが…)

次回はResolverやリレーションなどについて調べていきたいと思います。

参考文献

Lighthouse公式ドキュメント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Photos APIをPHPで使う

Google Photos APIを使用して、Google Photoにアクセスするアプリを作成しています。
その過程でのAPIを利用するまでを記載します。

基本的にオフィシャルのリファレンスを参照していますが、そこから読み取りにくい部分などを具体的に紹介できたらと思っております。

Google Photos API

Laravelを使用したコードを記載していますが、Laravelに特化した部分は少ないと思いますので、
Laravel以外のフレームワークを使っている場合もご参考になればと思います。

準備

Laravelプロジェクト作成

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

Google Photos APIを有効にする

  • Google APIsのダッシュボードからプロジェクト作成 → APIとサービスを有効化 → Google Photos APIを選択
  • Google APIsのプロジェクトが作成済みなら Google Photos API このページから直接もできます

クライアントID作成

  • Google APIsのダッシュボードから「認証情報」を選択 → 認証情報を作成 → OAuthクライアントID
  • アプリケーションの種類は、ウェブアプリケーション
  • 承認済みのリダイレクトURIは、今回は取り合えず http://localhost/photo/redirect
  • 作成したクライアントIDの画面からJSONをダウンロード → credential.jsonにリネームしておく
  • ダウンロードしたcredential.jsonをLaravelプロジェクトのフォルダに入れておきます。

Google APIs Client, Google Photos API ライブラリのインストール

composer.json
    "require": {
        "google/apiclient": "^2.0",
        "google/photos-library": "^1.5"
    },
$ composer update

composer.jsonを編集せずに、直接コマンドラインかrcomposer require google/apiclient:"^2.0"みたいにやるとなぜか失敗します。

Google Photos APIを使ってみる

PhotoControllerという名前のコントローラーと、コントローラーから呼び出されるGooglePhotoServiceという名前のクラスを作成していきます。

認証URLの生成、表示

app\Services\GooglePhotoService.php
<?php
namespace App\Services;

use Google\Auth\Credentials\UserRefreshCredentials;
use Google\Photos\Library\V1\PhotosLibraryClient;

class GooglePhotoService
{
    private function createGoogleClient()
    {
        $client = new \Google_Client();
        $client->setApplicationName('GooglePhotoSample');
        $client->setScopes(\Google_Service_PhotosLibrary::PHOTOSLIBRARY_READONLY); 
        $client->setAuthConfig(base_path("credentials.json"));
        $client->setAccessType('offline');
        return $client;
    }

    public function createAuthUrl($redirectUri)
    {
        $client = $this->createGoogleClient();
        $client->setRedirectUri($redirectUri);
        return $client->createAuthUrl();
    }
}
app\Http\Controllers\PhotoController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\GooglePhotoService;

class PhotoController extends Controller
{
    public function start()
    {
        $service = new GooglePhotoService();
        $url = $service->createAuthUrl("http://localhost/photo/redirect");
        return view(start, [
            'url' => $url
        ]);
    }
}

resources\views\start.blade.php
<html>
<body>
  <div class="flex-center position-ref full-height">
    <div class="content">
      Step.1<br/>
      <a href="{{$url}}">{{$url}}</a>
    </div>
  </div>
</body>
</html>
routes\web.php
Route::get('/photo/start, 'PhotoController@start);

OAuthのリダイレクト先を作成

リダイレクト時に受け取ったauthCodeをもとにアクセストークンを取得する。
取得したアクセストークンを保存する処理は$saveTokenFunctionという名前のコールバック関数として定義する。

app\Services\GooglePhotoService.php
    public $saveTokenFunction = null;

    public function getAccessToken($authCode)
    {
        $client = $this->createGoogleClient();
        $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
        if ($this->saveTokenFunction != null) {
            call_user_func($this->saveTokenFunction, $accessToken);
        }
        return $accessToken;
    }
app\Http\Controllers\PhotoController.php
    public function saveToken($accessToken)
    {
        // 保存の処理は省略。データベースなどに保存
    }

    public function redirect(Request $request)
    {
        $authCode = $request->input('code');
        $service = new GooglePhotoService();
        $service->saveTokenFunction = [$this, 'saveToken'];
        $service->getAccessToken($authCode);
        return view('redirect', [
        ]);
    }
resources\views\redirect.blade.php
// 省略
routes\web.php
Route::get('/photo/redirect, 'PhotoController@redirect);

PhotosLibraryClientを生成

取得したアクセストークンで、PhotosLibraryClientを生成
アクセストークンの期限が切れたときのリフレッシュの処理も一緒に実装

app\Services\GooglePhotoService.php
    public function getPhotosLibraryClient($accessToken)
    {
        $client = $this->createGoogleClient();
        $client->setAccessToken(json_decode($accessToken, true));
        if ($client->isAccessTokenExpired())
        {
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
            if ($this->saveTokenFunction != null) {
                call_user_func($this->saveTokenFunction, $$client->getAccessToken());
            }
        }

        $authCredentials = new UserRefreshCredentials(\Google_Service_PhotosLibrary::PHOTOSLIBRARY_READONLY, [
            "client_id" => $client->getClientId(),
            "client_secret" => $client->getClientSecret(),
            "refresh_token" => $client->getRefreshToken()
        ]);
        return new PhotosLibraryClient(['credentials' => $authCredentials]);
    }

PhotosLibraryClientを使用してGoogle Photoからアルバム情報を取得してみる

app\Http\Controllers\PhotoController.php
    public function listAlbums()
    {
        $accessToken = [] // 保存してあるアクセストークンを取得する処理
        // 例) $accessToken = UserToken::where('user_id', auth()->user()->id)->value('token');
        $service = new GooglePhotoService();
        $service->saveTokenFunction = [$this, 'saveToken'];
        $client = $service->getPhotosLibraryClient($accessToken);
        $pagedResponse = $client->listAlbums();
        $data = [];
        foreach ($pagedResponse->iterateAllElements() as $album) {
            $data[] = $album->getTitle();
        }
        return response()->json([
            "title" => $data
        ]);
    }
routes\web.php
Route::get('/photo/albums', 'PhotoController@listAlbums');

以上です

authCodeからAccessTokenを取得する処理を、なんとかAPIだけで完結させたかったのですが
できませんでした。今回は、このような形でウェブページを一度経由してその後APIで利用するという形になりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lravel MixでVue.js+Bootstrapの簡単なログインフォームを作る。

FIRST PLAN株式会社のフロントエンドエンジニアtakeです。

sampleText

さて、今回はテストでログインフォームを作ったので良ければご覧ください。

こんな事をやりました

Image from Gyazo

環境設定

まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。

$ composer require laravel/ui
$ php artisan ui vue --auth

すぐにvueを始めたい場合は公式ドキュメントにある通りCDNでも使えますし、初めからwebpackの環境構築がされている「Vue CLI」もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。

そして次にBootstrapですが、BootstrapVueを使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。

$ npm install bootstrap-vue bootstrap

でインストールし、
以下のコードをメインのjsファイルに追加します。

import BootstrapVue from 'bootstrap-vue';
Vue.use(BootstrapVue);

後は好みの問題なのですが、FontAwesomevue-sweetalert2をインストールします。

vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。

FontAwesome

$ npm install --save @fortawesome/fontawesome-free
import '@fortawesome/fontawesome-free/css/all.css';

vue-sweetalert2

$ import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
Vue.use(VueSweetalert2);

コード

html

今回は以下のような感じで、cardinput-groupを使って実装しました。

HTML
<template>
  <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center">
    <div class="login-form__field card rounded-0 pt-5 pb-3">
      <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i>
      <div class="card-body">
        <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1>

        <hr class="bg-primary mt-0 mb-4">

        <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-user"></i>
            </span>
          </div>
          <input class="form-control" type="text" placeholder="Username" v-model="login.user_name">
        </div>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-key"></i>
            </span>
          </div>
          <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password">
        </div>

        <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button>

        <div class="login-form__attention d-flex justify-content-between my-o mx-auto">
          <div class="custom-control custom-checkbox">
            <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember">
            <label class="custom-control-label" for="remember-me">情報を記憶する</label>
          </div>
          <a class="card-link" href="#">
            <u>パスワードを忘れた方はこちら</u>
          </a>
        </div>
      </div>
    </div>
  </section>
</template>

Scss

今回はBootstrapベースなので、BEMを採用して影のエフェクト以外は軽く整える程度にしました。

scss
.login-form {
  &__field {
    position: relative;
    max-width: 30rem;
    //影エフェクト
    &:after {
      content: '';
      position: absolute;
      bottom: 20px;
      right: 55px;
      z-index: -1;
      width: 50%;
      height: 100px;
      box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3);
      transform: skew(-40deg);
    }
  } //__field
  &__budge {
    position: absolute;
    top: -2rem;
    left: 50%;
    font-size: 6rem;
    transform: translateX(-50%);
  } //__budge
  &__attention {
    font-size: 0.6rem;
  } //__attention
  &__button {
    border-radius: 0!important;
  } //__button
} // .login-form

JavaScript

これがデフォルトのVueの記述方法になります。
ざっくり説明するとdataは変数等を格納する場所・computedは変更があるとリアルタイムで値を再計算してくれる場所・methodsは文字通りメソッドを格納する場所です。
他にもwatchやライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは公式ドキュメントをご参照ください。

まずdata内のloginは「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内にv-model="login.user_name"といった記述があると思いますが、このv-modelによって双方向データバインディングを実現しています。
これによりデータと表示の同期・DOM操作を自力でやる必要がなくなります。

次はcomputedですが、今回はlogin.user_namelogin.passwordが条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内にif (!this.checkInput)と指定されている関数がありますが、このように使用する事で、他の場所で毎回入力を確認する必要がなくなります。

最後にmethodsは一番簡単で、関数を格納する場所になります。
とりあえず確認用にv-modelで同期した値をsweetalert2で表示しています。

JavaScript
export default {
  data() {
    return {
      //ログイン情報
      login: {
        user_name: '',
        password: '',
        remember: false
      }
    }
  },
  computed: {
    //ユザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる
    checkInput() {
      return (!this.login.user_name || !this.login.password) ? true : false;
    }
  },
  methods: {
    signIn() {
      //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。
      if (!this.checkInput) {
        //v-modelで同期した値を出力する
        this.$swal({
          icon: 'info',
          title: 'Information',
          text: `
            ID: ${this.login.user_name},
            Password: ${this.login.password},
            Remember: ${this.login.remember}
          `
        })
      }
      return;
    } //signIn
  }
}

最後に

$ npm run dev

でコンパイルして確認すると...

Image from Gyazo

キチンと動いています。

よろしければ参考にしてみて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel MixでVue.js+Bootstrapの簡単なログインフォームを作る。

FIRST PLAN株式会社のフロントエンドエンジニアtakeです。

sampleText

さて、今回はテストでログインフォームを作ったので良ければご覧ください。

こんな事をやりました

Image from Gyazo

環境設定

まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。

$ composer require laravel/ui
$ php artisan ui vue --auth

すぐにvueを始めたい場合は公式ドキュメントにある通りCDNでも使えますし、初めからwebpackの環境構築がされている「Vue CLI」もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。

そして次にBootstrapですが、BootstrapVueを使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。

$ npm install bootstrap-vue bootstrap

でインストールし、
以下のコードをメインのjsファイルに追加します。

import BootstrapVue from 'bootstrap-vue';
Vue.use(BootstrapVue);

後は好みの問題なのですが、FontAwesomevue-sweetalert2をインストールします。

vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。

FontAwesome

$ npm install --save @fortawesome/fontawesome-free
import '@fortawesome/fontawesome-free/css/all.css';

vue-sweetalert2

$ import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
Vue.use(VueSweetalert2);

コード

HTML

今回は以下のような感じで、cardinput-groupを使って実装しました。

HTML
<template>
  <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center">
    <div class="login-form__field card rounded-0 pt-5 pb-3">
      <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i>
      <div class="card-body">
        <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1>

        <hr class="bg-primary mt-0 mb-4">

        <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-user"></i>
            </span>
          </div>
          <input class="form-control" type="text" placeholder="Username" v-model="login.user_name">
        </div>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-key"></i>
            </span>
          </div>
          <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password">
        </div>

        <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button>

        <div class="login-form__attention d-flex justify-content-between my-o mx-auto">
          <div class="custom-control custom-checkbox">
            <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember">
            <label class="custom-control-label" for="remember-me">情報を記憶する</label>
          </div>
          <a class="card-link" href="#">
            <u>パスワードを忘れた方はこちら</u>
          </a>
        </div>
      </div>
    </div>
  </section>
</template>

CSS

飽くまでBootstrapベースなので、scssでBEMを採用して影のエフェクト以外は軽く整える程度にしました。

scss
.login-form {
  &__field {
    position: relative;
    max-width: 30rem;
    //影エフェクト
    &:after {
      content: '';
      position: absolute;
      bottom: 20px;
      right: 55px;
      z-index: -1;
      width: 50%;
      height: 100px;
      box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3);
      transform: skew(-40deg);
    }
  } //__field
  &__budge {
    position: absolute;
    top: -2rem;
    left: 50%;
    font-size: 6rem;
    transform: translateX(-50%);
  } //__budge
  &__attention {
    font-size: 0.6rem;
  } //__attention
  &__button {
    border-radius: 0!important;
  } //__button
} // .login-form

JavaScript

以下がデフォルトのVueの記述方法になります。

ざっくり説明するとdataは変数等を格納する場所・computedは変更があるとリアルタイムで値を再計算してくれる場所・methodsは文字通りメソッドを格納する場所です。
他にもwatchやライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは公式ドキュメントをご参照ください。

さて、まずdata内のloginは「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内にv-model="login.user_name"といった記述があると思いますが、このv-modelによって双方向データバインディングを実現しています。
どちらかが変更された段階でもう片方にも自動で変更が同期されるため、自分で逐一document.querySelector(element)~をする必要がなくなります。

次はcomputedですが、今回はlogin.user_namelogin.passwordが条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内にif (!this.checkInput)と指定されている関数がありますが、このように使用する事で他の場所で毎回入力を確認する必要がなくなります。
ちなみにbuttonにある:disabled="checkInput"にも使用されていますが、まずこの:マークはv-bindといってhtml内の属性(classhref等)に付ける事で内部でJavaScriptのコードを直接使用可能にするものです。
今回はこれらを利用してdisabledの値をフォーム入力の有無によって動的に変更しています。

最後にmethodsは一番簡単で、関数を格納する場所になります。
とりあえず確認用にv-modelで同期した値をsweetalert2で表示しています。
イベントハンドリングはhtml側で@event="hoge"のように行います。(今回の場合は@click="signIn"
ちなみにこの@click以下はコンパイル時に削除されるため、htmlを汚染する心配はありません。

JavaScript
export default {
  data() {
    return {
      //ログイン情報
      login: {
        user_name: '',
        password: '',
        remember: false
      }
    }
  },
  computed: {
    //ユーザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる
    checkInput() {
      return (!this.login.user_name || !this.login.password) ? true : false;
    }
  },
  methods: {
    signIn() {
      //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。
      if (!this.checkInput) {
        //v-modelで同期した値を出力する
        this.$swal({
          icon: 'info',
          title: 'Information',
          text: `
            ID: ${this.login.user_name},
            Password: ${this.login.password},
            Remember: ${this.login.remember}
          `
        })
      }
      return;
    } //signIn
  }
}

最後に

$ npm run dev

でコンパイルして確認すると...

Image from Gyazo

キチンと動いています。

よろしければ参考にしてみて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#MacでLaravelの環境構築をした件

MacでLaravelの環境構築をした件

MacでLaravelの環境構築をした際の内容をまとめます.

目次

動作環境

OS : macOS Mojave 10.14.6
HOMEBREW_VERSION: 2.2.17
Composer version 1.10.6
Laravel Installer 2.3.0
PHP : 7.2.31

構築手順

homebrewのインストール

Homebrewはmacのパッケージマネージャです。
パスワードを求められたらMacのログインパスワードを適宜入力してください

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

インストールが完了したらアップデートをして完了です.

$ brew upgrade

composerのインストール

brew install composer

インストールが完了したらPATHを追加し、インストールできているか確認します.

PATHの追加とバージョン確認
$ echo export PATH=\"$HOME/.composer/vendor/bin:\$PATH\" >> ~/.bash_profile
$ source .bash_profile
$ composer -V
Composer version 1.10.6 2020-05-06 10:28:10

phpのインストール

PHPのバージョンを確認

$ php -v
PHP 7.1.23 (cli) (built: Feb 22 2019 22:19:32) ( NTS )

注意点

7.1系でLaravel newを行うと以下のエラーが出たため7.2以上をインストールします.

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - This package requires php ^7.2.5 but your PHP version (7.1.23) does not satisfy that requirement.

7.2系のインストール

brew install php@7.2
PATHの追加とバージョン確認
$ echo 'export PATH="/usr/local/opt/php@7.2/bin:$PATH"' >> ~/.bash_profile
$ echo 'export PATH="/usr/local/opt/php@7.2/sbin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
$ php -v
PHP 7.2.31 (cli) (built: May 14 2020 10:55:21) ( NTS )

laravelのインストール

laravelのインストール
$ composer global require "laravel/installer"

注意点

WEBサイトで以下のコマンドを書いていることがありましたが、このコマンドではエラーになります.詳細はこちらをご参考ください

$ composer global require “laravel/install”

動作確認

最後にアプリを作成するディレクトリに移動し実行

$ laravel new dg_laravel_test

サーバーを起動

$ php artisan serve
Laravel development server started: http://127.0.0.1:8000
[Tue May 19 16:52:04 2020] 127.0.0.1:56892 [200]: /favicon.ico

ブラウザからhttp://127.0.0.1:8000にアクセス
以下の画面がでれば成功

スクリーンショット 2020-05-19 16.56.21.png

おわりに

今回の件で以下のことを学びました。
1. Mac OSでのLaravelの環境構築

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ドメインの異なるサイトにAPIリクエストを送るとエラーが発生

ブラウザから違うドメインにAPIリクエストをした際に、以下のエラーが発生

Access to XMLHttpRequest at 'http://xxxxx' from origin 'http://localhost:3001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

原因

ドメインが違うサイトにリクエストを送る時、「あなたのサイトからのリクエストを許可していない」ときに起こるエラー

解決策は3つ

  1. リクエストを送りたいサイトに、自分のサイトのドメインからのリクエストを許可してもらう。
  2. Chromeの拡張機能 CORS Unblock を使ってリクエストを送れるようにする
  3. サーバサイド(Nodeやphpなど)からリクエストを送る。

実施した解決策

リクエストの送信先がLaravelで作っている自作APIだったので、1 の方法を実施。
具体的には、laravelに新しくCORSを許可するミドルウェアを作成して、router.php で該当のAPIリクエストにミドルウェアを適応することで解決。

参考にしたサイト

クロスドメインによるエラーを解決する
CORSについて理解してLaravel5.6で対応する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【CORS】ドメインの異なるサイトにAPIリクエストを送るとエラーが発生

ブラウザから違うドメインにAPIリクエストをした際に、以下のエラーが発生

Access to XMLHttpRequest at 'http://xxxxx' from origin 'http://localhost:3001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

原因

ドメインが違うサイトにリクエストを送る時、クライアント(送信元のサイト)からのリクエストを許可していないときに起こるエラー

解決策は3つ

  1. リクエストを送りたいサイトに、自分のサイトのドメインからのリクエストを許可してもらう。
  2. Chromeの拡張機能 CORS Unblock を使ってリクエストを送れるようにする
  3. サーバサイド(Nodeやphpなど)からリクエストを送る。

実施した解決策

リクエストの送信先がLaravelで作っている自作APIだったので、1 の方法を実施。
具体的には、laravelに新しくCORSを許可するミドルウェアを作成して、router.php で該当のAPIリクエストにミドルウェアを適応することで解決。

参考にしたサイト

クロスドメインによるエラーを解決する
CORSについて理解してLaravel5.6で対応する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【CORS】ドメインのことなるサイトにAPIリクエストを送るとエラーが発生

ブラウザから違うドメインにAPIリクエストをした際に、以下のエラーが発生

Access to XMLHttpRequest at 'http://xxxxx' from origin 'http://localhost:3001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

原因

ドメインが違うサイトにリクエストを送る時、「あなたのサイトからのリクエストを許可していない」ときに起こるエラー

解決策は3つ

  1. リクエストを送りたいサイトに、自分のサイトのドメインからのリクエストを許可してもらう。
  2. Chromeの拡張機能 CORS Unblock を使ってリクエストを送れるようにする
  3. サーバサイド(Nodeやphpなど)からリクエストを送る。

実施した解決策

リクエストの送信先がLaravelで作っている自作APIだったので、1 の方法を実施。
具体的には、laravelに新しくCORSを許可するミドルウェアを作成して、router.php で該当のAPIリクエストにミドルウェアを適応することで解決。

参考にしたサイト

クロスドメインによるエラーを解決する
CORSについて理解してLaravel5.6で対応する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでコマンドライン処理を行う

前提条件

eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

Commandクラス作成

コマンドラインで
cd sample
php artisan make:command SampleCommand
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します
eclipseプロジェクトを右クリック→リフレッシュ
/sample/app/Console/Commands/SampleCommand.phpが現れます

Commandクラス修正

さきほど作成したSampleCommand.phpを下記に修正します

SampleCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SampleCommand extends Command
{
    protected $signature = 'sample:name1 {arg1} {arg2=val2} {arg3?} {--option1} {--option2=} {--option3=opVal3} {--option4=*}';

    protected $description = 'このプログラムの説明を書く';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $arg1 = $this->argument('arg1');
        $arg2 = $this->argument('arg2');
        $arg3 = $this->argument('arg3');
        $option1 = $this->option('option1');
        $option2 = $this->option('option2');
        $option3 = $this->option('option3');
        $option4 = $this->option('option4');

        $this->line('下記入力を受け付けました');
        $this->line('arg1:' . $arg1);
        $this->line('arg2:' . $arg2);
        $this->line('arg3:' . $arg3);
        $this->line('option1:' . $option1);
        $this->line('option2:' . $option2);
        $this->line('option3:' . $option3);
        $this->line('option4:' . var_export($option4, true));

        $in = $this->ask('何か入力してください');
        $this->line('下記入力を受け付けました');
        $this->line($in);

        $in = $this->secret('何か入力してください。この入力中はユーザーがタイプした値を表示しません');
        $this->line('下記入力を受け付けました');
        $this->line($in);

        if ($this->confirm('yかyesを入力するとtrueとして扱います')) {
            $this->line('true');
        } else {
            $this->line('false');
        }

        $in = $this->choice(
                  '選択入力。カンマ区切りで複数入力できます',
                  ['php', 'Laravel', 'apache', 'eclipse'], //選択肢
                  $defaultIndex = 1, // 何も選ばれなかった場合に返ってくる選択肢の要素
                  $maxAttempts = 2, // 最大選択可能個数
                  $allowMultipleSelections = true // 複数選択可ならtrueにする
            );
        $this->line('下記入力を受け付けました');
        $this->line(var_export($in, true));

        $this->line('テーブル出力');
        $head = ['col1', 'col2', 'col3', 'col4'];
        $body = [
            ['1-col1', '1-col2', '1-col3', '1-col4'],
            ['2-col1', '2-col2', '2-col3', '2-col4'],
            ['3-col1', '3-col2', '3-col3', '3-col4'],
        ];
        $this->table($head, $body);


        $this->line('プログレスバー出力');
        $bar = $this->output->createProgressBar(10); // プログレスバーを10の区切りで出力
        $bar->start();
        for ($i = 0; $i < 10; $i++) {
            sleep(1);
            $bar->advance();
        }
        $bar->finish();

        $this->line('');
        $this->error('エラー出力');

    }
}

$signature変数はこのコマンドの引数、オプションを定義します
ユーザーから入力してもらう引数とオプションはすべて波括弧で囲んで定義します
{arg1} 第1引数必須。プログラム内で$this->argument('arg1')で値を取得できる
{arg2=val2} 第2引数任意。入力されなかった場合、$this->argument('arg2')でval2が返る
{arg3?} 第3引数任意。デフォルト値無し
{--option1} オプション。option1オプションが指定されると$this->option('option1')でtrueが返る
{--option2=} オプション。コマンド実行時何か値を与えるオプション
{--option3=opVal3} オプション。入力されなかった場合、$this->option('option3')でopVal3が返る
{--option4=*} オプション。コマンド実行時何か値を与えるオプション。複数の値を渡すことができる。$this->option('option4')の結果は配列となる

$description変数はこのコマンドの説明です

handleメソッドは実際の処理です
コマンドライン処理が実行されるとhandleメソッドが実行されます
$this->argumentは引数を取得できます
$this->optionはオプションを取得できます
$this->lineは文字列を出力できます
$this->askはユーザーからの入力を待ちます
$this->secretはユーザーからの入力を待ちます。ただし、ユーザーのタイピングした値が表示されません。パスワード入力などに使います
$this->confirmはユーザーの入力によりbool値を返します
$this->choiceはユーザーに選択肢から値を選択させます
$this->tableはテーブル形式で表示されます
$this->output->createProgressBarの戻り値を使うことによってプログレスバーを表示できます
$this->errorでエラー時の文字列出力ができます

Commandクラス登録

作成したCommandクラスを呼び出せるようにLaravelに登録しましょう
/sample/app/Console/Kernel.php修正

Kernel.php
‥‥
    protected $commands = [
        Commands\SampleCommand::class
    ];

‥‥

Kernel.phpに定義されている$commands配列に先ほど作成したSampleCommandクラス名を追記します
これでコマンドラインで実行できるようになりました

動作確認

コマンドラインで
cd sample
php artisan sample:name1 aaa bbb ccc --option1 --option2=eee --option3=fff --option4=ggg --option4=hhh
先ほど作成したSampleCommandクラスの$signature変数に定義した通りにコマンドラインで実行します
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します

実行結果

下記入力を受け付けました
arg1:aaa
arg2:bbb
arg3:ccc
option1:1
option2:eee
option3:fff
option4:array (
  0 => 'ggg',
  1 => 'hhh',
)

 何か入力してください:
 > aaa

下記入力を受け付けました
aaa

 何か入力してください。この入力中はユーザーがタイプした値を表示しません:
 >

下記入力を受け付けました
bbb

 yかyesを入力するとtrueとして扱います (yes/no) [no]:
 > y

true

 選択入力。カンマ区切りで複数入力できます [Laravel]:
  [0] php
  [1] Laravel
  [2] apache
  [3] eclipse
 > 2,3

下記入力を受け付けました
array (
  0 => 'apache',
  1 => 'eclipse',
)
テーブル出力
+--------+--------+--------+--------+
| col1   | col2   | col3   | col4   |
+--------+--------+--------+--------+
| 1-col1 | 1-col2 | 1-col3 | 1-col4 |
| 2-col1 | 2-col2 | 2-col3 | 2-col4 |
| 3-col1 | 3-col2 | 3-col3 | 3-col4 |
+--------+--------+--------+--------+
プログレスバー出力
 10/10 [============================] 100%
エラー出力

実行できました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel + Vue.jsのかんたん実装

laravel上でVuejsを実装は、大まかに3段階です。

  1. resources/js/components にvueデータを作成。
  2. resources/js/app.jsにvueコンポーネントを定義。
  3. app.jsに定義したvueコンポーネントを resources/views/*.blade.phpにマークアップ。

スクリーンショット 2020-05-31 23.45.19.png

ExampleComponent.vueを表示してみましょう!
app.jsを以下のように設定します。

resources/js/app.js
require('./bootstrap');

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue').default);

const app = new Vue({
    el: '#app',
});

デフォルトのwelcome.blade.phpに記述します。

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

        <title>ExampleComponent</title>
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    </head>
    <body>
        <div id="app">
            <example-component></example-component>
        </div>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

vueファイルを記述、変更したらターミナルでコンパイルをします。

#コンパイル
npm run dev
#サーバーを立ち上げて確認
php artisan serve

表示されました!
スクリーンショット 2020-06-01 0.08.10.png

新しくVueを作成してみましょう。
1. vueファイルを作成します。
スクリーンショット 2020-06-01 0.12.15.png

resources/js/components/LaravelVue.vue
<template>
  <div>
    <h1>LaravelでVueを実装</h1>
    <p>Good!!</p>
  </div>
</template>
  1. resources/js/app.jsにvueコンポーネントを定義します。
resources/js/app.js
require('./bootstrap');

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue').default);
Vue.component('laravelvue', require('./components/LaravelVue.vue').default);

const app = new Vue({
    el: '#app',
});
  1. app.jsに定義したvueコンポーネントを resources/views/welcome.blade.phpにマークアップ。
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>laravelvue</title>
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    </head>
    <body>
        <div id="app">
            <laravelvue></laravelvue>
        </div>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

コンパイルのたびにnpm run devするのは、面倒くさいので
vueファイルの記述が変更されるたびに自動でコンパイルしてくれるnpm run watch
を実行します。

#コンパイル
npm run watch

command + Tで新しくタブを作ってサーバーを立ち上げます。

#サーバーを立ち上げて確認
php artisan serve

表示されました!
スクリーンショット 2020-06-01 0.19.17.png

もう一度vueファイルの記述を変更してみましょう。

resources/js/components/LaravelVue.vue
<template>
  <div>
    <h1>LaravelでVueを実装できた!!</h1>
    <p>Yeah!!</p>
  </div>
</template>

npm run watchで監視されているので、vueファイルの記述に変更があると自動的にコンパイルをしてくれます。
ブラウザを更新してみましょう!
スクリーンショット 2020-06-01 0.32.47.png
更新されています。

かんたんでは、ありますが少しでも参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む