Help us understand the problem. What is going on with this article?

OctoberCMSプラグイン作成:リソースの管理画面からファイルを関連付けてアップロードする

More than 3 years have passed since last update.

概要

例えば、製品やユーザなどのデータに画像ファイルを関連付けたい時、OctoberCMSでは簡単にそのデータの管理ページに画像ファイルのアップロード・表示・管理機能が追加できる。意外にもドキュメントではまとまった説明がなかったのでここに記載する。

Screen Shot 2018-01-01 at 19.22.31.png

ステップ

  1. Configを設定
  2. モデルにリレーションを定義
  3. CRUD画面にファイルアップロードウィジェットを追加

前提

テーブル、モデル、CRUD画面は既に作成済み。

Configを設定する

まだ設定していない場合は config/filesystem.php にdisksを設定する。デフォルトで存在するが、下記に注意。

  • localを使用する場合、指定ディレクトリにweb serverユーザの書き込み権限がある。
  • s3などクラウドを使用する場合はクラウドサーバへの書き込みが可能な状態。

下記の例ではenv()を使用しているが、直書きでも問題ない。

config/filesystems.php
    'disks' => [

        'local' => [
            'driver' => 'local',
            'root'   => storage_path('app'),
        ],

        's3' => [
            'driver' => 's3',
            'key'    => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_REGION'),
            'bucket' => env('AWS_S3_BUCKET'),
            'visibility' => 'public',
        ],
    ],

そして、ファイルアップロード関連の設定は config/cms.php のstorage.uploadsで行う。

config/cms.php
    'storage' => [

        'uploads' => [
            'disk'   => 'local',
            'folder' => 'uploads',
            'path'   => '/storage/app/uploads',
        ],

        // S3へを指定する場合は`path`はバケットへのURLを指定する必要がある
//        'uploads' => [
//            'disk'   => 's3',
//            'folder' => 'uploads',
//            'path'   => 'https://s3-ap-southeast-1.amazonaws.com/my-bucket/uploads',
//        ],

        ...
    ],

対象データのモデルにリレーションを定義する

OctoberCMSでは添付ファイルの情報は別のテーブルに保存される。ここで説明するようにOctoberCMSの添付ファイルのドキュメントに従えば、添付ファイルの情報は既存のsystem_filesテーブルに保存されるので、新たにテーブルを作成したり修正したりする必要はない。

対象のモデルクラスにattachOneまたはattachManyを追加する。下記の例ではProductというモデルクラスに$attachManyフィールドを設けてattachManyリレーションを定義している。リレーション名がimageでリレーションのモデルクラスをSystem\Models\Fileとしている。

models/Product.php
    public $attachMany = [
        'images' => 'System\Models\File'
    ];

CRUD画面にファイルアップロードウィジェットを追加する

BuilderプラグインのCRUD画面作成画面からでFileUpload controlを追加する。
Screen_Shot_2018-01-01_at_19_48_42.png

追加したウィジェットの設定でField nameをモデルクラスで定義したリレーション名に設定する。上記の場合、imagesになる。

ファイルが画像の場合は、Builderで生成されたyamlファイル(デフォルトでは<plugin_name>/models/<model_name>/fields.yaml)を開いて追加したフィールドのmodeimageにすると、サムネイルを表示してくれる。

fields:
    name:
        label: 'cocci.utility::backend.common.field.name.label'
        oc.commentPosition: ''
        span: auto
        required: true
        type: text
    display_name:
        label: 'cocci.utility::backend.common.field.display_name.label'
        oc.commentPosition: ''
        span: auto
        required: true
        type: text
    description:
        label: 'cocci.utility::backend.common.field.description.label'
        size: large
        oc.commentPosition: ''
        span: auto
        type: textarea
    images:
        label: Images
        oc.commentPosition: ''
        mode: image
        useCaption: true
        thumbOptions:
            mode: crop
            extension: auto
        span: auto
        type: fileupload

アップロードしたファイルの使用方法

データに添付したファイルの使用方法も非常に簡単だ。
例えば上の例だと、下記のようにアップロードしたファイルのURLを取得できる。

$product->images[0]->getPath();

また、attachOneのリレーションの場合は単純に配列ではなくなる。

$product->image->getPath();

Twigテンプレートからは単純にこうだ。

<img src='{{ product.images[0].getPath() }}'>

バグ対処方法

2018/1/1現在(build 431)、OctoberCMSにバグらしきものがあり、storage.uploads.diskが効かない。代わりにfilesystem.phpのdefaultが適用されてしまっている。が、修正されるまで比較的簡単・安全な方法で回避できる。

上記の説明ではSystem\Models\Fileをリレーションモデルに使用しているが、このクラスを拡張して修正を仕込む。1行目のnamespaceだけ適宜変更すれば下記を丸コピーでよい。

myplugin/models/File.php
<?php namespace Pikanji\Myplugin\Models;

use Config;
use File as FileHelper;
use Storage;

class File extends \System\Models\File
{
    /**
     * Returns true if storage.uploads.disk in config/cms.php is "local".
     * @return bool
     */
    protected function isLocalStorage()
    {
        return Config::get('cms.storage.uploads.disk') == 'local';
    }

    /**
     * Copy the local file to Storage
     * @return bool True on success, false on failure.
     */
    protected function copyLocalToStorage($localPath, $storagePath)
    {
        $disk = Storage::disk(Config::get('cms.storage.uploads.disk'));
        return $disk->put($storagePath, FileHelper::get($localPath), ($this->isPublic()) ? 'public' : null);
    }
}

そして、このクラスをattachOne/Manyで指定したSystem\Models\Fileの代わりに指定する。これで、storage.uploads.diskで指定した先にアップロードしたファイルが保存されるはずだ。

この修正は、System\Models\FileのベースクラスOctober\Rain\Database\Attach\FileのメソッドisLocalStoragecopyLocalToStorageをオーバーライドしている。元のメソッドはcms.storage.uploads.diskではなくfilesystem.defaultを見てしまっているからだ。

この問題はこのissueで報告しており、pull requestも出しているので、早く取り込んでもらいたい。

pikanji
主な職歴:産業系組込みソフト → Androidアプリ → Webサイト・アプリ(PHPからFE全般) → Webサービス(高トラフィックJava BE)→ Webサービス(インフラ、BE、FE)& Flutterアプリ & Android+Kotlinアプリ & 会社経営 ←イマココ。とりあえず何でもやる。
gaogao-asia
バンコク/ホーチミン/東京/シンガポール拠点のスタートアップ・スタジオです! 起業家・起業家候補エンジニアメンバーが在籍し、0→1開発特化のプロフェッショナルが集まっています。他にも、海外プログラミング修行GAOGAOゲートやGAOGAOハウスの運営も行なっています! 絶賛メンバー募集中!
https://gaogao.asia/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away