20190522のlaravelに関する記事は11件です。

【個人用】Laravel5.5 初開発メモ①

用途

  • 開発中に調べたことをメモしてた(雑です)
  • 限定公開に埋もれ出したので、公開する
  • Laravel 5.5
  • 困った時に見てる

設計

  • 外部キーにユニーク制約が必要でないか調べる

FormHelper

RadioButton

CheckBox

だいたいこんな感じで進むと思われる

1) make migration
2) make model
3) make factory
4) make seeder
5) make controller
6) make blade
7) make routing
8) make RequestForm
9) make logic
10) testing api & validation

Done!

実装・修正箇所の探し方

Controllerから探す

①Controller見る
②Routingを見る
③Blade見る

Viewから探す

①Routingから見る
②Controller探す
or
①Grepかける

使ったライブラリ

Composer

composerについて初期学習まとめ

dump-autoload

  • とりあえず、クラス等のロードが早くなるイメージ

Migrate

Laravel 5.5 データベース:マイグレーション

$ php artisan make:migration create_users_table

int、bigint、smallint、および tinyint (Transact-SQL)

  • TinyIntにunsignedは要らない
  • intは-2からなのでいる

Eloquent(Model)

  • Migrationファイルと一緒に生成することもできる。

Laravel 5.5 Eloquent:利用の開始

論理削除

$ php artisan make:model User

Seeder

Laravel 5.5 データベース:シーディング

  • SeederでFactory使うときは、呼び出す
$ php artisan make:seeder UsersTableSeeder

Factory

Laravel 5.5 データベースのテスト

$user = factory(User::class)->create(); # モデルの作成&保存
$user = factory(User::class)->make(); # モデルの作成
$ php artisan make:factory PostFactory

Faker

- 出たものは出さないようにする

'user_id' => $faker->unique()->numberBetween(1, 50),

Controller

Laravel 5.5 コントローラ

Routing

Laravel 5.5 ルーティング

Route::resource

Roure::resource('/email-template', 'EmailTemplateController');
  • ルートの確認
$ php artisan route:list

名前付きルート

バリデーション

Laravel 5.5 バリデーション

  • おそらく、sizeとmaxの違いは、文字数かKBの違い

フォームリクエストバリデーション

$ php artisan make:request StoreBlogPost
  • return=falseでrulesがからの場合、the action is unconciousになる

検索のバリデーション

- 検索のバリデーションも考慮する。

  • name属性がちゃんと指定されているか、確認する
name="title" #いける
title="title" #grepかけて、直すとミスる時がある。

PHPUnit

Generate

// Featureディレクトリにテストを生成する(Controller)
$ php artisan make:test StaffControllerTest

// Unitディレクトリにテストを生成する(Model)
$ php artisan make:test StaffTest --unit

Execute

$ ./vendor/bin/phpunit // 全体のテスト
$ ./vendor/bin/phpunit tests/Feature // Featureのテスト
$ ./vendor/bin/phpunit tests/Unit // Unitのテスト

Point of view

Feature Test(Controller)
- Testing API

Unit Test(Model etc)
- Testing Validation

Tips

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
  • Docker内だと、セッション開始がテスト時に実行されないケースがあるので、明示的に開始する
    use DatabaseMigrations;
    use WithFaker;

    protected function setUp()
    {
        parent::setUp();
        Session::start();
    }

FormHelper

Form::open
  {{ Form::open(['route' => array('email-template.update', $email_template->id), 'method' => 'post']) }}
    {{ method_field('PUT') }}
    @include('email_template.partials.form')

Mailer

MailtrapでLaravelの簡単メール送信テスト

https://mailtrap.io

  • 設定の変更
$ cp .env.example .env
  • 再読み込み
$ php artisan config:cache
  • generate
$ php artisan make:mail UserPasswordResetMail
  • Error
Class 'App\Http\Controllers\Mail' not found

Laravel 5.5 メール

  • HTMLの命名規則はケバブケース

=> password-reset.blade.php

namespaceについて

form request validation

フォームのバリデーション設定

パスワードリセット機能を作る

laravel str_randomは安全ですか? - 文字列、laravel、URL、ランダム
CSRF の安全なトークンの作成方法
[Laravel]メール認証を使った会員登録

- メールで初期パスワードを平文で送るの大丈夫か??

ルーティング

Laravel5.5でのルーティング方法一覧

Viewに値を渡す

[laravel] controllerからviewへの変数の受け渡しとその展開方法

RequestFormによるバリデーション

Laravel 5.5 バリデーション

ORM

Laravel 5.5 Eloquent:利用の開始

  • where(※一件だけ取得する)
$user = User::where('email', $request->email)->first();

HTTPレスポンス

Laravel 5.5 HTTPレスポンス

フラッシュメッセージ

trans

Laravel 5.5 ヘルパ

日付処理

Laravelで日付関連の処理を行うCarbonを使ってみる

複数の変数を引数で渡す

php – Laravel – 複数の変数を渡して表示する
- 取り出し方

$user->name = $data['user_info']['name'];

405: ルーティング定義漏れ
419: CSRF対策漏れ

ハッシュ化と暗号化

Laravel 5.6 ハッシュ
Laravel 5.5 暗号化

コメント

