maatwebsite/excel 複合keyを持つmodelを使うとmb_strpos() expects…

PHP

Laravelを使ってアプリを開発中、maatwebsite/excelを用いて
CSV出力を行う機能を実装する際に、ハマったところがあるので共有です

mb_strpos() expectsってエラーが出た

Hoge.php

:
class Hoge extends Model
{
    use \LaravelTreats\Model\Traits\HasCompositePrimaryKey;
    protected $table = 'hoge';
    protected $primaryKey = ['key1', 'key2'];
:

こんな感じで、データ抽出対象のテーブルのモデルを定義して

Export.php

:
use App\Models\Hoge;
class Export implements FromQuery, WithHeadings
{
    use Exportable;
    public function __construct($condition)
    {
        $this->conditions = $conditions;
    }
    public function headings(): array
    {
        return [
            'カラム1',
            'カラム2',
            'カラム3',
        ];
    }
    public function query()
    {
        return Hoge::select(
            'key1',
            'key2',
            'column'
        )->where('key1', 'hoge');
    }
:

こんな感じのすごくシンプルなクエリを組んだ

この状態で動作確認をすると、以下のようなエラーが発生する

local.ERROR: mb_strpos() expects parameter 1 to be string, array given {"exception":"[object] (ErrorException(code: 0): mb_strpos() expects parameter 1 to be string, array given at APP\vendor\\laravel\\framework\\src\\Illuminate\\Support\\Str.php:158)
[stacktrace]
#0 [internal function]: Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(2, 'mb_strpos() exp...', 'APP...', 158, Array)
#1 APP\vendor\\laravel\\framework\\src\\Illuminate\\Support\\Str.php(158): mb_strpos(Array, '.')
#2 APP\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Model.php(363): Illuminate\\Support\\Str::contains(Array, '.')
#3 APP\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Model.php(1344): Illuminate\\Database\\Eloquent\\Model->qualifyColumn(Array)
#4 APP\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Builder.php(683): Illuminate\\Database\\Eloquent\\Model->getQualifiedKeyName()
#5 APP\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Concerns\\BuildsQueries.php(20): Illuminate\\Database\\Eloquent\\Builder->enforceOrderBy()

エラーの原因

さっきのエラーログをみると、Model.phpの363行目に何かがありそう。ってことで確認

/**
 * Qualify the given column name by the model's table.
 *
 * @param  string  $column
 * @return string
 */
public function qualifyColumn($column)
{
    if (Str::contains($column, '.')) {
        return $column;
    }

    return $this->getTable().'.'.$column;
}

Str::containsってところが悪いらしい。カラム名を受け取って ‘.’ があるかチェックしてる感じ
じゃぁ qualifyColumn って関数を呼び出している箇所を確認してみる。

/**
 * Get the table qualified key name.
 *
 * @return string
 */
public function getQualifiedKeyName()
{
    return $this->qualifyColumn($this->getKeyName());
}

$this->getKeyName() ここで、文字列を取得する想定で組まれてますねー
ところが、Hoge.phpのprimaryKeyは複合KEYなので、配列で飛んできます。。。
これがエラーの原因か!判明

対応

さて、どうやってこの問題に対応すればよいかを唸りながら考えました。

compositePrimaryKeyのトレイトも書いてるし、これでダメならどうしたものか。。と

結局、とても簡単な解決方法に辿り着きました。
モデルを使わなければイイ!

というわけで、以下のコードで解決です。

public function query()
{
    return DB::table('hoge')
        ->select(
            'key1',
            'key2',
            'column'
        )
        ->where('key1', 'hoge');
}

どうしてもModelを使いたいって人は・・・

ごめんなさい。わかりません!