20201207のlaravelに関する記事は9件です。

hintを追加したらtype errorで動かなくなった話

概要

laravelでmockeryを用いてunit testを作成中、以下のエラーが出た。

TypeError: Argument 1 passed to App\Http\Services\hoge::__construct() must be an instance of App\Http\Services\hoge or null, instance of Mockery_0__Tests_Feature_hoge given, called in /var/www/hoge/tests/Feature/hogeTest.php on line 277

hoge型かnullのみ受け付けるコードに、Mockery_0__Tests_Feature_hogeを与えたというエラーである。

エラーについて

エラーを起こしたコード

php-テストコード.php
# testServiceで利用するモック
$hogeServiceMock = \Mockery::mock(hogeService::class);
$hogeServiceMock->shouldReceive('gethoge')
                ->andReturn($gethoge);

// テスト対象のサービス、コンストラクタでモックと連携
$testService = new testService($hogeServiceMock);

// テスト対象の関数実行
$response = $testService->issue($testInput);

// テスト結果の確認
$this->assertSame($expected, $response);
php-テスト対象.php
class TestService
{
    /** @var hogeService */
    private $hogeService;

    /**
     * AuthController constructor.
     *
     * @param hogeService $hogeService
     */
    public function __construct(hogeService $hogeService)
  {
        $this->hogeService = $hogeService;
    }

    /**
     * テスト対象関数
     *
     * @param string $testInput
     * @return 実行結果の文字列
     */
    public function issue(string $testInput)    
    {
       # いろいろする
    }

エラーの経緯

当初、testService側のhintを記載漏れしており、コードレビューを経てhintを全て追加することに。
すると今まで正常稼働していてたテストコードが上記のtype errorを出力するようになった。

エラーが起きた原因

実はテストコードphp側に記載したhogeServiceへの名前空間(use)にタイポがあり、hogeserviceをうまく呼び出せていなかった。
そのためmockeyが独自型のMockery_0__Tests_Feature_hoge型として動作しており、hintを追加したことでそのエラーに気が付いた。

エラー解消

ちゃんとhogeServiceを呼び出せるように名前空間のtypoを直す。
するとテストコード自体には一切手を入れずとも、hintのエラーが解消する。

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

laravelでフォローしたユーザーの投稿を取得する

フォローしたユーザーの投稿を取得します
以下にテーブル構造やモデルの設定を簡単に示しておきます

以前にもフォロー機能作成記事を書いているので詳しくはそっちで
https://qiita.com/mitsu-0720/items/0c2fcdd367e8a6c5999c

XXXX_XX_XX_XXXXXX_create_follow_users_table.php
 Schema::create('follow_users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('followed_user_id')->index();
            $table->unsignedBigInteger('following_user_id')->index();
            $table->foreign('followed_user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('following_user_id')->references('id')->on('users')->onDelete('cascade');
            $table->timestamps();
        });

User.php
// フォロワー→フォロー
    public function followUsers()
    {
        return $this->belongsToMany('App\User', 'follow_users', 'followed_user_id', 'following_user_id');
    }