@section('script')
  <script>
      (function ($) {
          /* ---------------------------------------------------
          // character count
          -----------------------------------------------------*/
          (function () {
              $('textarea').on('keyup', function() {
                  const len = $(this).val().length;
                  if (len > 1000) {
                      $(this).val($(this).val().substring(0, 999));
                  } else {
                      $(this).next('span').text(len + '/1000文字');
                  }
              })
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで投稿アプリの機能を作成 ~リレーション編~

はじめに

前回、「【30分クッキング】Laravelで投稿アプリの機能を作成 ~CRUD編~」というタイトルで記事を書かせていただきましたが、その続きの記事になります。
今回は、Auth認証できるusersテーブルと前回作ったpostsテーブルをリレーション(関連付け)させてみたいと思います。

前提

前回のデータをベースに修正していきます
なので、前回の記事から実装をお勧めします
完全コピペすれば30分ほどで実装できるはずです
https://qiita.com/ProgramingDai/items/cf6944f9cd0ac08f4e3e

テーブルイメージ

relation.jpg

外部キーカラムの命名規則
(主テーブル名の単数形) _ id
※今回のケースではusersテーブルが主テーブルなので「user_id」が外部キーとなります

[用語説明]リレーション【relation】とは
かかわりがあること・つながりがあること・関係・関連

リレーションの一例

・誰が投稿したのかを区別して表示できる
・記事を投稿した人だけがその記事を編集したり削除したり、またそのユーザーだけ表示することもできる
など

手順レシピ

1.ログイン機能実装
Laravelでは以下のコマンドだけで簡単にAuth機能を実装できます
なんと便利なこと!!!
$ php artisan make:auth

そうするとwelcome.blade.phpの右上に「LOGIN」と「REGISTER」リンクが表示されるので
「REGISTER」からアカウント登録をおこなってください
そのアカウントで「LOGIN」ページからログインできます

(注)前回の記事で実装した方ならすでにマイグレートでusersテーブルができているかと思いますが、
まだマイグレートしてない方は以下コマンドでマイグレートお願いします
$ php artisan migrate

2.ルーティング設定

/routes/web.php
// RESTfulサービスのルーティング
Route::resource('/post', 'PostController')->middleware('auth');
// ->middleware('auth')を追加

こうすることで、このページにアクセスする際は登録したメールアドレス・パスワードが必要になります

3.モデル内修正

/app/Post.php
// belongsTo結合(主テーブル <- 従テーブル)
    public function user() {
        return $this->belongsTo('App\User');
    }

postsテーブルは従テーブルなので「belongsTo」を設定します

/app/User.php
// hasMany結合(主テーブル -> 従テーブル)
    public function posts() {
        return $this->hasMany('App\Post');
    }
// 今回はこちらは使いませんが一応設定しておきます

usersテーブルは主テーブルなので「hasMany」(1対多結合) or 「hasOne」(1対1結合)を設定します
共に引数には互いのモデル名を入れます

4.コントローラー設定

/app/Http/PostController.php
// 追加 これがないとエラーになります
use Illuminate\Support\Facades\Auth;

// 中略
public function index()
    {   
        $user = Auth::user(); // 認証ユーザー取得
        $items = Post::with('user')->get();
        // 「Post::all();」ではN+1問題になり、SQLの発行数が増える

        $params = [
            'user' => $user,
            'items' => $items,
        ];
        return view('post.index', $params); // ビューの描画
        // return $items->toArray(); // JSONデータで描画
    }
// 中略
public function show($id)
    {   
        $user = Auth::user(); // 認証ユーザー取得
        $item = Post::find($id);
        $params = [
            'user' => $user,
            'item' => $item,
        ];
        return view('post.show', $params);
    }
// 中略

use追加、indexとshowメソッドのみ修正
cf. N+1問題について
https://qiita.com/B106827/items/2a33fcf288eda7769e41

5.ビューでの切り分け

/resource/post/index.blade.php
<!-- 中略 -->

<!-- 前回仮入れした箇所: <input type="hidden" name="user_id" value="1"> -->
<!-- これで認証ユーザーでの記事投稿が可能になる -->
<input type="hidden" name="user_id" value="{{ $user->id }}">

<!-- 中略 -->

<!-- 記事描画部分 -->
    @if(count($items) > 0)
        @foreach($items as $item)
            <div class="alert alert-primary" role="alert">
                @if($user->id === $item->user_id)
                <!-- 主キーと外部キーが同じ場合 -> リンク付きテキストと削除ボタン表示 -->
                <a href="/post/{{ $item->id }}" class="alert-link">{{ $item->title }}</a>
                <div>{{ $item->message }}</div>
                <form action="/post/{{ $item->id }}" method="POST">
                {{ csrf_field() }}
                <input type="hidden" name="_method" value="DELETE">
                <input type="submit" class="delete" value="削除">
                </form>
                @else
                <!-- 主キーと外部キーが違う場合 -> 通常のテキストのみ表示 -->
                <span class="alert-link">{{ $item->title }}</span>
                <div>{{ $item->message }}</div>
                @endif
                <!-- この部分はいろいろ出し方を変えられますのでおのおのでカスタマイズしてみてください -->
            </div>
        @endforeach
    @else
        <div>投稿記事がありません</div>
    @endif

<!-- 中略 -->
/resource/post/show.blade.php
<!-- index.blade.php同様、value差し替え -->
<input type="hidden" name="user_id" value="{{ $user->id }}">

cf. RESTfulのルーティング確認方法
php artisan route:list

あとがき

これで2つのテーブルのリレーションはできたかと思います。
まだログイン画面やログアウト先のルーティングなどはデフォルトのままなので、
次はオリジナルのログイン画面の実装などをしていきたいと思います。
※そちらは別ページでのアップ予定です。

Laravelで投稿アプリの機能を作成
~CRUD編~
https://qiita.com/ProgramingDai/items/cf6944f9cd0ac08f4e3e
~リレーション編~
https://qiita.com/ProgramingDai/items/249acc8894079ee58268
~ログインカスタマイズ編~
https://qiita.com/ProgramingDai/items/fee669e5a8cf67f0e38e

参考書籍: PHPフレームワーク Laravel入門
https://blog.hiroyuki90.com/articles/laravel-books/
N+1問題について: https://qiita.com/B106827/items/2a33fcf288eda7769e41

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

vagrant box add した際のエラー解決(OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 54)

Homesteadで仮想マシンを使用していたが、VirtualBoxのバージョンアップをしたらホストOSとゲストOSのバージョンが違うということで使用できなくなった。

vagrant-vbguestでうまくできると思ったが、ゲストOSのビルド?が必要だったり?と難しそうだったので今回はマシンを作り直すことにしました。

仮想マシンを作り直そうとHomesteadのboxファイルを削除して、また入れ直そうとした時に以下のエラーが発生

$ vagrant box add laravel/homestead
==> box: Loading metadata for box 'laravel/homestead'
    box: URL: https://vagrantcloud.com/laravel/homestead
This box can work with multiple providers! The providers that it
can work with are listed below. Please review the list and choose
the provider you will be working with.

1) hyperv
2) parallels
3) virtualbox
4) vmware_desktop

Enter your choice: 3
==> box: Adding box 'laravel/homestead' (v7.2.1) for provider: virtualbox
    box: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/7.2.1/providers/virtualbox.box
    box: Download redirected to host: vagrantcloud-files-production.s3.amazonaws.com

An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.

OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 54

ダウンロード関係のファイル等が格納されている~/.vagrant.d/tmp/を確認


$ ls ~/.vagrant.d/tmp/
boxd96a327de9d4e23d1ce1a91384ae3975899cf47e boxf045ffba7f5588c8bf5a474bb05e6eab94df86ac

