0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Grav CMS の Flex Objects で、ミニブログを作る

Last updated at Posted at 2024-05-20

GravとFlex Objectsについて

Gravとは、PHPのSymfony製のflat-file CMSです。

MySQLなどのRDBを使わず、マークダウンファイルだけでサイトが作れます。
その気軽さから、大変評価の高いCMSのひとつです。

なお、静的サイトジェネレータではなく、動的サイトであるため、記事の予約投稿など、動的処理が可能です。

一方、RDBを使わないため、1000ページを超えるようなサイトだと、実用に向かなくなるそうです。

このデメリットを克服するため(だと思うのですが)、json やyaml でデータ管理できるようにしたのが、Flex Objects というプラグインのようです。

作者によれば、Flex Objects を使えば、5万ページ以上でも使える そうです。

Flex Objects は、Grav 1.7から追加されました。

今回は、このFlex Objects を使って、ミニブログ(microblog)を作りたいと思います。

環境

Grav 1.7.46
Flex Objects v1.3.6

参考サイト

Gravについて:

Flex Objectsについて:

Flex Objectでblogを作っている例:

プラグインをつくる

ミニブログ用の独自プラグインをつくります。
今回は、

  • 独自プラグイン名をmbloにし、
  • Flex Directory名をmicroblogにしました

なお、Flex Directoryというのは、Flex Objectsを使うときの、データのまとまりのようなイメージです。

手順は、公式ドキュメントのとおりです。

プラグインをつくるには、CLIから入力が必要です。
SSH接続していないレンタルサーバーの場合、いったんローカル環境でプラグインを作成してから、FTPなどでアップロードすることになるのではないかと思います。

bin/gpm install devtools
grav-admin bin/plugin devtools new-plugin

これを実行すると、いろいろ聞かれますが、とりあえず、以下2点に気をつける必要があります。

  • オプションでflexを選択することと、
  • ストレージはfolderを選択すること

※ストレージは、folderではなく、fileを選んでも良いかもしれません。
しかしsimpleを選ぶと、画像のアップロードができない(かもしれない)ので、注意してください。
(厳密に言えば、このあと操作するuse/plugins/【独自プラグイン】/blueprints/flex-objects/【Flex Directory】.yaml ファイルの、form.fields.images.destinationに、保存先ディレクトリを指定すれば、画像をアップロードできなくはないのかもしれません。
なんとなく、推奨されていなさそうだったので、今回はsimpleをさけました。)

その後、できた独自プラグインのディレクトリにcdして、composer update してください。

必要な入力フィールドを考える

ミニブログなので、投稿本文は必須です。
将来的にはmarkdownにしたくなるかもしれませんが、難しくなりそうだったので、今回はプレーンなテキストエリアで作ります。

また、投稿日時は、自動入力したいです。
画像も、ツイッター(X)みたいに4枚以内くらいにはアップロードしたいです。
一方、各投稿にタイトルやカテゴリー分けは不要です。

よって、以下のようなフィールドを想定しました。