    // フォロー→フォロワー
    public function follows()
    {
        return $this->belongsToMany('App\User', 'follow_users', 'following_user_id', 'followed_user_id');
    }

PostController.php
public function timeline() {
        $posts = Post::query()->whereIn('user_id', Auth::user()->follows()->pluck('followed_user_id'))->latest()->get();
        return view('posts.timeline')->with([
            'posts' => $posts,
            ]);
    }

$posts = Post::query()->whereIn('user_id', Auth::user()->follows()->pluck('followed_user_id'))->latest()->get();
について

日本語で解説してみると
Post::query() ポストモデルの中の
whereIn('user_id') user_idが
Auth::user()->follows()->pluck('followed_user_id') 自分がフォローしているユーザーの中でフォロワーが自分であるユーザーを取得して
latest()->get() 最新順に取得する

といった感じになります

whereInはまだ自分も理解が浅いのですが第二引数が配列となる場合はこっちを使わないと想定した結果を取得できないイメージ
今回みたいに条件指定が複雑な場合はwhereよりwhereInを使った方がいいと思います
実際このtimeline()関数もwhereだと何も取得できませんでした

pluckメソッドは引数の値だけを取得できるメソッド
今回はフォローしてるユーザーのIDだけを取得したかったのでpluck('followed_user_id')でIDのみを取得しています

つまり今回自分がuser_id1、2、3のユーザーをフォローしているとすると上の関数は
Post::query()->whereIn('user_id', [1,2,3])->latest()->get();
といった意味を持ちます

ハマった点

以前自分が書いたメソッドはこう

PostsController.php
$posts = Post::query()->whereIn('user_id', Auth::user()->follows()->pluck('following_user_id'))->latest()->get();

フォローしてるユーザーのIDが欲しいんだから取得するのはfollowed_user_idではなくfollowing_user_idや!
と思ってdd($posts)してみたところ取得されたのは自分自身の投稿のみだった

当時は、は?????????????なんで??????????????????
ってなってましたが今考えればそれもそのはず

フォローユーザーは自分なのだからフォローしているユーザーのidを取得しても自分のIDしか取得できないのだ

まとめ

フォローしているユーザーの投稿を取得したいのだからフォロワーのIDなんか使わないだろうと頭でっかちになって考えていたが
「フォロワーが自分であるユーザー」を取得することにより結果的に「自分がフォローしているユーザー」を取得できるという結論になったのはいい経験になった
プログラミングの面白さをしれたいい経験でした

おまけ

「フォローしているユーザー」+「自分自身」の投稿を取得したい場合

PostsController.php
$posts = Post::query()->whereIn('user_id', Auth::user()->follows()->pluck('followed_user_id'))->orWhere('user_id', Auth::user()->id)->latest()->get();

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

Laravel6をさくらレンタルサーバーにデプロイ 備忘録

手順

sshでサーバにログイン

ssh user@xxx.com

Laravel プロジェクトをgit経由でダウンロードする

/home/user/
mkdir laravel
cd laravel
git clone ~~

composerインストールをする

/home/user/laravel/project_name/
curl -sS https://getcomposer.org/installer | php

インストールできたか確認してみる

/home/user/laravel/project_name/
php composer.phar

Venderのインストール

/home/user/laravel/project_name/
php composer.phar install

.envファイルの設定をする

/home/user/laravel/project_name/.env
APP_ENV=production
APP_DEBUG=false

DB_CONNECTION=mysql
DB_HOST=mysql000.db.sakura.ne.jp
DB_PORT=3306
DB_DATABASE=さくらインターネットのレンタルサーバで作成したデータベース名
DB_USERNAME=アカウント名
DB_PASSWORD=データベースパスワード

php artisan ○○をする

/home/user/laravel/project_name/
php artisan key:generate
php artisan migrate
php artisan storage:link

.htaccessを編集する

/home/user/laravel/project_name/public/.htaccess
<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301] 

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

index.phpを編集する

/home/user/laravel/project_name/public/index.php
require '/home/user/laravel/project_name/vendor/autoload.php';

$app = require_once '/home/user/laravel/project_name/bootstrap/app.php';

wwwフォルダー下にシンボリックリンクを作成する

ln -s /home/user/laravel/project_name/public/ /home/user/www/project_name

ブラウザからアクセスして確認してみる

https://xxx.com/project_name/

以上。
自分が実際にデプロイした際の手順をまとめておきました。
いろいろなサイトを飛び回って探して苦労したので忘れない為に。

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

【LaravelExcel】LaravelExcelのバージョンアップでハマったことまとめ

経緯

Laravelをバージョンアップするに伴い、PHPやライブラリであるLaravelExcelやPHPExcelなどもバージョンアップされ、いくつかの変更点でハマった点をまとめてみた

新旧のバージョン情報

Laravel

新:6.18.42
旧:5.1.46

PHP

新:7.4.11
旧:5.6.30

ハマった点と解決方法

Excel::load()の廃止

元々はこんな感じでExcelを読み込んでいました。

test.php
Excel::load('file.xlsx', function ($file) {
  // PHPExcelを使った処理 など
});

→後述のPHPSpreadsheetの機能で読み込む方法に変更しました。
多分、Excelファイルを読み込んで値を返すだけならLaravelExcelの3以降だとExcel::import()を使用するのがいいと思います。
PHPExcelなどを使ってシート、列、行、セルなどを操作している場合はPHPSpreadsheetを使用する形へ変更した方がいいと思います。

PHPExcelの廃止→PHPSpreadsheetへ移行

PHPExcel自体が非推奨・廃止となり、PHPSpreadsheetの使用を推奨されています。
使用するクラスなどが変更となっています。

ちなみにExcelファイルの読み込みは下記のような形で実装しました。

namespace App\Http\Controllers;

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as Reader;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;

class ImportExcelController extends Controller
{

// エクセル読み込み処理
$reader = new Reader();
$sheet = $reader->load('/test.xlsx');

}

※前述した通り、色々な操作をする場合のExcel::import()での読み込みが上手く構築出来ず…もし何か良い方法などをご存知の方がいらっしゃいましたらご教示くださいますと嬉しいです。

セルオブジェクトの値取得時、列番号の指定が変更

PHPSpreadsheetへ移行したことでセルオブジェクトにおける列番号の指定が変更になりました。これが地味に混乱させる要因に…

旧処理

test.php
$col = 0; // 列
$row = 0; // 行

// A1のセルオブジェクトを取得
$cell = $sheet->getCellByColumnAndRow($col, $row)->getValue();