ダウンロードに失敗した後にtmpフォルダにごみファイルが残ってしまうようなので、この中のファイルを削除します。

$ rm ~/.vagrant.d/tmp/*

再度boxをインストール

$ vagrant box add laravel/homestead
==> box: Loading metadata for box 'laravel/homestead'
    box: URL: https://vagrantcloud.com/laravel/homestead
This box can work with multiple providers! The providers that it
can work with are listed below. Please review the list and choose
the provider you will be working with.

1) hyperv
2) parallels
3) virtualbox
4) vmware_desktop

Enter your choice: 3
==> box: Adding box 'laravel/homestead' (v7.2.1) for provider: virtualbox
    box: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/7.2.1/providers/virtualbox.box
    box: Download redirected to host: vagrantcloud-files-production.s3.amazonaws.com
==> box: Successfully added box 'laravel/homestead' (v7.2.1) for 'virtualbox'!

無事入りました!

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

Lalavelでのログイン機能実装にあたって罠にはまった

背景

Laravel5.8でコマンド「php artisan make:Auth」でログイン機能やら登録画面やら一撃実装できることを知って、
感動してたときのことです。

やっぱり自分流にカスタマイズしたい

そう思うでしょう?

登録画面をカスタマイズ、DBにテーブルも準備してユーザ登録されることも確認!いざ!ログイン!

だがしかし、IDとパスワードを入れてログインボタンを押すと、永遠にログイン画面にリダイレクトされる。。。

今回の問題点

僕は数行前でIDとパスワードを入れてログインボタンを押すといった。

そう、IDとパスワード。

そう、IDこれだ。

改善

コマンドで一撃実装をすると、ログイン画面で入力するフォームは
・e-mail
・password
となっている。

このe-mailのフィールドをblade上でidに書き換えるだけではだめだったのである。

ここで公式のドキュメントを見てみる。

laravelのドキュメント

[ユーザー名のカスタマイズ]の項目を見ると、どうやらLoginController内にてusernameメソッドを定義する必要があったようだ。

なので、
image.png

こんな感じで定義してやると、ログインできる。

もちろん、bladeのフィールド名はちゃんと書き換えてくれ。

公式のドキュメント見たら済むお話をわざわざ記事にしてしまった自分が情けない。。。。

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

Laravelでヘルパー乱用をしてグローバル汚染するのを避ける提案

Laravelでviewのロジックを切り出したいときにhelperは便利

以下のように定義してしまえば、どこからでも呼び出せる
viewロジックをまとめられた!!

となるが、どこからでも呼び出せてしまうという問題も同時におきている。

if (!function_exists('normalizeAge')) {
  /**
   * 年齢から 10代前半 の表記に丸めた文字列を返す
   * @param int $age 年齢
   * @return string 10代前半など年代を丸めた文字列
  */
  function normalizeAge($age) {
    if (empty($age)) {
      return '年齢 未回答';
    }
    return getTensPlace($age) . '代' . (isEarlyAge($age) ? '前半' : '後半');
  }
}

そこでviewテンプレートのBladeの構文解析器を拡張する(directive追加)

Controller側でこれを定義しておくことで、そのControllerを用いた時だけ使える
viewテンプレートで利用可能な構文が追加される。

Blade::directive('normalizeAge', function ($age) {
  if (empty($age)) {
    return "<?php echo 年齢、未回答; ?>";
  }
  return "<?php echo getTensPlace($age) . '代' . (isEarlyAge($age) ? '前半' : '後半') ?>";
});

使い方

bladeテンプレートファイルで @if@foreach のように記述するだけ

@normalizeAge(14)

このようにすることでグローバル汚染を防ぎつつ、viewで使えるロジックを定義できるのではないかと思った。
いや、普通に違う方法あるでしょというベストアンサーお待ちしてます。

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

AWS CodeBuildのコンテナから、RDSへ直接マイグレーションを実行する方法

概要

サービスを全部ECS + Fargateで行うサービスで、簡単かつCoolにマイグレーション実行する方法を記します。
具体的にはCodePipeline & CodeBuildを設定し、ビルド中にマイグレーションが走るように設定します。

この記事が役に立ちそうな人

  • ECS + Fargateでサービスを構築しようとしている人
  • サービスにはRDSを用いているが、マイグレーションをどこから行おうか悩んでいる人

背景

マイグレーションをいかにCoolに行うかはサーバーサイドの至上命題と言っても過言ではありません。(?)

前述の通りECS + Fargateでサービスを構築すると、FargateにはSSHできないのは周知の事実です。
よってコンテナに入れないため、フレームワークの機能を用いたマイグレーションを実行できません。
そして、マイグレーションは必ずソースコードのデプロイ前に実行される必要があります。

以上のことから、ソースコードのデプロイを行うCodePipelineのフローでマイグレーションを行うフェーズを追加し、マイグレーション含めたアプリケーションのデプロイを管理できたらCoolだなと思った次第です。

こちらの方法で実はその例が示されているのですが、タスク定義をjsonで書く必要があり、ステージング環境などの環境ごと対応が手間のように見受けられました。

もっと簡単でセキュアな方法はないかと考えていたところ、CodeBuildのドキュメントにCodeBuildが特定VPC, サブネットで実行できると書いていました。

RDSにはプライベートサブネットからしかアクセスできないようにしていましたが、これならCodeBuildインスタンスに立ち上げたコンテナから、マイグレーションを直接実行できるのでは? と思いついたわけです。

実際に

プライベートサブネット上に分離された Amazon RDS データベース内のデータに対して、ビルドから統合テストを実行する。

とあるように、RDSへのアクセスが想定されているようです。

目的

  • CodePipelineのフェーズ中にmigrationフェーズを追加し、ソースコードのデプロイに加えてマイグレーションも制御したい
  • ECSタスク定義を書きたくない
  • もちろんセキュリティは維持する
  • フレームワークのマイグレーション機能を使いたい 1

インフラ構成の説明

CBMirationDesc.png

これが全貌ではありませんが、プライベートサブネットについてはこの構成です。AZの冗長化は端折っています。
ECSのセキュリティグループとは別のSGを用意し、CodeBuildにはそれを設定しておきます。

セキュリティグループ3にはセキュリティグループ1のインバウンドトラフィックを許可しておきます。
セキュリティグループ1には特に設定は不要です。

具体的な設定