form:
    validation: loose
    fields:
        log:
            type: textarea
            label: '投稿'
            validate:
                required: true
        time:
            type: datetime
            label: '投稿日'
            format: 'Y-m-d H:i:s'
            data-default@: '\Grav\Plugin\【独自プラグイン名】Plugin::currentTimeDataDefault'
            validate:
                required: true
        images:
            type: file
            label: '画像'
            multiple: true
            limit: 4
            accept:
                - image/*
  

Gravでは、設定をyamlファイルでするのですが、formの設定については、公式ドキュメントが参考になります。

form.fields.time.data-default@ は、投稿日時の自動入力に必要です。
独自プラグインのPHPファイル(user/plugins/【独自プラグイン名】/【独自プラグイン名】.php)に、以下のような関数を追加しておきます。
現在時刻を返すだけの関数ですが、data-default@に設定しておくと、デフォルト値として自動入力してくれます。

public static function currentTimeDataDefault(): string
{
    return date('Y-m-d H:i:s');
}

blueprintを作る

上記のformを含む設定ファイルを作成します。
このような設定ファイルを、Gravではblueprintと呼んでいます。

設定ファイルは、独自プラグインディレクトリの、blueprints/flex-objects/フォルダに入っています。
とても長いですが、ほとんどは自動で出力されるものです。

title: 'Microblog'
description: 'Gravでつくるミニブログ'
type: flex-objects

# Flex Configuration
config:
    # Administration Configuration
    admin:
        # Admin router (optional)
        router:
            path: '/microblog'

        # Admin menu (optional)
        menu:
            list:
                route: '/microblog'
                title: 'Microblog'
                icon: fa-address-card-o
                # Authorization to collection admin
                authorize: ['admin.microblog.list', 'admin.super']
                # Priority -10 .. 10 (highest goes up)
                priority: 2

        # Admin template type / folder
        template: default

        # Permissions
        permissions:
            # Primary permissions
            admin.microblog:
                type: crudpl
                label: 'Microblog'

        # List view
        list:
            title: log
            fields:
                time:
                log:
                    link: edit
                #images:
            options:
                per_page: 10
                order:
                    by: time
                    dir: desc

        # Edit View
        edit:
            title:
                template: ''

        # Preview View
        preview:
            enabled: false
            route:
            #template: '/plugins/flex-objects/directory:contacts'

        # Data Export
        export:
            enabled: true
            method: 'jsonSerialize'
            formatter:
                class: 'Grav\Framework\File\Formatter\YamlFormatter'
            filename: 'microblog'

    # Site Configuration
    site:
        templates:
            collection:
                # Lookup for the template layout files for collections of objects
                paths:
                    - 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
            object:
                # Lookup for the template layout files for objects
                paths:
                    - 'flex/{TYPE}/object/{LAYOUT}{EXT}'
            defaults:
                # Default template {TYPE}; overridden by filename of this blueprint if template folder exists
                type: 'microblog'
                # Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
                layout: default

    # Data Configuration
    data:
        # Object class to be used, allowing custom methods for the object
        object: 'Grav\Plugin\Mblo\Flex\Types\Microblog\MicroblogObject'
        # Collection class to be used, allowing custom methods for the collections
        collection: 'Grav\Plugin\Mblo\Flex\Types\Microblog\MicroblogCollection'
        # Index class to be used, works as a quick database-like lookup index
        index: 'Grav\Common\Flex\Types\Generic\GenericIndex'
        storage:
            # Storage class, use single file storage (does not support images and assets)
            class: 'Grav\Framework\Flex\Storage\FolderStorage'
            options:
                formatter:
                    # File formatter class, in this case the file is stored in markdown
                    class: 'Grav\Framework\File\Formatter\JsonFormatter'
                # JSON file where all the objects will be stored
                folder: user-data://microblog
        ordering:
            time: DESC 
        search:
            # Search options
            options:
                contains: 1
            # Fields to be searched
            fields:

form:
    validation: loose
    fields:
        log:
            type: textarea
            label: '投稿'
            validate:
                required: true
        time:
            type: datetime
            label: '投稿日'
            format: 'Y-m-d H:i:s'
            data-default@: '\Grav\Plugin\mbloPlugin::currentTimeDataDefault'
            validate:
                required: true
        images:
            type: file
            label: '画像'
            multiple: true
            limit: 4
            accept:
                - image/*

flex-objectsのyaml設定については、公式ドキュメントが参考になります。
公式ドキュメントによると、細かい設定もできるようです。

管理画面で確認してみる

この時点で、管理画面から操作できるようになります。
左側のメニューリストに、カードのようなアイコンが増えているはずなので、そこから操作できます。

初期設定のままだと、日付表示が、外国の表示形式なので、日本で馴染み深い年-月-日 時間:分 にします。
まず、user/plugins/【独自プラグイン名】/【独自プラグイン名】.phpを開きます。
この中の、getSubscribedEvents()という関数の戻り値が配列になっているので、この配列の中に、以下を追記します。

'onTwigTemplatePaths' => ['onTwigTemplatePaths', 1]

そして、onTwigTemplatePaths()関数を作ります。

public function onTwigTemplatePaths() : void
{
  $this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
  if ($this->isAdmin())
  {
    $this->grav['twig']->twig_paths[] = __DIR__ . '/admin/templates';
  }
}

なお、/templatesフォルダは、後ほど作ります。

次に、user/plugins/【独自プラグイン】/admin/templates/forms/fields/datetime/edit_list.html.twigファイルを作ります。
このtwigファイルには、以下を記入します。

{{ value ? value|date(Y-m-d G:i)|e }}

おそらくこれで、adminプラグインのtwigファイルが上書きされ、日付が日本の表示になるはずです。

ミニブログ用のページを準備する

管理画面から、ページを追加します。
ページの追加方法は、記事を書くなどが参考になります。
frontmatterを書くのですが、Flex Objectsの場合、以下のように書きます。

---
title: home
flex:
    directory: microblog
    collection:
        title: '{{ directory.title }}'
        layout: default
        object:
            layout: default
    object:
        title: 'object'
        layout: default
---
# flex microblogging

frontmatterの書き方は、公式ドキュメントを参考にしてください。

Twigを用意する

先ほど書いたfrontmatterで、layoutをdefaultにしていましたが、このdefaultのレイアウトを作成します。

collection(記事一覧)用のdefaultレイアウトと、object(個別の記事)用のdefaultレイアウトの、2種類あります。

作成場所は、user/plugins/【独自プラグイン名】/templates/flex/【ディレクトリ名】/collection/defaul.html.twigと、
user/plugins/【独自プラグイン名】/templates/flex/【ディレクトリ名】/object/defaul.html.twigの2ヶ所です。

Twigでは、いろいろできそうですが、さしあたり日付降順表示となるように、以下のように書きました。

<div style="max-width:30rem;margin-inline:auto;">
{%- for object in collection -%}
  <div style="margin-block:3rem;padding:1rem;border:thin solid #aaa;">
  {%- render object layout: object_layout with object_context -%}
  <a href="./id:{{ object.getStorageKey() }}">個別記事ページへ</a>
  </div>
{%- endfor -%}
</div>
<div>
<p style="font-size:1.125rem;">{{ object.log|nl2br }}</p>

{%- if object.images -%}
<div style="display:flex;flex-wrap:nowrap;justify-content:center;gap:5px;">
  {% for image_data in object.images %}
    {% set image = object.media.all[image_data.name] %}
  <div style="max-height:200px;">
    {{ image.cropResize(200, 100).html()|raw }}
  </div>
  {% endfor %}
</div>
{%- endif -%}
<p style="border-top:thin solid #aaa;color:#777;font-size:.95;text-align:right;margin:0;">
  {{ object.time }}
</p>
</div>

この状態で、先ほど作成したページにアクセスすると、ミニブログが一覧表示されるはずです。

感想

ここまでで、とりあえず、最低限のものはできたと思います。
が、リンクを張ったり、ページネーションを設定したりと、まだまだ不完全です。

また、Generic indexというClassをextendすることで、データを探すときの索引が作れるようです。
データが多くなれば、仕方ないことかもしれませんが、調整しないといけないことがどんどん増えていきます。

他のサイトでも指摘されていることですが、Gravは、基本的には、1000ページ以下の、小規模なサイトの運用に向いている気がします。
どうしてもGravで大量のデータが扱いたい場合のみ、Flex Objectsを使うのがよいと思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?