カテゴリー: BackEnd

Laravelで画像アップロード実装が楽になるかもしれないlaravel-imageup

はじめに

こんにちは。webアプリケーションを作る際にファイルアップロードはごく当然の機能で、Laravelでももちろん標準でサポートされています。が、今回は要件次第では実装がほとんど不要になるライブラリlaravel-imageupを紹介します。
(要件次第で、と前置きをつけた理由は後述します)

環境

  • PHP 7.3.3
  • Laravel 5.8.4
  • MariaDB 10.3.13

導入

公式のREADMEに従って、composerでサクッとインストールします。
なお、依存先のライブラリであるIntervention ImageがPHPのGD拡張もしくはImagick拡張に依存するので、あらかじめ入れておきましょう。

# インストール
$ composer require qcod/laravel-imageup
Installing qcod/laravel-imageup (1.0.6): Downloading (100%)
...
...
Writing lock file
Generating optimized autoload files
...
...
Discovered Package: qcod/laravel-imageup
Package manifest generated successfully.

# 設定ファイル生成
$ php artisan vendor:publish --provider="QCod\ImageUp\ImageUpServiceProvider" --tag="config"
Copied File [/vendor/qcod/laravel-imageup/config/imageup.php] To [/config/imageup.php]
Publishing complete.

# /storageを公開するためのシンボリックリンク作成(laravel-imageupはデフォルトでstorageを使う)
$ php artisan storage:link
The [public/storage] directory has been linked.

あとはライブラリを/config/app.phpに登録して導入完了です。

'providers' => [
  /*
   * Laravel Framework Service Providers...
   */  ...
  ...
  /*
   * Package Service Providers...
   */  Illuminate\View\ViewServiceProvider::class,
]

実装

チュートリアルでお馴染みのmake:authでログイン機能を生成し、登録画面に画像アップロードのフォームを追加します。
なおマイグレーション処理は省略しますがUserテーブルにファイル名を保存するためのavatarカラムを追加済みとします。

$ php artisan make:auth
Authentication scaffolding generated successfully.

次にregister.blade.phpを編集します。form要素にenctype="multipart/form-data"を追加するのをお忘れなく。

@if ($errors->has('name')) {{ $errors->first('avatar') }} @endif

モデルクラス/app/User.phpにプロパティを追記します。

namespace App;

use QCod\ImageUp\HasImageUploads;

class User extends Authenticatable
{
  use HasImageUploads;

  protected $fillable = [
    'name', 'email', 'password', 'avatar',
  ];

  // ライブラリ用に追記するプロパティ
  protected static $imageFields = [
    'avatar'
  ];
}

Userテーブルにレコードを作成する処理ところで、avatarカラムに値を入れるためのコードを追記します。

protected function create(array $data)
{
    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => Hash::make($data['password']),
        'avatar' => sprintf('%s/%s', config('imageup.upload_directory'), $data['avatar']->hashName()), // 追記
    ]);
}

実際にはRequestクラスでのバリデーションなどありますが、これで最低限の実装完了です。フォームを送信してみましょう。
アップロードした画像が/app/storage/app/public/uploadsに保存されていれば成功です。

個人的にハマったこと

配列はダメ

私がこのライブラリを使った時、実装の要件として「複数のファイルを一度にアップロードできる」というのがありました。当初は特に深く考えず以下のようなhtmlを書いてましたが、ここで軽くハマりました。

この状態のままだと、前述の$data['avatar']に配列で格納され、ライブラリの自動アップロードが効かなくなるばかりかエラーを吐いてしまいます。
そもそもライブラリ側がIlluminate\Http\UploadedFileを受け取ることが前提なので、この場合は以下2つの解決策があります。

  • inputのname要素をname="avatar_1"name="avatar_2"というようにユニークな値で記述する
  • 自動アップロードを無効化して(デフォルトでは有効)、foreachなどでひとつずつ手動でアップロードする

私は後者で解決しました。
自動アップロードを無効化する場合は設定ファイルで'auto_upload_images' => falseとしてもよいのですが、アプリケーション全体に影響してしまうので、モデルクラスで以下のようにプロパティを設定します。

protected static $imageFields = [
    'avatar' => [
        // override global auto upload setting coming from config('imageup.auto_upload_images')
        'auto_upload' => false,
    ],
];

あとは$model->uploadImage($uploadedFile, 'avatar');で、配列に格納されているUploadedFileオブジェクトをひとつずつ渡せばOKです。

PHPStanで引っかかる

uploadImageメソッドが、PHPコードを静的解析するツールであるPHPStanでエラーを吐きます。
ライブラリのPHPDocが以下のようになっているため「第二引数$fieldがNULLのみ受け付ける」という意味になってしまっています。

/**
 * Upload and resize image
 *
 * @param $imageFile
 * @param null $field
 * @throws InvalidUploadFieldException|\Exception
 */public function uploadImage($imageFile, $field = null)
{
  ...
  ...

これはuploadImageをオーバーライドするか、モデルクラスに適当なメソッドを作って、その中でuploadImageを呼ぶようにしましょう。

さいごに

少々面倒なファイルアップロード処理を少ないコードで実装できるlaravel-imageupを紹介しました。中身とその挙動を把握した上で使えばかなり便利だと思います。また、Laravel本体のバージョンに追随している点も地味に長所だと思います。

おすすめ書籍

nomura

シェア
執筆者:
nomura
タグ: phplaravel

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前