CodeBuild

vpcsettings.png

上記画像のようなVPCに関する設定があります。
ここにインフラ構成で記した通りの設定をしておきます。

あと環境変数AWS_ENVIRONMENTを設定しておきます。
これでどの環境のデータベースへ接続するかを、laravelのenvファイルの読み込みを切り替えることで選択できるようになります。
つまり後述のbuildspecは全環境で共通利用できます。

加えて使用するbuildspecは後述のbuildspec-migration.ymlを指定してください。

buildspec

実例を示します。

buildspec-migration.yml
version: 0.2
phases:
  install:
    runtime-versions:
      docker: 18
    commands:
      - echo Install started on `date`
      - cat /etc/issue
      - curl -L "https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
      - docker-compose --version
      - docker -v
      - pwd && ls -l
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - sed -e 's/AWS_ENVIRONMENT/$AWS_ENVIRONMENT/g' docker-compose-migration.yml > docker-compose.yml
      - docker-compose build
      - docker-compose up -d
      - docker ps
      - docker exec laravel-api bash /project/build.sh
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Start migrate
      - docker exec laravel-api php project/artisan migrate

私はdocker-composeを用いてコンテナを立ち上げています。2

sed -e 's/AWS_ENVIRONMENT/$AWS_ENVIRONMENT/g' docker-compose-migration.yml
これで環境ごとにdocker-compose.ymlを生成します。これでこのymlは全環境で使いまわせます。

docker exec laravel-api bash /project/build.sh
ここではcomposer installを行うだけのシェルを実行しています。でないとmigrateが動かないので。

docker exec laravel-api php project/artisan migrate
ここでついにmigrationを実行しています。

docker-compose

私はdocker-composeを用いているので、その設定も例を示します。

docker-compose-migration.yml
version: '3.3'

services:
  # web - api
  api:
    build: ./alpine-apache-php7
    container_name: laravel-api
    environment:
      - APP_ENV=AWS_ENVIRONMENT
    volumes:
      - "./project:/project"
    ports:
      - "18080:80"

APP_ENVには設定すればdevelopmentなどの任意のテキストを入れられるので、読み込む環境設定ファイルの制御ができます。

CodePipeline

パイプラインの例を示します。

cp.png

上記のようにECRへのビルドフェーズと、マイグレーション含むデプロイフェーズは分けています。
こうすることでマイグレーションとデプロイが自動的に実行されないようになります。

参考文献

LaravelをElastic BeanstalkからFargateに移行しました
https://qiita.com/hareku/items/73f7730c1adc01bbe5a0

AWS公式CodeBuildのVPCサポートについてのドキュメント
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/vpc-support.html


  1. フレームワークはLaravelを使っています。多分他のフレームワーク、言語でも同じことができるはずです。 

  2. どこかのタイミングからCodeBuildでdocker-composeを動かすとエラーを吐くようになりました。どうやら標準イメージを使うようになってから追加の設定が必要になったようです。このように別のソフトをインストールするときは、CodeBuildの環境設定においてEnable this flag if you want to build Docker images or want your builds to get elevated privileges.にチェックを入れないとエラーとなります。注意してください。 

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

(Laravel5.8)ポリモーフィックを使った時の保存方法で詰んだのでメモしておく

はじめに

マルチログインを実装した画像投稿アプリを作っている中で、ポリモーフィックリレーションについての記事があまりなくて悩んだので、忘備録として残しておきます。内容はモデルのリレーションの話と、実装方法のみ。Laravelのインストール〜モデルの作成、マルチログイン実装などは参考になる記事がたくさんあったのでそちらをご参照ください。

今回のユースケースの解説

マルチログインを実装した時に、usersテーブルとgroupsテーブルを使ってそれぞれ別のログインページからログインさせて、middlewareを使ってログイン後の画面も別々で管理していました。group側からは写真の投稿ができたりマイページで自分の投稿を一覧で見たりする機能がありますが、user側からは投稿の閲覧とコメントしかできないように設計しています。

今回ポリモーフィックを使った箇所はコメント機能の実装です。コメントはuser側からもgroup側からも可能で、誰がコメントしたか分かるようにします。この時コメント(comment)はuserもしくはgroupどちらかに所属することになります。これがポリモーフィック関係です。

テーブル構造

users //親その1
    id - integer
    name - string
    nickname - string
    email - string
    ///省略

groups //親その2
    id - integer
    name - string
    email - string
    establish - date
    ///省略

comments  //userかgroupいずれかにひも付く
    id - integer
   post_id - unsignedInteger   //外部キーでpostにひも付く
    body - text
    commentable_id - integer    //userかgroupのidが入る
    commentable_type - string   //モデル名:App\UserかApp\Groupが入る

commentsテーブルだけ書き方が特殊です。構造見れば分かりますが、commentsテーブルにuser_idgroup_idを外部キーとして持たせておけば、別にポリモーフィックを使わずともそれぞれ一対多のリレーションで処理できるのです。
ということでポリモーフィックは実際使う人が少なくて記事がなかったんだろうなと思っています。(私はせっかく機能としてあるなら使いたい!ということで使いました)

それと、ポリモーフィック自体がアンチパターンでもあるという記事も見かけましたので置いておきますね。

SQLアンチパターンを読んで (ポリモーフィック関連について)

実際に書いていく

前提はここまでにして、いざ書いていきます。まずは、各モデルにリレーションの定義をしていきます。

各モデルのリレーション定義

User.php
class User extends Authenticatable
{
    /** 
     * Commentに対してmorphMany
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}
Group.php
class Group extends Authenticatable
{
    /** 
     * Userと同じ
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

Comment.php
class Comment extends Model
{
    /**
     * 忘れずに$fillableを定義する
     */
    protected $fillable = [
        'post_id',
        'body',
        'commentable_id',
        'commentable_type'
    ];

    /**
     * コメントはPost(投稿)にひも付く(今回は解説しません)
     */
    public function post()
    {
        return $this->belongsTo('App\post');
    }
    /**
     * 所有しているcommentableモデルの全取得
     */
    public function commentable()
    {
        return $this->morphTo();
    }
}

で、ここまではだいたい調べれば書いてあるので、大丈夫だと思います。
次は、リレーションが定義できているかを調べるため、まずはtinkerでダミーのデータを登録しておきます。

tinkerでダミーデータの登録