新処理

test.php
$col = 1; // 列
$row = 0; // 行

// A1のセルオブジェクトを取得
$cell = $sheet->getCellByColumnAndRow($col, $row)->getValue();

旧処理では列はA=0,B=1…と0始まりで指定されますが、新処理ではA=1,B=2…と1始まりに変更になっています。
普段プログラミングからもずれており、ややこしさを感じますね。

結論

・Excelを使う処理はシンプルであるべき!
特にテンプレートに可変部分を持たせないようにした方がいいですマジで

参考資料

Upgrade Guide | Laravel Excel
PHPExcel と PhpSpreadsheet の比較

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

LaravelExcelのバージョンアップでハマったことまとめ

経緯

Laravelをバージョンアップするに伴い、PHPやライブラリであるLaravelExcelやPHPExcelなどもバージョンアップされ、いくつかの変更点でハマった点をまとめてみた

新旧のバージョン情報

Laravel

新:6.18.42
旧:5.1.46

PHP

新:7.4.11
旧:5.6.30

ハマった点と解決方法

Excel::load()の廃止

元々はこんな感じでExcelを読み込んでいました。

test.php
Excel::load('file.xlsx', function ($file) {
  // PHPExcelを使った処理 など
});

→後述のPHPSpreadsheetの機能で読み込む方法に変更しました。
多分、Excelファイルを読み込んで値を返すだけならLaravelExcelの3以降だとExcel::import()を使用するのがいいと思います。
PHPExcelなどを使ってシート、列、行、セルなどを操作している場合はPHPSpreadsheetを使用する形へ変更した方がいいと思います。

PHPExcelの廃止→PHPSpreadsheetへ移行

PHPExcel自体が非推奨・廃止となり、PHPSpreadsheetの使用を推奨されています。
使用するクラスなどが変更となっています。

ちなみにExcelファイルの読み込みは下記のような形で実装しました。

namespace App\Http\Controllers;

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as Reader;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;

class ImportExcelController extends Controller
{

// エクセル読み込み処理
$reader = new Reader();
$sheet = $reader->load('/test.xlsx');

}

※前述した通り、色々な操作をする場合のExcel::import()での読み込みが上手く構築出来ず…もし何か良い方法などをご存知の方がいらっしゃいましたらご教示くださいますと嬉しいです。

セルオブジェクトの値取得時、列番号の指定が変更

PHPSpreadsheetへ移行したことでセルオブジェクトにおける列番号の指定が変更になりました。これが地味に混乱させる要因に…

旧処理

test.php
$col = 0; // 列
$row = 0; // 行

// A1のセルオブジェクトを取得
$cell = $sheet->getCellByColumnAndRow($col, $row)->getValue();

新処理

test.php
$col = 1; // 列
$row = 0; // 行

// A1のセルオブジェクトを取得
$cell = $sheet->getCellByColumnAndRow($col, $row)->getValue();

旧処理では列はA=0,B=1…と0始まりで指定されますが、新処理ではA=1,B=2…と1始まりに変更になっています。
普段プログラミングからもずれており、ややこしさを感じますね。

結論

・Excelを使う処理はシンプルであるべき!
特にテンプレートに可変部分を持たせないようにした方がいいですマジで

参考資料

Upgrade Guide | Laravel Excel
PHPExcel と PhpSpreadsheet の比較

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

LaravelでAWSのSESからメール送信する方法

環境

Laravel:7.3
AWS SESリージョン:Tokyo(ap-northeast-1)

前提

・メールアドレスがある

やること

①composerでAmazon AWS SDK forPHPをインストールする
②SESにメールアドレスを登録する
③サンドボックスの解除
④SESのAPI使えるようにAIMでユーザー作る
⑤Laravelの設定(.env)
⑥試しにメール送信

①composerでAmazon AWS SDK forPHPをインストールする


composer.jsonに以下を記載しcomposer update する。

"aws/aws-sdk-php": "~3.0"

②SESにメールアドレスを登録する

Mails > Verify a New Email Addressの順でクリック

メールアドレスを入力してVerify This Email Addressをクリック

入力したメールアドレスに認証用のURLが記載されたメールが届きますので、URLを開いて認証を完了します。

③サンドボックスの解除

https://laraweb.net/knowledge/7083/

これ参考にしてくれぇ

④SESのAPI使えるようにAIMでユーザー作る

サービス(左上) > IAM > ユーザー > ユーザーの追加 > 任意の名前入力 + プログラムによるアクセスにチェック入れる > 次のステップ(右下の青いやつ) > [既存のポリシーを直接アタッチ]をクリック > [AmazonSESFullAccess] をアタッチ > 後はなんもいじらないで青いボタン押す。

重要↓

最後まで進むと、「アクセスキー」と「シークレットアクセスキー」が表示されますので、CSVでダウンロードするかメモしておきます。

