Laravel・PHP入門

PHPer初心者

【#bcu30】BATTLE CONFERENCE UNDER30 2018 公開スライドまとめ

昨日2018.04.21にCyberAgentさん主催の BATTLE CONFERENCE UNDER30に参加してきました!

bcu30.jp

控えめにいってもすごくおもしろくて勉強になりました..!

体感熱いうちに復習したいのですが 同じように探したい方もいらっしゃるかと思いまして 既にTwitterでシェアされていたスライドのリンクをまとめます。 slideshareとspeakerdeckで横幅違うんですね。

運営、登壇者の皆様本当にありがとうございました!

サーバ・インフラ

3. 動画配信サービスとしてこの先生きのこるには by AbemaTV 山中さん

speakerdeck.com

4. U30エンジニアだからこそ実現できた劇的ビフォーアフター by エイチーム 田中さん

speakerdeck.com

7. クラウドサービスの成長とログ基盤の進化 by サイボウズ 上岡さん

www.slideshare.net

6. カバレッジ95%以上を実現したテストコードの書き方 by HRBrain 鈴木さん

speakerdeck.com

5. モンストのサーバー負荷との戦い 〜あけおめ2018編〜 by ミクシィ 浜田さん

speakerdeck.com

11. 事業を伸ばすエンジニアリング by マッチングエージェント 木邑さん

speakerdeck.com

データ

4. ユーザー行動の数理モデルと高速推薦システム by Gunocy 米田さん

speakerdeck.com

5. レコメンドは稼げる by ランサーズ 高田さん

www.slideshare.net

クライアント

3. 先人の亡霊 VS 俺 〜 Rails の JS の場合 〜 リブセンス 鈴木さん

www.slideshare.net

5. 5年間VRゲームを作ってみた by gumi 渡部さん

www.slideshare.net

8. 複数人で高速に開発するための npm モジュール by ソウゾウ 大原さん

speakerdeck.com

9. CASHのユーザビリティを支える(泥臭い)技術 by BANK 熊谷さん

speakerdeck.com

開発プロセス

1. 現場エンジニアが採用活動やってみたお話。by サイボウズ 杉山さん

speakerdeck.com

2. エンジニアが数字を作る開発チームの生まれ方 by リクルートマーケティングパートナーズ 小原さん

speakerdeck.com

随時upお見かけしたら追加します。 bcu来年も参加したい!本当にありがとうございました!

【API】【Swagger】Swaggerをモックサーバとして機能させるまで

今回、サーバ・フロントの完全分業実現のため Swaggerを導入し、APIの仕様を管理、かつモックサーバとして機能させることに。

仕様を決める→モックサーバとして動作させるまでの流れメモ。

動作確認環境

Mac OS X以上 Swagger2.0 (Open APIは3.0が最新ですがswagger-codegen使用のため2.0を使用) はやく3.0対応してほしみ。。。

流れ

※色々インストールなどの前準備※ ①API仕様をSwagger形式(yaml)で記載、 ②Swagger-Codegenを使用してnode.jsのプロジェクトへ変換 ③サーバ起動 →モックサーバとしてブラウザ上での確認、 自分のプロジェクトからもレスポンス確認ができる

良いこと: * Githubyamlファイルのみの管理で良い * コマンド一つでモックサーバが起動する

悪いこと: * 仕様書を書き換えるたびにプロジェクトの上書きが必要 * サーバ必要

前準備(brew/npmは入ってる前提)

@任意のdirで(今回は/Users/ユーザ名/swagger/) * javaのinstall(入っていなければ) brew cask install caskroom/versions/java8

  • swagger-codegenのinstall brew install swagger-codegen

swagger形式のAPI仕様書作成

  • swagger.yamlファイルの作成(名前は任意、api1.yamlなどでもOK) vi swagger.yaml
swagger: '2.0'