php artisan tinker
>>> $com = new App\Comment
=> App\Comment {#2972}
>>> $com->post_id = 1
=> 1
>>> $com->body = 'testtesttest'
=> "testtesttest"
>>> $com-> commentable_id = 1
=> 1
>>> $com->commentable_type = 'App\Group'
=> "App\Group"
>>> $com->save();
=> true
>>> exit

mysql> select * from comments;
+----+---------+--------------+----------------+------------------+---------------------+---------------------+
| id | post_id | body         | commentable_id | commentable_type | created_at          | updated_at          |
+----+---------+--------------+----------------+------------------+---------------------+---------------------+
|  1 |       1 | testtesttest |              1 | App\Group        | 2019-05-21 05:51:03 | 2019-05-21 05:51:03 |
+----+---------+--------------+----------------+------------------+---------------------+---------------------+
1 row in set (0.00 sec)

ちゃんと登録されました。

投稿者に関連するコメントを取り出す

先ほど登録した情報を取り出します。コメントの投稿者はGroupモデルのid=1としたので、Groupモデルからコメントのデータを取って来られれば成功です。

php artisan tinker
>>> $comment = App\Group::find(1)->comments->all();

[
     App\Comment {#2988
       id: 1,
       post_id: 1,
       body: "testtesttest",
       commentable_id: 1,
       commentable_type: "App\Group",
       created_at: "2019-05-21 05:51:03",
       updated_at: "2019-05-21 05:51:03",
     },
]

//ちなみに逆の時、つまりcommentからuserもしくはgroupを取得する時はこんな感じ

>>> $auth = App\Comment::find(1)->commentable->all();
=> App\Group {#2989
     id: 1,
     name: "グループname",
     cover_img: "sample.jpeg",
     icon_img: "sample.jpeg",
     //・・・省略
  }

よしっ!!!ページに実装だ!

・・・と思ってview側からコメントの表示はできたのですが、保存は???となりました。

コメントの保存

結論から言うと、めちゃくちゃ簡単でした。この辺が調べた時見たやつです。

https://laracasts.com/discuss/channels/eloquent/save-polymorphic-table?page=1

https://qiita.com/naga3/items/e7612d868857228c0bad

commentsテーブルのcommentable_idcommentable_typeは定義せずともいい感じにやって頂けるようで。相変わらずかしこい。

CommentController.php
    public function store(Request $request)
    {
        /**ログインユーザーの情報取得*/
        $auth = Auth::user();

        /**ログインユーザーにひも付けてコメントを保存*/
        $auth->comments()->create(
            [
            'post_id' => $request->post_id,
            'body' => $request->body
            ]
        );
        //これで保存完了

        /**リダイレクト先の振り分け*/
        if (get_class($auth) == "App\Group") {
            return redirect('/group/posts');
        } elseif (get_class($auth) == "App\User") {
            return redirect('/');
        }
    }

これでOKです!最後に一応、groupのview側からのコメント投稿箇所のコードも一部載せておきます。ルートの定義は、こんな感じでいけるはず。

web.php
    Route::get('group/comments', 'CommentController@index')
    Route::post('group/comments', 'CommentController@store');
index.blade.php
   @forelse ($post->comments as $comment)
     <p>コメント</p>
     <pre>{{ $comment->body }}</pre>
   @empty
     <p>コメントがまだありません</p>
   @endforelse
     <form method="post" action="{{ url('group/comments') }}">
     @csrf
       <p>コメント<br><textarea name="body" cols="40" rows="3"></textarea></p>
       <input type="hidden" name="post_id" value="{{ $post->id }}">
       <button type="submit">コメント投稿</button>
     </form>

不備や、もっとこうしたほうがいい!等ご意見があればぜひコメントいただければと思います。

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

【Laravel】コレクションのgroupByメソッド 練習問題3問(初心者向け)

はじめに

Laravelのコレクションの便利なメソッドgroupBy

あれこれ触ったので、覚えたことを忘れないよう、練習問題にしてみました。

問題

Order, OrderDetail, Book, Publisherモデルがあり、OrderDetailからリレーションのあるBook, Publisherまでを以下のように取得しました。

$order_details = OrderDetail::with('book.publisher')->get();

print_r(json_encode($order_details, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT))
$order_detailsをJSON化したもの
[
    {
        "id": 10001,
        "order_id": 1,
        "book_id": 111,
        "selling_price": 3000,
        "book": {
            "id": 111,
            "publisher_id": 11,
            "name": "自習Laravel",
            "list_price": 3500,
            "is_electronic_book": false,
            "publisher": {
                "id": 11,
                "name": "AA出版"
            }
        }
    },
    {
        "id": 10002,
        "order_id": 1,
        "book_id": 222,
        "selling_price": 2500,
        "book": {
            "id": 222,
            "publisher_id": 22,
            "name": "基礎から学ぶPHP",
            "list_price": 3000,
            "is_electronic_book": true,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    },
    {
        "id": 10003,
        "order_id": 1,
        "book_id": 333,
        "selling_price": 3000,
        "book": {
            "id": 333,
            "publisher_id": 22,
            "name": "パーフェクトLaravel",
            "list_price": 3000,
            "is_electronic_book": false,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    }
]

問1

publisher_idgroupByしてください。

問2

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)

{
    "11": [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    "22": [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

これを0から振り直してください。(JSON化した場合、以下のように出力されるようにしてください)

[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

問3

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

補足

必要に応じてLaravel公式ドキュメントのgroupByほかを参照してください。
https://readouble.com/laravel/5.8/ja/collections.html#method-groupby

ここから先、解答になります。

...

...

...

解答

問1の解答

publisher_idgroupByしてください。

publisher_idは、bookをキーとする値の中に、さらにキーとして存在していました。

$order_detailsをJSON化したものの抜粋
        "book": {
            "id": 111,
            "publisher_id": 11,

こういった場合は、.で繋いで指定すればOKです。

問1の解答例
$answer1 = $order_details->groupBy('book.publisher_id');
問1の別解
$answer1 = $order_details->groupBy('book.publisher.id');

問2の解答

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)
これを0から振り直してください。

キーの振り直しにはコレクションのメソッドであるvaluesが使えます。

valuesメソッドはキーをリセット後、連続した整数にした新しいコレクションを返します。

Laravel5.8 公式ドキュメント - values
https://readouble.com/laravel/5.8/ja/collections.html#method-values

問2の解答例
$answer2 = $order_details->groupBy('book.publisher_id')
    ->values();
問2の解答例の結果をJSON化したもの
[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
            "selling_price": 2500,
            "book": {
                "id": 222,
                "publisher_id": 22,
                "name": "基礎から学ぶPHP",
                "list_price": 3000,
                "is_electronic_book": true,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        },
        {
            "id": 10003,
            "order_id": 1,
            "book_id": 333,
            "selling_price": 3000,
            "book": {
                "id": 333,
                "publisher_id": 22,
                "name": "パーフェクトLaravel",
                "list_price": 3000,
                "is_electronic_book": false,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        }
    ]
]

問3の解答

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

こういった場合は、同じくコレクションのメソッドであるtransformの利用が考えられます。

transformメソッドはコレクションを繰り返し処理し、コレクションの各アイテムに指定したコールバックを適用します。コレクション中のアイテムはコールバックから返される値に置き換わります。

Laravel5.8 公式ドキュメント - transform
https://readouble.com/laravel/5.8/ja/collections.html#method-transform

is_electronic_bookgroupByした結果、2つの要素に分かれていますので、各々の要素に対してさらにpublisher_idgroupByします。

問3の解答例
$answer3 = $order_details->groupBy('book.is_electronic_book')
    ->transform(function ($order_detail) {
        return $order_detail->groupBy('book.publisher_id')
            ->values();
        });
問3の解答例の結果をJSON化したもの
[
    [
        [
            {
                "id": 10001,
                "order_id": 1,
                "book_id": 111,
                "selling_price": 3000,
                "book": {
                    "id": 111,
                    "publisher_id": 11,
                    "name": "自習Laravel",
                    "list_price": 3500,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 11,
                        "name": "AA出版"
                    }
                }
            }
        ],
        [
            {
                "id": 10003,
                "order_id": 1,
                "book_id": 333,
                "selling_price": 3000,
                "book": {
                    "id": 333,
                    "publisher_id": 22,
                    "name": "パーフェクトLaravel",
                    "list_price": 3000,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ],
    [
        [
            {
                "id": 10002,
                "order_id": 1,
                "book_id": 222,
                "selling_price": 2500,
                "book": {
                    "id": 222,
                    "publisher_id": 22,
                    "name": "基礎から学ぶPHP",
                    "list_price": 3000,
                    "is_electronic_book": true,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ]
]

最後に

以上です。何問解けたでしょうか?

コレクションメソッドを使っていて、また何か気付きがあれば練習問題にしてみたいと思います:innocent:

参考

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

Laravelコレクションメソッド groupBy練習問題3問(初心者向け)

はじめに

Laravelのコレクションの便利なメソッドgroupBy

あれこれ触ったので、覚えたことを忘れないよう、練習問題にしてみました。

問題

Order, OrderDetail, Book, Publisherモデルがあり、OrderDetailからリレーションのあるBook, Publisherまでを以下のように取得しました。

$order_details = OrderDetail::with('book.publisher')->get();

print_r(json_encode($order_details, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT))
$order_detailsをJSON化したもの
[
    {
        "id": 10001,
        "order_id": 1,
        "book_id": 111,
        "selling_price": 3000,
        "book": {
            "id": 111,
            "publisher_id": 11,
            "name": "自習Laravel",
            "list_price": 3500,
            "is_electronic_book": false,
            "publisher": {
                "id": 11,
                "name": "AA出版"
            }
        }
    },
    {
        "id": 10002,
        "order_id": 1,
        "book_id": 222,
        "selling_price": 2500,
        "book": {
            "id": 222,
            "publisher_id": 22,
            "name": "基礎から学ぶPHP",
            "list_price": 3000,
            "is_electronic_book": true,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    },
    {
        "id": 10003,
        "order_id": 1,
        "book_id": 333,
        "selling_price": 3000,
        "book": {
            "id": 333,
            "publisher_id": 22,
            "name": "パーフェクトLaravel",
            "list_price": 3000,
            "is_electronic_book": false,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    }
]

問1

publisher_idgroupByしてください。

問2

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)

{
    "11": [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    "22": [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

これを0から振り直してください。(JSON化した場合、以下のように出力されるようにしてください)

[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

問3

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

補足

必要に応じてLaravel公式ドキュメントのgroupByほかを参照してください。
https://readouble.com/laravel/5.8/ja/collections.html#method-groupby

ここから先、解答になります。

...

...

...

解答

問1の解答

publisher_idgroupByしてください。

publisher_idは、bookをキーとする値の中に、さらにキーとして存在していました。

$order_detailsをJSON化したものの抜粋
        "book": {
            "id": 111,
            "publisher_id": 11,

こういった場合は、.で繋いで指定すればOKです。

問1の解答例
$answer1 = $order_details->groupBy('book.publisher_id');
問1の別解
$answer1 = $order_details->groupBy('book.publisher.id');

問2の解答

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)
これを0から振り直してください。

キーの振り直しにはコレクションのメソッドであるvaluesが使えます。

valuesメソッドはキーをリセット後、連続した整数にした新しいコレクションを返します。

Laravel5.8 公式ドキュメント - values
https://readouble.com/laravel/5.8/ja/collections.html#method-values

問2の解答例
$answer2 = $order_details->groupBy('book.publisher_id')
    ->values();
問2の解答例の結果をJSON化したもの
[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
            "selling_price": 2500,
            "book": {
                "id": 222,
                "publisher_id": 22,
                "name": "基礎から学ぶPHP",
                "list_price": 3000,
                "is_electronic_book": true,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        },
        {
            "id": 10003,
            "order_id": 1,
            "book_id": 333,
            "selling_price": 3000,
            "book": {
                "id": 333,
                "publisher_id": 22,
                "name": "パーフェクトLaravel",
                "list_price": 3000,
                "is_electronic_book": false,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        }
    ]
]

問3の解答

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

こういった場合は、同じくコレクションのメソッドであるtransformの利用が考えられます。

transformメソッドはコレクションを繰り返し処理し、コレクションの各アイテムに指定したコールバックを適用します。コレクション中のアイテムはコールバックから返される値に置き換わります。

Laravel5.8 公式ドキュメント - transform
https://readouble.com/laravel/5.8/ja/collections.html#method-transform

is_electronic_bookgroupByした結果、2つの要素に分かれていますので、各々の要素に対してさらにpublisher_idgroupByします。

問3の解答例
$answer3 = $order_details->groupBy('book.is_electronic_book')
    ->transform(function ($order_detail) {
        return $order_detail->groupBy('book.publisher_id')
            ->values();
        });
問3の解答例の結果をJSON化したもの
[
    [
        [
            {
                "id": 10001,
                "order_id": 1,
                "book_id": 111,
                "selling_price": 3000,
                "book": {
                    "id": 111,
                    "publisher_id": 11,
                    "name": "自習Laravel",
                    "list_price": 3500,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 11,
                        "name": "AA出版"
                    }
                }
            }
        ],
        [
            {
                "id": 10003,
                "order_id": 1,
                "book_id": 333,
                "selling_price": 3000,
                "book": {
                    "id": 333,
                    "publisher_id": 22,
                    "name": "パーフェクトLaravel",
                    "list_price": 3000,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ],
    [
        [
            {
                "id": 10002,
                "order_id": 1,
                "book_id": 222,
                "selling_price": 2500,
                "book": {
                    "id": 222,
                    "publisher_id": 22,
                    "name": "基礎から学ぶPHP",
                    "list_price": 3000,
                    "is_electronic_book": true,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ]
]

最後に

以上です。何問解けたでしょうか?

コレクションメソッドを使っていて、また何か気付きがあれば練習問題にしてみたいと思います:innocent:

参考

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

Laravelコレクションメソッド groupByを重ねて複数使う (練習問題形式)

はじめに

Laravelのコレクションの便利なメソッドgroupBy

あれこれ触ったので、覚えたことを忘れないよう、練習問題にしてみました。

タイトルの、groupByを重ねて複数使う、最後の問3です。

問題

Order, OrderDetail, Book, Publisherモデルがあり、OrderDetailからリレーションのあるBook, Publisherまでを以下のように取得しました。

$order_details = OrderDetail::with('book.publisher')->get();

print_r(json_encode($order_details, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT))
$order_detailsをJSON化したもの
[
    {
        "id": 10001,
        "order_id": 1,
        "book_id": 111,
        "selling_price": 3000,
        "book": {
            "id": 111,
            "publisher_id": 11,
            "name": "自習Laravel",
            "list_price": 3500,
            "is_electronic_book": false,
            "publisher": {
                "id": 11,
                "name": "AA出版"
            }
        }
    },
    {
        "id": 10002,
        "order_id": 1,
        "book_id": 222,
        "selling_price": 2500,
        "book": {
            "id": 222,
            "publisher_id": 22,
            "name": "基礎から学ぶPHP",
            "list_price": 3000,
            "is_electronic_book": true,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    },
    {
        "id": 10003,
        "order_id": 1,
        "book_id": 333,
        "selling_price": 3000,
        "book": {
            "id": 333,
            "publisher_id": 22,
            "name": "パーフェクトLaravel",
            "list_price": 3000,
            "is_electronic_book": false,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    }
]

問1

publisher_idgroupByしてください。

問2

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)

{
    "11": [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    "22": [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

これを0から振り直してください。(JSON化した場合、以下のように出力されるようにしてください)

[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

問3

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

補足

必要に応じてLaravel公式ドキュメントのgroupByほかを参照してください。
https://readouble.com/laravel/5.8/ja/collections.html#method-groupby

ここから先、解答になります。

...

...

...

解答

問1の解答

publisher_idgroupByしてください。

publisher_idは、bookをキーとする値の中に、さらにキーとして存在していました。

$order_detailsをJSON化したものの抜粋
        "book": {
            "id": 111,
            "publisher_id": 11,

こういった場合は、.で繋いで指定すればOKです。

問1の解答例
$answer1 = $order_details->groupBy('book.publisher_id');
問1の別解
$answer1 = $order_details->groupBy('book.publisher.id');

問2の解答

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)
これを0から振り直してください。

キーの振り直しにはコレクションのメソッドであるvaluesが使えます。

valuesメソッドはキーをリセット後、連続した整数にした新しいコレクションを返します。

Laravel5.8 公式ドキュメント - values
https://readouble.com/laravel/5.8/ja/collections.html#method-values

問2の解答例
$answer2 = $order_details->groupBy('book.publisher_id')
    ->values();
問2の解答例の結果をJSON化したもの
[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
            "selling_price": 2500,
            "book": {
                "id": 222,
                "publisher_id": 22,
                "name": "基礎から学ぶPHP",
                "list_price": 3000,
                "is_electronic_book": true,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        },
        {
            "id": 10003,
            "order_id": 1,
            "book_id": 333,
            "selling_price": 3000,
            "book": {
                "id": 333,
                "publisher_id": 22,
                "name": "パーフェクトLaravel",
                "list_price": 3000,
                "is_electronic_book": false,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        }
    ]
]

問3の解答

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

こういった場合は、同じくコレクションのメソッドであるtransformの利用が考えられます。

transformメソッドはコレクションを繰り返し処理し、コレクションの各アイテムに指定したコールバックを適用します。コレクション中のアイテムはコールバックから返される値に置き換わります。

Laravel5.8 公式ドキュメント - transform
https://readouble.com/laravel/5.8/ja/collections.html#method-transform

is_electronic_bookgroupByした結果、2つの要素に分かれていますので、各々の要素に対してさらにpublisher_idgroupByします。

問3の解答例
$answer3 = $order_details->groupBy('book.is_electronic_book')
    ->transform(function ($order_detail) {
        return $order_detail->groupBy('book.publisher_id')
            ->values();
        });
問3の解答例の結果をJSON化したもの
[
    [
        [
            {
                "id": 10001,
                "order_id": 1,
                "book_id": 111,
                "selling_price": 3000,
                "book": {
                    "id": 111,
                    "publisher_id": 11,
                    "name": "自習Laravel",
                    "list_price": 3500,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 11,
                        "name": "AA出版"
                    }
                }
            }
        ],
        [
            {
                "id": 10003,
                "order_id": 1,
                "book_id": 333,
                "selling_price": 3000,
                "book": {
                    "id": 333,
                    "publisher_id": 22,
                    "name": "パーフェクトLaravel",
                    "list_price": 3000,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ],
    [
        [
            {
                "id": 10002,
                "order_id": 1,
                "book_id": 222,
                "selling_price": 2500,
                "book": {
                    "id": 222,
                    "publisher_id": 22,
                    "name": "基礎から学ぶPHP",
                    "list_price": 3000,
                    "is_electronic_book": true,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ]
]

最後に

以上です。何問解けたでしょうか?

コレクションメソッドを使っていて、また何か気付きがあれば練習問題にしてみたいと思います:innocent:

参考

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

Laravelコレクションメソッド groupByを重ねて複数使うには? (練習問題形式)

はじめに

Laravelのコレクションの便利なメソッドgroupBy

あれこれ触ったので、覚えたことを忘れないよう、練習問題にしてみました。

タイトルの、groupByを重ねて複数使う、最後の問3です。
すぐにやり方を知りたいという方はこちらへどうぞ。

問題

Order, OrderDetail, Book, Publisherモデルがあり、OrderDetailからリレーションのあるBook, Publisherまでを以下のように取得しました。

$order_details = OrderDetail::with('book.publisher')->get();

print_r(json_encode($order_details, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT))
$order_detailsをJSON化したもの
[
    {
        "id": 10001,
        "order_id": 1,
        "book_id": 111,
        "selling_price": 3000,
        "book": {
            "id": 111,
            "publisher_id": 11,
            "name": "自習Laravel",
            "list_price": 3500,
            "is_electronic_book": false,
            "publisher": {
                "id": 11,
                "name": "AA出版"
            }
        }
    },
    {
        "id": 10002,
        "order_id": 1,
        "book_id": 222,
        "selling_price": 2500,
        "book": {
            "id": 222,
            "publisher_id": 22,
            "name": "基礎から学ぶPHP",
            "list_price": 3000,
            "is_electronic_book": true,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    },
    {
        "id": 10003,
        "order_id": 1,
        "book_id": 333,
        "selling_price": 3000,
        "book": {
            "id": 333,
            "publisher_id": 22,
            "name": "パーフェクトLaravel",
            "list_price": 3000,
            "is_electronic_book": false,
            "publisher": {
                "id": 22,
                "name": "BB出版"
            }
        }
    }
]

問1

publisher_idgroupByしてください。

問2

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)

{
    "11": [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    "22": [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

これを0から振り直してください。(JSON化した場合、以下のように出力されるようにしてください)

[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
//略

問3

まず、is_electronic_bookgroupByし、その上でそれぞれの要素に対して、publisher_idgroupByしてください。

補足

必要に応じてLaravel公式ドキュメントのgroupByほかを参照してください。
https://readouble.com/laravel/5.8/ja/collections.html#method-groupby

ここから先、解答になります。

...

...

...

解答

問1の解答

publisher_idgroupByしてください。

publisher_idは、bookをキーとする値の中に、さらにキーとして存在していました。

$order_detailsをJSON化したものの抜粋
        "book": {
            "id": 111,
            "publisher_id": 11,

こういった場合は、.で繋いで指定すればOKです。

問1の解答例
$answer1 = $order_details->groupBy('book.publisher_id');
問1の別解
$answer1 = $order_details->groupBy('book.publisher.id');

問2の解答

問1の結果では、グループ化された配列のキーはpublisher_idの値になります。(1122)
これを0から振り直してください。

キーの振り直しにはコレクションのメソッドであるvaluesが使えます。

valuesメソッドはキーをリセット後、連続した整数にした新しいコレクションを返します。

Laravel5.8 公式ドキュメント - values
https://readouble.com/laravel/5.8/ja/collections.html#method-values

問2の解答例
$answer2 = $order_details->groupBy('book.publisher_id')
    ->values();
問2の解答例の結果をJSON化したもの
[
    [
        {
            "id": 10001,
            "order_id": 1,
            "book_id": 111,
            "selling_price": 3000,
            "book": {
                "id": 111,
                "publisher_id": 11,
                "name": "自習Laravel",
                "list_price": 3500,
                "is_electronic_book": false,
                "publisher": {
                    "id": 11,
                    "name": "AA出版"
                }
            }
        }
    ],
    [
        {
            "id": 10002,
            "order_id": 1,
            "book_id": 222,
            "selling_price": 2500,
            "book": {
                "id": 222,
                "publisher_id": 22,
                "name": "基礎から学ぶPHP",
                "list_price": 3000,
                "is_electronic_book": true,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        },
        {
            "id": 10003,
            "order_id": 1,
            "book_id": 333,
            "selling_price": 3000,
            "book": {
                "id": 333,
                "publisher_id": 22,
                "name": "パーフェクトLaravel",
                "list_price": 3000,
                "is_electronic_book": false,
                "publisher": {
                    "id": 22,
                    "name": "BB出版"
                }
            }
        }
    ]
]

問3の解答

groupByを重ねて複数使うには、同じくコレクションのメソッドであるtransformも利用することが考えられます。

問3の解答例
$answer3 = $order_details->groupBy('book.is_electronic_book')
    ->transform(function ($order_detail) {
        return $order_detail->groupBy('book.publisher_id')
            ->values(); // valuesは用途に応じてお好みで。詳細は問2参照。
        });

transformメソッドはコレクションを繰り返し処理し、コレクションの各アイテムに指定したコールバックを適用します。コレクション中のアイテムはコールバックから返される値に置き換わります。

Laravel5.8 公式ドキュメント - transform
https://readouble.com/laravel/5.8/ja/collections.html#method-transform

解答例ではis_electronic_bookgroupByした結果、2つの要素に分かれていますので、このtransformを使って各々の要素に対してさらにpublisher_idgroupByしています。

問3の解答例の結果をJSON化したもの
[
    [
        [
            {
                "id": 10001,
                "order_id": 1,
                "book_id": 111,
                "selling_price": 3000,
                "book": {
                    "id": 111,
                    "publisher_id": 11,
                    "name": "自習Laravel",
                    "list_price": 3500,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 11,
                        "name": "AA出版"
                    }
                }
            }
        ],
        [
            {
                "id": 10003,
                "order_id": 1,
                "book_id": 333,
                "selling_price": 3000,
                "book": {
                    "id": 333,
                    "publisher_id": 22,
                    "name": "パーフェクトLaravel",
                    "list_price": 3000,
                    "is_electronic_book": false,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ],
    [
        [
            {
                "id": 10002,
                "order_id": 1,
                "book_id": 222,
                "selling_price": 2500,
                "book": {
                    "id": 222,
                    "publisher_id": 22,
                    "name": "基礎から学ぶPHP",
                    "list_price": 3000,
                    "is_electronic_book": true,
                    "publisher": {
                        "id": 22,
                        "name": "BB出版"
                    }
                }
            }
        ]
    ]
]

最後に

以上です。何問解けたでしょうか?

コレクションメソッドを使っていて、また何か気付きがあれば練習問題にしてみたいと思います:innocent:

参考

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