⑤Laravelの設定(.env)

MAIL_MAILER=ses  //他の記事だと MAIL_DRIVER=ses ※config/mail.phpで確認してください
MAIL_FROM_ADDRESS=上で登録したメアド
MAIL_FROM_NAME="メールの送り主の名前になる"(何も書かないとExampleで送られる)
SES_KEY=上でメモした[アクセスキー]
SES_SECRET=上でメモした「シークレットアクセスキー」
SES_REGION=ap-northeast-1 //これ東京リージョンです

⑥試しにメール送信

 sudo php artisan tinker

>>> Mail::raw('mail 本文', function($message) { $message->to('送りたいメールアドレス')->subject('subjectだよーん'); });
=> null  ←これ成功!メール確認して届いてたら成功!

※上で成功したのに仮にお問い合わせフォームとかでエラー出たら自分で書いたソースを疑った方がいいかもです。。。知らんけど。

終了

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

【Laravel】public staticとは何か?簡単に解説

public static についての解説

クラスプロパティもしくはメソッドを static として宣言することで、 クラスのインスタンス化の必要なしにアクセスすることができます。 static なプロパティは、インスタンス化されたクラスオブジェクトから アクセスすることはできません (static なメソッドにはアクセスできます)。
引用:公式サイト

要するにstaticをつければ
・インスタン化せずにアクセス
・アクセスのやり方はクラス::プロパティ
・逆にstaticをつけたプロパティにはインスタンスからのアクセス不可

staticをつけないとどうなる?

付けない時はインスタンス化してからアクセスする必要がある

変数 = new クラス
変数→メソッド
変数→プロパティ

こんな感じ。

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

LaravelでMailableクラスでメールを送信する方法

【メモです。】

コントローラーの作成

php artisan make:controller SampleMailController

ルーティングの追加

Route::get('/mail', 'SampleMailController@send');

コントローラーに処理を追加

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Mail\SendTestMail;

use Mail;

class MailSendController extends Controller
{
    public function send()
    {
        $to = [
        [
            'email' => 'XXXXX@XXXXX.jp',
            'name' => 'Test',
        ]
    ];

        Mail::to($to)->send(new SendTestMail());
    }
}

Mailableクラスの作成

php artisan make:mail SendTestMail

SendTestMail.phpの編集

public function build()
{
    return $this->view('emails.test')
                ->from('XXX@XXXX','Test')
                ->subject('This is a test mail');
}

views/emailsの下にtest_text.blade.phpファイルを作成し、メール本文を記述

好きなように書けばOK

【余談】複数人にメールを送信したい場合

コントローラーの処理を下記のように変える。

$to = User::all();

Mail::to($to)->send(new SendTestMail());

【余談2】引数を渡す

コントローラーで下記のように引数に値を渡す。

$user = User::find(1);

Mail::to($user)->send(new SendTestMail($user));

あとはMailbleクラスの方で好きなように記述。

public function __construct($user)
    {
        $this->user = $user;
    }

    public function build()
    {
        return $this->text('emails.test_text')
                    ->from('XXX@XXXXX','Reffect')
                    ->subject('This is a test mail')
                    ->with(['user' => $this->user]);
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelプロジェクト作成時に"Could not find package laravel/laravel with stability stable."と出る

筆者の環境

  • macOS Catalina バージョン10.15.6
  • PHP 7.4.13
  • Composer 2.0.8

発生問題

久しぶりに新規Laravelプロジェクトをローカルに作成しようと思い、いつも通りターミナルから以下のコマンドを打つと…

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

以下のエラーが。

  [InvalidArgumentException]
  Could not find package laravel/laravel with stability stable.

仕方がないのでlaravel/installerを使おうと思い、以下のようにlaravel/installerをインストールするコマンドを打つと…

$ composer global require "laravel/installer"

以下のエラーが。

  [InvalidArgumentException]
  Could not find package laravel/installer.

  Did you mean one of these?
      laravel/installer
      codemyviews/vanilla-installer

解決策

結論、Composer自体を完全にアンインストールし、インストールし直したら無事エラーも起きずLaravelプロジェクトが作成でき、laravel/installerもインストールできました…(詳細な原因は分からず…)

アンインストール手順については以下の記事を参考させて頂きました。
(アンインストール時に必要となる.composerとcomposer.pharのパスはMacでは、「/Users/ユーザ名/.composer」、「/usr/local/bin/composer/composer.phar」となります。)

[PHP]Composer自体を完全にアンインストールする方法 | akamist blog

問題の原因は特定できませんでしたが、ひとまずアンインストールすることで解決できました。
「Could not find package laravel/laravel with stability stable」に遭遇時の対処法はネット上ではあまり見つからなかったため、今回投稿致しました。
同じような問題に遭遇した方の参考になれば幸いです。

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