// モックのメタデータを記述する
info:
  description: |
    This is a sample server Petstore server.  You can find
    out more about Swagger at
    [http://swagger.io](http://swagger.io) or on
    [irc.freenode.net, #swagger](http://swagger.io/irc/).
  version: 1.0.0
  title: Swagger Petstore
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html

// モックサーバーのプロトコルの選択
schemes:
- http

// エンドポイントの記述
paths:
  /pet/{petId}:
    get:
      tags:
      - pet
      summary: Find pet by ID
      description: Returns a single pet
      operationId: getPetById
      produces:
      - application/json
      parameters:
      - name: petId
        in: path
        description: ID of pet to return
        required: true
        type: integer
        format: int64
      responses:
        200:
          description: successful operation
          schema:
            $ref: '#/definitions/Pet'
        400:
          description: Invalid ID supplied
        404:
          description: Pet not found

// モデル定義の記述
definitions:
  Category:
    type: object

    // モデルの詳細定義
    properties:
      id:
        type: integer
        format: int64
      name:
        type: string
  Tag:
    type: object
    properties:
      id:
        type: integer
        format: int64
      name:
        type: string
  Pet:
    type: object
    required:
    - name
    - photoUrls
    properties:
      id:
        type: integer
        format: int64
      category:
        $ref: '#/definitions/Category'
      name:
        type: string
        # 各値の例の記述
        example: doggie
      photoUrls:
        type: array
        items:
          type: string
      tags:
        type: array
        items:
          $ref: '#/definitions/Tag'
      status:
        type: string
        description: pet status in the store
        enum:
        - available
        - pending
        - sold
  • swagger codegenコマンドで、yamlファイルから任意のディレクトリ内にモックサーバのプロジェクト生成(今回はnodejsで作ってます)

mkdir codegen-test/

swagger-codegen generate -i swagger.yaml -l nodejs-server -o codegen-test/

codegen-test は任意のディレクトリ名でok

  • プロジェクト生成後、パッケージ諸々インストールして、モックサーバ起動
cd codegen-test
npm install
node index.js
Your server is listening on port 8080 (http://localhost:8080)
Swagger-ui is available on http://localhost:8080/docs

こんなレスポンスが帰って来れば、localhost:8080/docsへアクセスすれば swagger-ui上でテストできます!

一度作った仕様書も、更新した後はもう一度node.jsファイルへ変換してあげれば 既に作ったプロジェクトの中身が全部更新されます

OpenAPI3.0になるとかなり変わるので、 今度そのあたりもまとめたいなー かなりハマったのが2.0と3.0の違いでした。。。

ちなみに今は配列でのレスポンス、ファイルの結合とかで苦労してました phpyaml結合ツールつくるしかないかなー とりあえず一連のモックサーバ起動までの流れをshellかこう。

ではまた!

【🔰初心者】【API】RESTfulなAPIのリクエストとレスポンスについて

最近APIの設計について考えることが多かったのでまとめておきます。
※RESTFulとは何か、については言及しません。
あくまでリクエストとレスポンスの設計について言及します。

そもそもAPIとは

まず自分が初心者である時一番わからなかったのがAPIって何ってことでした。
偏見イメージで話すと、アプリケーションとアプリケーションを繋げるアダプタのようなものです。
皆さん例えばパソコンとモニターをつなぐケーブルをお持ちですか?

Amazonベーシック HDMI-DVI 変換ケーブル - 1.8m (タイプAオス- DVI24pinオス)

自分のパソコンが出力できる形と、モニターへ入力できる形って違っていますよね?
それを整形して、繋げてあげるのがケーブル、つまりAPIの役割です。

サーバ側:APIから受け取ったリクエストからデータをAPIに返す
API:特定のリクエストを受け取ってサーバ側に渡す
サーバで処理した後のデータをJSON形式で返す
フロント側:JSONしか受け取らない、APIから帰ってきたデータを整形して表示する

イメージとしてはこんな感じです。
なので、APIを考えるときは、
「どんなリクエストに対して、どんなレスポンスを返す必要があるのか」
を考えます。
多分あんまりわからないと思うので、もう少し具体的に考えてみます。

qiita.com

この記事にめちゃくちゃお世話になりました。ありがとうございます。土下座

例えばよくある、usersというリソースのCRUD処理について考えます。

/users(単数形)についてRESTfulなAPIを考える

まずは、単純な単数形で考えます。あくまで一例です。
レスポンスなどは特にベストプラクティスではありませんのでご注意ください。

▼CREATE

Request:POST /users
Response:201 CREATEしたuserリソース
ユーザは基本的に、createしたあとは自分の入力データがちゃんと保存されたかどうか?
という答えを求めるます。なので、成功レスポンスにはリソースを含める必要がありますね。

▼READ

Request:GET /users/{userId}
Response:200 要求したリソース(指定idのuserリソース)
ユーザは指定IDのuserを要求しており、今回は要求されたリソースを返します。

▼UPDATE

Request:PUT /users/{userId}
Response:201 UPDATEしたuserリソース
CREATEと同様、ユーザは自分の入力データがちゃんと更新できたかどうか?
を求めています。なので、成功レスポンスにリソースを含めます。

▼DELETE

Request:DELETE /users/{userId}
Response:204 NoContent
削除したあと、自分がどんなデータを削除したか気にしたことがあるでしょうか?
ユーザは削除に成功したかどうかは求めますが、
DELETEは中身を必要としないケースが多いです。
今回も同様のため、204でリソースを含んでいません。

こんな感じです。
ただ、複数形になるとそれはそれで変わります。(これも一例)

/users(複数形)についてRESTfulなAPIを考える

単数と複数で、何が変わりますか?
リクエストもしかり、(例えば一括削除なんかはどうでしょうか?)
レスポンスもしかりですね。
なぜなら、100件をCREATEした時に、100件がCREATEされたかどうかをきにする人はいたとしても、
100件のリソース全てをチェックしたいと思うユーザはどれほどいるでしょうか?

それを踏まえて、同様にAPIを考えてみます。

▼CREATE

Request:POST /users/create
Response:204 No Content
上に書いた内容と同様で、100件など多くの一括操作は中身を求めません。

▼READ

Request:GET /users
Response:200 要求したリソース(user一覧のリソース)
user一覧を要求し、それに対してuser一覧を返します。

▼UPDATE

Request:POST /users/update
Response:204 No Content
CREATEと同様ですね
POSTリクエストのbodyに、どのデータをどうupdateするのか
を含めます。

▼DELETE

Request:POST /users/delete
Response:204 NoContent
POSTリクエストのbodyに、どのデータをdeleteするのか
を含めます。

参考URL

http://www.atmarkit.co.jp/ait/articles/1511/19/news022_3.html
https://developer.cybozu.io/hc/ja/categories/200147600-kintone-API
https://developer.github.com/v3/
http://doc.ec-cube.net/api_policy

以上
APIについては色々意見があると思いますが
ざっくり今日考えたのはこんな感じ。

ではまた!

【JavaScript】メモ

サーバサイドエンジニアがフロントエンドエンジニアに 色々質問してきたのでそのまとめ。

webpackのお仕事

  • ES6 → ES5にコンパイルしている 1~2年前ES6非対応ブラウザが多く、それらに対応するため

  • indexファイルにまとめている import/exportなどファイル別だったのに なぜ一つにするのか? →ファイルを分けると、HTTPリクエストがとんで重くなるから

コンポーネント/クラス

いつも実装着手するとき、何を考えてますか? →コンポーネントを考える

例えば〜一覧の例だと、indexTableっていう一覧テーブルを 一つのコンポーネントとして考える

component // クラス定義 クラスの名前、ファイルの名前とまとめて各

小さいコンポーネント? →3つの選択肢から選ぶセレクトボックスとか

ルートコンポーネント// indexPage は 1つのページに1つしかない ルートコンポーネント.find('');

jQuery

  • jQueryでDOM取得して配列で帰ってくる →配列 each ごとにインスタンス化している

  • DOM操作はすごく重い DOM操作 = getElementbyId("")など なので、高速化のために今業務では innerhtmlで全部ぶちこんでる

関数について

$(() => {
});

これは

document.ready(() => {
});

と同意義

なので、例えば

$(() => {
});

$(() => {
});

などを同じファイルで繰り返していると、無意味

グローバル空間について

全部に影響する、$(() => {});などでくくられていない空間のこと window は全て、あらゆるもの情報を持っている

関数の中で、変数をdefineすればその空間のみ、 グローバル空間に影響NGのため、即時関数で実行していた 今はwebpackがやってくれているので グローバル空間は無視してok

セレクタについて

取得の仕方ってどうやってますか? idで取ってくるのが一番はやいのでid最優先、 1ページにいくつもでてくるようであればクラス指定をする。

.on('click')と.click()について

.on('click')は後からappendされた要素も対応するが .click()は対応しないので、 基本的にはon('click')しか使わない

$(div .classA)など複数指定のやつ

両方探すのに時間かかるので、 div.find(classA)など絞ってからにする

※このあたりは、 jQuery高速化で調べればでてくる

thisについて

ES5→ES6で、thisの意味が変わった

ES5 は例えばイベントが起これば、 そのイベントまるごとのことをthisと指していた ES6ではthisは一つ外側を指している。 イメージでいくとphpのクラス。

そのお陰で、外側のプロパティにアクセスしやすくなった

【Laravel/PHP】NULLや空文字列の判定まとめ

業務でNULL判定よく使うのでまとめ。

# 判定表

Laravel内で使っているときは
定義されていることが前提の条件文でエラーをはく。

Laravel5.5 + PHP7.1

未定義 NULL 0
if($a) error false false false
if($a == NULL) error true true true
if($a === NULL) error true false false
if($a == 0) error true true true
if($a === 0) error false true false
if($a == "") error true true true
if($a === "") error false false true
is_null($a) error true false false
isset($a) false false true true
empty($a) true true true true


例えば

<?php
public function is_null()
{
    if($a) {
        return "aだ";
    }
    return "aではない";
}

これを実行すると、Undefined variable: a
が返る。三項演算子も同様。

その代わり、

<?php

public function is_null()
{
    if(isset($a)) {
        return "aは存在する";
    }
    return "aは存在しない";
}

これを実行しても、"aは存在しない"がかえる。
issetはそもそも定義されているかどうかを判別するメソッドだからだ。


ちなみにLaravelでなく、PHPベタだとレスポンスが変わる

未定義 NULL 0
if($a) false false false false
if($a == NULL) true true true true
if($a === NULL) true true false false
if($a == 0) true true true true
if($a === 0) false false true false
if($a == "") true true true true
if($a === "") false false false true
is_null($a) true true false false
isset($a) false false true true
empty($a) true true true true

undefinedには気をつけましょう

【Laravel】ユーザの使用端末がPCかスマホか判定する(PHP)

PCとスマホ
表示項目やバリデーションを場合分けしたかったので。


Requestインスタンス
端末のデータを持ってくれている!
そこから、大概のスマートフォンの情報のみを省いて
場合分けする。

このUser-Agentが持ってるらしい。

 'agent' => $request->header('User-Agent')

なので、これを使って場合分け。

<?php

...

    /**
     * クライアントの使用端末がMobileかPCか判定
     *
     * @param $request
     * @return string
     * @access private
     */
    private function isMobileOrPc($request): string
    {
        $user_agent =  $request->header('User-Agent');
        if ((strpos($user_agent, 'iPhone') !== false)
            || (strpos($user_agent, 'iPod') !== false)
            || (strpos($user_agent, 'Android') !== false)) {
            return 'mobile';
        } else {
            return 'pc';
        }
    }

どうやら、iPoneとiPod(iPadiPodになるそうで)、後Androidを省けば
大体対応できるようです。海外はわかりませんが

以上

【Laravel】GoogleのOAuth認証でログイン機能を実装する(PHP)

LaravelのSocialiteに感動したのでメモ。

【やりたいこと】
GoogleのOAuthAPIを用いてログイン機能を実装する

※ログインのみ、GoogleAPIを用いて判定、
ユーザ情報はGoogle APIから取得する。

また、今回はuserの新規作成は割愛します。ログインのみ。
emailで管理ユーザか否か判定します。
※必要な方は、ログイン時にユーザがいなければcreateしてください。

【環境】
Laravel5.5
PHP7.2
Google API

【前提】
php artisan make:auth 実施済(なければ叩いてください)
Usersテーブル(もしくはauthに対応したテーブル)が存在する
DBは接続済み
ログインできるGoogleアカウントがある

やってみます。


Laravelは公式で、GoogleFacebookTwitterなど
様々なソーシャル認証を使用できるパッケージを持っています。(神)

Google

OAuth情報を取得・設定

この辺りは、Qiitaや記事がたくさんありますので割愛。
Google developerコンソールで、プロジェクト作成します。
Google OAuth 2.0 認証を使ったログインの実装 - Qiita
GoogleのOAuth2.0を使ってプロフィールを取得【PHP】 - FaMirror Project

・クライアントID(自動発行)
・クライアントシークレット(自動発行)
・リダイレクト先 (自分で設定)Ex.https://samplesample.jp/login/google/callback

が今後必要になります。

※redirectは、ログイン後にアクセスするURLになります。ご自身で設定ください。

※使用できるURLに制限があります!
.comや.jpなど、パブリックドメインのみ使用可能。(プライベートはlocalhostのみ)
これでかなりハマりました。。。
apacheいじって、.jpに変更したらgoogleが許してくれました。
もしgoogleから not foundなどのエラーが吐かれた場合、ここに問題ある可能性あります。

Google側の必要情報が取得できたら、
それらを認証できるよう、APIを有効にしていきます。

APIの有効化

Google のdeveloperコンソール、左側メニューのライブラリを選択。
Google Cloud Platform

Google + API
Google Drive API
・Contacts API

この3つが必要になりますので、全て有効にしてください。

f:id:fresh_engineer:20180110014305p:plain

こういうものですね。
これで、Google側の設定は完了です。

Laravelの設定

Socialiteパッケージをインストール

まずは、composerでinstallしましょう。

composer require laravel/socialite

Configの設定変更

コントローラでSocialiteを使用するため、
providerとaliasに登録しておきます。

config/app.php

<?php
Laravel\Socialite\SocialiteServiceProvider::class,

/.../

'Socialite' => Laravel\Socialite\Facades\Socialite::class,


config/service.php

<?php
'google' => [
         'client_id' => env('GOOGLE_CLIENT_ID'),
         'client_secret' => env('GOOGLE_CLIENT_SECRET'),
         'redirect' => env('APP_URL').'/login/google/callback',
],

シークレットなど隠すために、.envファイルで設定します。

.envファイル

<?php
APP_URL=(自分のwebアプリURL(上の方に既存))
...
GOOGLE_CLIENT_ID=(自分のクライアントID|新規作成)
GOOGLE_CLIENT_SECRET=(自分のクライアントシークレット|新規作成)


以上で設定が完了しました。
Controller/Model/Viewを実装します。

Laravelで実装

Route実装

まずはControllerにアクセスできるよう、Routeを設定しましょう。

<?php
// ログイン前アクセス可能(Google OAuth認証)
 Route::group(['prefix' => '/login/google', 'namespace' => 'Auth'], function ($router) {
     $router->get('/', 'LoginController@index')->name('login');
     $router->get('/redirect', 'LoginController@getRedirect');
     $router->get('/callback', 'LoginController@getCallback');
 });

Controller実装

AuthでLoginコントローラがあるはずなので、
Loginコントローラを編集します。

app/Http/Controller/Auth/LoginController.php

<?php

use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
use App\Models\User as UserModel; 
// 私はModelディレクトリ配下にUserModelを置いてるので
use Socialite; // Socialite使用可能にする

 class LoginController extends BaseController
 {
 use AuthenticatesUsers;

private $user_model;

public function __construct(UserModel $user_model)
{
    $this->middleware('guest:admin')->except('logout');
    $this->user_model = $user_model; //Usermodelをインスタンス化
}


     /**
      * エラーの有無を確認して、ログインページにリダイレクト
      *
      * @return response
      * @access public
      */
     public function index()
     {
         Auth::logout();
         return $this->render();
     }

    /**
     * Google認証ページヘユーザーをリダイレクト
     *
     * @return \Illuminate\Http\Response
     * @access public
     */
    public function getRedirect()
    {
        return Socialite::driver('google')->redirect();
    }

   /**
      * Googleからユーザー情報を取得、ログイン権限があればログイン
      *
      * @return \Illuminate\Http\Response
      * @access public
      */
     public function getCallback()
     {
         $email = Socialite::driver('google')->stateless()->user()->email;
         $user = $this->user_model->getAdminUserFromEmail($email);
         // すでに登録しているユーザとemailが一致するか確認

         if ($user === null) {
             return redirect()
                 ->route('login')
                 ->with('message', 'ログイン権限がありません。');
         }

        Auth::login($user, true); //弾かれなければ$userオブジェクトを用いてログイン
         return redirect($this->redirectTo); //ログイン後リダイレクト
      }
  }

Model実装

そしてこのUserModelでemailからユーザの有無判定。
(パスワードは使用しないので、消してしまって大丈夫です。)

<?php
     /**
      * 特定のEmailから管理ユーザを取得
      *
      * @param str $email
      * @return object
      * @access public
      */
     public function getAdminUserFromEmail(string $email)
     {
         return $this->where('email', $email)
                      ->first();
      }

Viewの実装

最後に、Viewを設定して、、、

<a href="/login/google/redirect" class="btn btn-google">Sign in using Google+</a>


ログインできれば、完了です!
な、なんて便利なんでしょう・・・!!!!涙