Ibexa DXP の初心者向けチュートリアル

# 初心者向けチュートリアル
このチュートリアルは、Ibexa DXP で Web サイトを構築するためのステップバイステップガイドです。

## 初心者向けチュートリアル
### 想定される読者
このチュートリアルは、 Ibexa DXP の使用経験がほとんどないユーザーを対象としています。

以下の点が必要です。
* HTMLとCSSの基本的な知識を持っている。
* 選択したデータベースの基本的な知識を持っている。

### 学習成果
このチュートリアルを終了すると、次のことができるようになります。
* Web サイトのコンテンツモデルの構築方法がわかる。
* テンプレートを使って、ニーズに合わせてコンテンツを表示することができる。
* ユーザーと権限の管理方法がわかる。

### シナリオ
このチュートリアルでは、自転車の乗り心地を記録し、共有するための Web サイトを構築します。ユーザーがルートの情報や写真を追加し、旅行中にどのような興味深いポイントを訪れることができるかを示すことができるようになります。

### ステップ
このチュートリアルでは、次の手順を実行します。
1. 準備する
2. コンテンツモデルの作成
3. フロントページのカスタマイズ
4. コンテンツの表示
5. コンテンツの一覧表示
6. 設定の改善
7. コンテンツの埋め込み
8. アカウント登録の有効化


## ステップ1 — Get ready
チュートリアルを始めるには Ibexa DXP のクリーンインストールが必要です。

「[Ibexa DXP のインストール手順](https://qiita.com/bezeklik/items/8dee078a4d8ed2cc7f22)」にしたがって対応してください。 Web サーバー、リレーショナルデータベース、PHP が必要です。

クリーンインストールには、ウェルカムページを表示するルートコンテンツアイテムのみが含まれています。
![ウェルカムページ](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_homepage_install_clean.png)

ステップ3でウェルカムページを独自のものに置き換えます。

とりあえず削除するには、`config/packages/` に移動して `ibexa_welcome_page.yaml` ファイルを削除してください。

これで、コンテンツモデルの作成に取りかかることができます。

## ステップ 2 — コンテンツモデルの作成
Ibexa DXP プロジェクトでは、コンテンツをどのように構造化するかは非常に重要なポイントになります。これは、アプリケーションのデータベース設計と考えることができます。

完全な情報を得るには、コンテンツモデルのドキュメントページをお読みください。以下は、このチュートリアルに必要な点のみをカバーした短い紹介文です。

### コンテンツモデルの概要
Ibexa DXP コンテンツ リポジトリは、**コンテンツアイテム**を中心に構成されています。コンテンツアイテムは、記事、製品レビュー、場所など、1つのコンテンツのピースです。

すべてのコンテンツアイテムは、**コンテンツタイプ**のインスタンスです。コンテンツタイプは、各コンテンツアイテムにどのような**フィールド**が含まれるかを定義します。例えば、記事には、タイトル、画像、要約、記事本文、出版日、著者リストなどのフィールドが含まれます。

フィールドは、インストールされている**フィールドタイプ**のいずれかに属することができ、デフォルトのディストリビューションでは約30種類が用意されています。各フィールドタイプは、テキスト行、リッチテキストのブロック、画像、コンテンツアイテムとの関係のコレクションなど、特定のタイプのデータを表現するために構築されています。完全なリストは、[フィールドタイプのリファレンス](https://doc.ibexa.co/en/latest/api/field_type_reference/)セクションを参照してください。各フィールドタイプは独自のオプションを持つことができ、独自の編集および表示インターフェイスが付属しています。

### コンテンツタイプの作成
このサイトでは2種類のコンテンツタイプを使用します。 「**Ride**」と「**Landmark**」です。「Ride」は自転車旅行のルートです。途中の面白い場所である「Landmark」を1つ以上含むことができます。複数の「Ride」が同じ「Landmark」を訪れることができるので、データベースにおける N-N リレーションシップモデルに似ています。

このステップでは、最初のコンテンツタイプである「Ride」を作成します。

管理画面（`<yourdomain>/admin`）に移動し、デフォルトのユーザー名： `admin` 、デフォルトのパスワード： `publish` でログインします。

左サイドメニューから「**コンテンツタイプ**」を選択します。

**コンテンツタイプグループ**のリストが表示されます。これらは、コンテンツタイプを論理的な方法でグループ化するために使用されます。

**[Content]** を選択し、 **[＋ Add new]** ボタンをクリックします。

![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_create_content_type.png)

この基本情報をフォームに入力してください。
* 名前: `Ride`
* 識別子: `ride`

次に、以下の内容ですべてのフィールドを作成します。

|Field Type		|Name			|Identifier			|Required	|Searchable	|Translatable	|
|---			|---			|---				|---		|---		|---			|
|Text line		|名前			|`name`				|☑		|☑		|☑			|
|Image Asset	|写真			|`photo`			|☐			|☐			|☐				|
|Rich text		|説明			|`description`		|☑		|☑		|☑			|
|Map location	|開始位置		|`starting_point`	|☑		|☑		|☐				|
|Map location	|終了位置		|`ending_point`		|☑		|☑		|☐				|
|整数			|長さ			|`length`			|☑		|☑		|☐				|

入力が終わったら「新規作成」ボタンをクリックします。

### 「Rides」の作成
:::note
**備考**
Ibexa Experience を使用している場合、インストールのルートコンテンツ項目は「Ibexa Digital Experience Platform」と呼ばれるページです。

このチュートリアルでは、その子である「Ibexa Platform」というフォルダと入れ替えます。

「Ibexa Digital Experience Platform」に移動し、**[ロケーション]** タブを選択して **[Swap Locations]** セクションで [Ibexa Platform] に移動します。

ページの操作方法については、[別のチュートリアル](https://doc.ibexa.co/en/latest/tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world/)で紹介されています。
:::

左メニューの「コンテンツ構成」を選択して、コンテンツに戻ります。次に、コンテンツツリーを参照し、画面右上の **[＋ Create content]** ボタンを使用して、「*All Rides*」という名前のフォルダを作成します。フォルダを公開します。

フォルダ内で、コンテンツ作成ボタンを使っていくつかの「Rides」を作成し、写真を追加して公開します。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_all_rides_admin.png)

フォルダに2つ以上の「Rides」があれば、Web サイトのトップページをカスタマイズする準備が整います。

## ステップ3 — フロントページのカスタマイズ
このステップでは、サイトのグローバルレイアウトを作成し、カスタムテンプレートを使用してコンテンツを表示します。

まず、サイトのルート（`<yourdomain>`）に移動してください。すると、何のレイアウトもない、クリーンインストールのホームページが表示されるはずです。このステップでは、このコンテンツアイテムをレンダリングするためにカスタムテンプレートを使用するようにプラットフォームに指示することで、カスタマイズします。

### コンテンツレンダリングの設定
ルートコンテンツをレンダリングする際にカスタムテンプレートを使用するには、 `ibexa` の `content_view` 設定ブロックを作成します。

:::note
**備考**
YAML コードを貼り付けるときは、インデントとレベルに注意してください。ここで示されるコードブロックは YAML ファイルの完全な構造を含むので、新しいブロックをどこに設置すればよいのか学ぶのに役立ちます。YAML は既存のキーを重複させないように注意してください。
:::

`config/packages/ibexa.yaml` を編集します。インデント（字下げ）に注意しながら、 `site` の下に以下のブロックを追加します - `content_view` は `site` の一階層下にあるべきです。

```yaml:config/packages/ibexa.yaml
ibexa:
    system:
        site:
            content_view:
                full:
                    home_page:
                        template: full/home_page.html.twig
                        match:
                            Id\Location: 2
```
これは、「Location ID」が `2` のコンテンツをレンダリングするときに `template` を使用するよう Ibexa DXP に指示します。 `2` はルートコンテンツ項目のデフォルトの場所です。

`Id\Location` は、さまざまな基準に応じてレンダリングをカスタマイズするために使用できる、いくつかの[ビューマッチャー](https://doc.ibexa.co/en/latest/guide/content_rendering/templates/view_matcher_reference/)のうちの1つです。

:::note
**キャッシュのクリア**
YAML ファイルを変更するたびに、キャッシュをクリアする必要があります。開発環境では必須ではありません。

キャッシュをクリアするには:
```shell
php bin/console cache:clear
```
:::

### テンプレートとレイアウトの作成
#### 最初のテンプレートの作成
次に、設定で指定したテンプレートを作成する必要があります。

さしあたりテンプレートにお約束の「Hello world」メッセージを記入します。

`templates/full/` に `home_page.html.twig` ファイルを作成します。
```twig:templates/full/home_page.html.twig
<div>
    <h1>Hello World!</h1>
</div>
```

ページを更新すると、シンプルでスタイルにこだわらないメッセージが表示されます。

:::note
**備考**
それでもまだウェルカムページが表示される場合は、`config/packages/` に移動して、`ibexa_welcome_page.yaml` ファイルが削除されていることを確認してください。
:::

#### メインレイアウトの追加
多くのサイトでは、ロゴ入りヘッダーやフッターなど、一般的なレイアウトが決まっています。これはすべてのページに表示され、ページのコンテンツはその中に配置されます。

このようなテンプレートをサイトに追加するには、 `templates/` に `main_layout.html.twig` ファイルを作成し、以下のコードを貼り付けてください。

```twig:templates/main_layout.html.twig
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Ibexa DXP beginner tutorial">

    <title>Ibexa DXP Beginner Tutorial</title>

    <script src="{{ asset('bundles/ibexaadminuiassets/vendors/jquery/dist/jquery.min.js') }}"></script>

    {{ encore_entry_link_tags('tutorial') }}

    <link href='http://fonts.googleapis.com/css?family=Oswald' rel='stylesheet' type='text/css'>
    <link href='http://fonts.googleapis.com/css?family=Roboto+Slab' rel='stylesheet' type='text/css'>
    <link href='http://fonts.googleapis.com/css?family=Fjalla+One' rel='stylesheet' type='text/css'>
</head>

<body>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-fixed-top" id="total-navbar">
    <div class="container">
        <!-- Brand and toggle -->
        <div class="navbar-header page-scroll">
            <a class="navbar-brand" href="/">
                <img alt="Brand" src="{{ asset('../assets/images/logo.png') }}">
            </a>
        </div>
    </div>
</nav>

<!-- Header -->
<header class="below-navbar">
    <div class="container">
        <div class="row" id="banner">
            <div class="col-xs-12">
                <div class="banner-header-block">
                    <img alt="Go Bike! logo" src="{{ asset('../assets/images/wordmark.png') }}" style="width:100%" />
                    <h3 class="banner-header">{{ 'Discover new rides! Go Bike!'|trans }}</h3>
                </div>
            </div>
        </div>
    </div>
</header>

<section>
    <div class="container">
        <div class="row regular-content-size">
            <div class="col-xs-12 box-style">
                {% block content %}
                {% endblock %}
            </div>
        </div>
    </div>
</section>

<!-- Footer -->
<footer class="text-center">
    <div class="footer-color-one">
    </div>
    <div class="footer-color-two">
        <div class="container">
            <div class="row regular-content-size">
                <div class="col-xs-8 col-xs-offset-2 footer-links-block">
                    <ul class="list-inline footer-links">
                        <li><a href="#">{{ 'About Us'|trans }}</a></li>
                        <li><a href="/">{{ 'All Rides'|trans }}</a></li>
                        <li><a href="#">{{ 'Submit a Ride'|trans }}</a></li>
                        <li><a href="#">{{ 'My Account'|trans }}</a></li>
                        <li><a href="#">{{ 'FAQ'|trans }}</a></li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
    <div class="footer-color-three">
        <div class="container">
            <div class="row">
                <div class="col-xs-12">
                    <p class="small">Website design &#38; content © copyright {{ "now"|date("Y") }} Ibexa. This website was made with Ibexa DXP.</p>
                </div>
            </div>
        </div>
    </div>
</footer>

{{ encore_entry_script_tags('tutorial-js') }}

</body>
</html>
```
12行目と89行目で、テンプレートが [Symfony Webpack Encore](http://symfony.com/doc/5.4/frontend.html#webpack-encore) を利用していることに注意してください。このチュートリアルでは、Webpack の設定方法を説明しますが、まずアセットが必要です。

#### アセットの追加
このサイトには、まだスタイルシートやアセットがありません。用意されたアセットファイルが含まれる [assets.zip](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/assets.zip) をダウンロードする必要があります。

そして、その中身を以下のディレクトリに解凍してください。
* `css`, `fonts`, `js` フォルダを `assets/` に
* `images` フォルダを `public/assets/` に

先に進む前に、追加したファイルの構成が以下のようになっていることを確認してください。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_listing_web_v3.png)

#### Webpack の設定
Ibexa DXP では、[Symfony Webpack Encore](http://symfony.com/doc/5.4/frontend.html#webpack-encore) - Webpack の統合により、CSS スタイルシートと JS スクリプトのバンドルを構築し、プロジェクトに追加することが可能です。詳細については、[バンドルからアセットをインポートする](https://doc.ibexa.co/en/latest/guide/project_organization/#assets)を参照してください。

バンドルを作成するには、まず、どのファイルをバンドルに含めるかを指定します。

プロジェクトのルートフォルダにある `webpack.config.js` ファイルを開きます。 `Encore.addEntry('app', './assets/app.js');` のすぐ下に、以下のコードを貼り付けます。

```js:webpack.config.js
Encore
    .addStyleEntry('tutorial', [
        path.resolve(__dirname, './assets/css/normalize.css'),
        path.resolve(__dirname, './assets/css/bootstrap.min.css'),
        path.resolve(__dirname, './assets/css/bootstrap-theme.css'),
        path.resolve(__dirname, './assets/css/style.css')
    ])
    .addEntry('tutorial-js', [
        path.resolve(__dirname, './assets/js/bootstrap.min.js')
    ]);
```
`.addStyleEntry('tutorial', [])` と `.addEntry('tutorial-js', [])` は、それぞれ `main_layout.html.twig` の `{{ encore_entry_link_tags('tutorial') }}` と `{{ encore_entry_script_tags('tutorial-js') }}` に該当することに注意ください。この設定は、テンプレートに追加されるファイルからなるバンドルを作成します。

この時点でバンドルが作成され、すぐに使用できるようになります。


#### テンプレートの拡張
あとは、 `home_page.html.twig` テンプレートに、アセットを利用する `main_layout.html.twig` テンプレートを追加してください。

あるテンプレートを別のテンプレートに追加するには、 `templates/full/home_page.html.twig` を編集して、次のコードに置き換えます。
```twig:templates/full/home_page.html.twig
{% extends "main_layout.html.twig" %}

{% block content %}
    <div class="col-xs-10 col-xs-offset-1 text-justified">
        <h1>Hello World!</h1>
    </div>
{% endblock %}
```

テンプレート言語Twigは、テンプレートの継承をサポートしています。テンプレートは名前付きブロックを含むことができます。どのテンプレートも他のテンプレートを拡張することができ、その親によって定義されたブロックを変更することができます。

上のコードは、1行目で `main_layout.html.twig` を指しています。また、"Hello world "メッセージをコンテンツブロックにラップしています。メインレイアウトのテンプレートを見返すと、空の `{% block content %}{% endblock %}` セクションがあります（52〜53行目）。ここが `home_page.html.twig` がレンダリングされる場所です。

以下のコマンドを実行して、キャッシュをクリアし、アセットを再生成してください。
```shell
php bin/console cache:clear
php bin/console assets:install
yarn encore <dev|prod>
```

:::note warn
**Tip**
`yarn encore` コマンドは、使用している環境で実行する必要があります。

デフォルトでは、 Ibexa DXP は dev 環境にインストールされます。prod に変更した場合は、`yarn encore prod` を使用してください。
:::

ページを更新すると、スタイル付きレイアウトの中に「Hello world」が配置されているのが確認できるはずです。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_hello_world.png)
この時点では、テンプレートは静的です。リポジトリから動的なデータをレンダリングすることはありません。

## ステップ4 — コンテンツの表示
次のステップでは、ここにすべての乗り物のリストをレンダリングします。しかしその前に、既存のページレイアウトを使用して、単一の「Ride」のコンテンツをレンダリングすることができます。

### 「Ride」ビューの作成
以下のコードで Twig テンプレート `templates/full/ride.html.twig` を作成します。
```twig:templates/full/ride.html.twig
{% extends "main_layout.html.twig" %}
{% block content %}
<div class="col-xs-10 col-xs-offset-1 text-justified">
    <section>
        <div class="row regular-content-size">
            <div class="col-xs-12">
                <h3 class="center bottom-plus new-header">{{ content.name }}</h3>
            </div>
        </div>
    </section>
    <section>
        <div class="row regular-content-size">
            <div class="row">
                <div class="col-xs-6">
                    <h4 class="underscore">{{ 'Starting point'|trans }}</h4>
                    {{ ibexa_render_field(content, 'starting_point', {'parameters': { 'width': '100%', height: '200px', 'showMap': true, 'showInfo': false }}) }}
                </div>
                <div class="col-xs-6">
                    <h4 class="underscore">{{ 'Ending point'|trans }}</h4>
                    {{ ibexa_render_field(content, 'ending_point', {'parameters': { 'width': '100%', height: '200px', 'showMap': true, 'showInfo': false }}) }}
                </div>
            </div>
        </div>
    </section>
    <section>
        <div class="row regular-content-size">
            <div class="col-xs-12 padding-box">
                <div class="col-xs-2">
                    <div class="box-ride">
                        <p class="special-number">{{ ibexa_render_field( content, 'length') }} km</p>
                    </div>
                </div>
                <div class="col-xs-8">
                    <h4 class="underscore">{{ 'Description'|trans }}</h4>
                    {{ ibexa_render_field( content, 'description') }}
                </div>
            </div>
        </div>
    </section>
</div>
{% endblock %}
```

このテンプレートは `main_layout.html.twig` を再利用し、再びテンプレートをコンテンツブロックに配置します。

:::note
**変数のプレビュー**
現在のテンプレートで利用可能な変数は、Twig 関数の `dump()` で確認することができます。
```twig
{{ dump() }}
```
また、特定の変数をダンプすることも可能です。
```twig
{{ dump(location) }}
```
:::

次に、このテンプレートがいつ使われるべきかを示す必要があります。

`config/packages/ibexa.yaml` に戻り、以下の設定を追加します（既存の `content_view` の `full` キーの下に :）。
```yaml:config/packages/ibexa.yaml
site:
    content_view:
        full:
            # existing keys, do not change them
            ride:
                template: full/ride.html.twig
                match:
                    Identifier\ContentType: ride
```

### 「Ride」 full ビューの確認
フロントページにはまだライドのリストがないため、ライドをクリックするだけでプレビューすることはできません。しかし、2つの方法でテンプレートがどのように機能するかを確認することができます。

#### 管理画面でのプレビュー
管理画面での編集中にプレビューを使用し、コンテンツがどのように表示されるかを全画面で確認することができます。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_preview_full_ride.png)

#### 「Ride」ページの表示
「Ride」の URL に直接アクセスすることもできます。

「All Rides」フォルダにある「Ride」コンテンツの URL は `http://<yourdomain>/all-rides/<ride-name>` です。

## ステップ5 — コンテンツの一覧表示
1 つのコンテンツを表示する方法がわかったので、次はコンテンツのリストを表示する方法を説明します。

このステップでは、フロントページにすべての乗り物のテーブルを表示します。

### トップページテンプレートのカスタマイズ
`templates/full/home_page.html.twig` で、"Hello world" を既存のすべての「Rides」の一覧を表示するテーブルと置き換えます。
```twig:templates/full/home_page.html.twig
{% extends "main_layout.html.twig" %}

{% block content %}
    <div class="col-xs-10 col-xs-offset-1 text-justified">
        <table class="table table-hover">
            <thead>
            <tr class="table-header">
                <th>{{ 'Ride'|trans }}</th>
                <th>{{ 'From'|trans }}</th>
                <th>{{ 'To'|trans }}</th>
                <th>{{ 'Distance'|trans }}</th>
            </tr>
            </thead>
            <tbody>
            {% for ride in rides.currentPageResults %}
                {{ render( controller( 'ibexa_content::viewAction', { 'location': ride.valueObject, 'viewType': 'line' } )) }}
            {% endfor %}
            </tbody>
        </table>
        {% if rides.haveToPaginate() %}
            <div class="col-xs-12 text-center">
                <div class="pagerfanta pagination">
                    {{ pagerfanta(rides, 'ibexa') }}
                </div>
            </div>
        {% endif %}
    </div>
{% endblock %}
```

上記の15行目で使用する `rides` 変数には、すべての「Rides」の一覧が含まれている必要があります。この一覧を取得するために Query Type を使用します。

### トップページ用の QueryType の作成
QueryType オブジェクトは、Content Item クエリの結果を制限したり、ソートしたりするために使用されます。詳細は、「[組み込みの Query Type](https://doc.ibexa.co/en/latest/guide/controllers/#built-in-query-types)」を参照してください。

ここでは、公開されている（可視化されている） `ride` オブジェクトを表示する必要があります。 `src/QueryType` に `RideQueryType.php` ファイルを作成します。
```php:src/QueryType/RideQueryType.php
<?php

namespace App\QueryType;

use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Core\QueryType\QueryType;

class RideQueryType implements QueryType
{
    public static function getName()
    {
        return 'Ride';
    }

    public function getQuery(array $parameters = [])
    {
        return new LocationQuery([
            'filter' => new Criterion\LogicalAnd(
                [
                    new Criterion\Visibility(Criterion\Visibility::VISIBLE),
                    new Criterion\ContentTypeIdentifier(['ride']),
                ]
            )
        ]);
    }

    public function getSupportedParameters()
    {
    }
}
```

この Query Type は、 `ride` コンテンツタイプに属するすべての可視コンテンツアイテムを検索します（21～22行目）。

ここで、この Query Type が設定に使用されることを示す必要があります。

### Query Type の設定への追加
`config/packages/ibexa.yaml` を編集します。ホームページのビュー設定で、このビューがクエリタイプを使用することを示します。
```yaml:config/packages/ibexa.yaml
site:
    content_view:
        full:
            # existing keys, do not change them
            home_page:
                controller: ibexa_query::pagingQueryAction
                template: full/home_page.html.twig
                match:
                    Id\Location: 2
                params:
                    query:
                        query_type: Ride
                        limit: 4
                        assign_results_to: rides
```
12行目の `query_type` パラメータは、どの Query Type を使用するかを示しています。Query Type ファイルの `Ride` という名前は `getName` メソッドで定義しました。

組み込みの `ibexa_query` コントローラの `pagingQueryAction` を使用すると（6行目）、ページネーションされた結果を自動的に取得することができます。 `limit` パラメータで1ページあたりの結果の上限を設定することができます。

#### View types
これまで、ライドの全景を表示するために `full` ビュータイプを使用してきました。一方ここでは、ホームページのテンプレートで `'viewType': 'line'` と表示されているように `line` ビューを使用しています（16行目）。

カスタムビュータイプは、設定に含める限り好きな名前で設定することができます。では、「Rides」の `line` ビューでやってみましょう。

### 「Rides」の line テンプレートの作成
`config/packages/ibexa.yaml` ファイルに `ride` テンプレートのルールを追加します。`line` は `full` と同じレベルである必要があります。
```yaml:config/packages/ibexa.yaml
system:
    site:
        content_view:
            line:
                ride:
                    template: line/rides.html.twig
                    match:
                        Identifier\ContentType: ride
```

`templates/line/rides.html.twig` テンプレートを作成します。

このテンプレートはテーブルの中に表示されるため `<tr>` タグで始まります。

```twig:templates/line/rides.html.twig
<tr>
    <td>
        <a href="{{ path( "ibexa.url.alias", { 'locationId': content.contentInfo.mainLocationId } ) }}"
           target="_self">
            <strong>
                {{ content.name }}
            </strong>
            {% if not ibexa_field_is_empty( content, 'photo' ) %}
                {{ ibexa_render_field(content, 'photo') }}
            {% endif %}
        </a>
    </td>
    <td>
        {{ ibexa_render_field(content, 'starting_point', {'parameters': {'width': '100%', 'height': '100px', 'showMap': true, 'showInfo': true }}) }}
    </td>
    <td>
        {{ ibexa_render_field(content, 'ending_point', {'parameters': {'width': '100%', 'height': '100px', 'showMap': true, 'showInfo': true }}) }}
    </td>
    <td>
        <p>{{ ibexa_render_field( content, 'length' ) }} Km</p>
    </td>
</tr>
```

さて、あなたのホームページのトップページに行くと、乗り物のリストが表示されます。しかし、ライドの写真が大きすぎて、テーブルを引き伸ばしています。次のステップでは、それらが適切な大きさで表示されていることを確認します。

## ステップ6 — 設定の改善
### 画像バリエーションの定義

画像バリエーションは、同じ画像の異なるバージョンです。画像の拡大縮小、切り取り、エフェクトの追加などに利用できます。

ここまでは「Ride」一覧の画像を自動的にテンプレートに当てはめたものですが、その結果はあまり良いものではありません。今度は、バリエーションを作成して画像の見え方を細かく指定します。

以下を含む `config/packages/image_variations.yaml` ファイルを新規に作成します。
```yaml:config/packages/image_variations.yaml
ibexa:
    system:
        default:
            image_variations:
                ride_list:
                    reference: null
                    filters:
                        - {name: geometry/scaledownonly, params: [140, 100]}
```

次に、これらのバリエーションを使用するようにテンプレートを変更します。バリエーション名は、画像コンテンツをレンダリングする際のパラメータとして提供されます。

`templates/line/rides.html.twig` で、8～10行目に以下のように `'alias': 'ride_list'` パラメータを追加してください。

```twig:templates/line/rides.html.twig
{% if not ibexa_field_is_empty( content, 'photo' ) %}
    {{ ibexa_render_field(content, 'photo', {
        'parameters': {
            'alias': 'ride_list'
        }
    }) }}
{% endif %}
```

これにより、一覧の各「Ride」の横に表示される写真は、比率を維持したまま適切に縮小されます。

キャッシュをクリアして、トップページを更新してください。写真が通常のサイズになり、表に収まるようになりました。

![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_ride_list.png)

### ビュー設定の分離
大規模なサイトでは、設定が必要な要素がたくさんあります。それをより整理するために、設定の一部を別のファイルに分割することができます。

例として、すべてのコンテンツビューの設定を独自のファイルに分離することができます。`config/packages/views.yaml` ファイルを作成します。 `config/packages/ibexa.yaml` から `content_view` 以下をすべてコピーして、新しいファイルに移動します。 `ibexa.yaml` から対応するコードを削除します。

`views.yaml` ファイルは次のようになります。

```yaml:config/packages/views.yaml
ibexa:
    system:
        site:
            content_view:
                full:
                    home_page:
                        controller: ibexa_query::pagingQueryAction
                        template: full/home_page.html.twig
                        match:
                            Id\Location: 2
                        params:
                            query:
                                query_type: Ride
                                limit: 4
                                assign_results_to: rides
                    ride:
                        template: full/ride.html.twig
                        match:
                            Identifier\ContentType: ride
                line:
                    ride:
                        template: line/rides.html.twig
                        match:
                            Identifier\ContentType: ride
```

## ステップ7 — コンテンツの埋め込み
コンテンツタイプとそのアイテムの一覧や詳細ビューを作成するには、多くの場合、関連するリソースを読み込む必要があります。このステップでは、「Ride」のページに表示される関連オブジェクトである「Landmark」を追加します。

関連リソースはいくつでも追加することができます。

### 「Landmark」コンテンツタイプの作成
このサイトに必要な2つ目のコンテンツタイプである「Landmark」を作成する必要があります。

**[コンテンツタイプ]** に移動し、 **[コンテンツ]** グループで「Landmark」コンテンツタイプを作成します。

ランドマークは、ライドが通過する興味深い場所です。それぞれの「Ride」は複数の「Landmark」に関連することがあります。

* 名前: `Landmark`
* 識別子: `landmark`

次に、以下の情報を持つすべてのフィールドを作成します。
|Field Type		|Name	|Identifier		|Required	|Searchable	|Translatable|
|---			|---	|---			|:-:	|:-:	|:-:|
|Text line		|名前	|name			|☑	|☑	|☑|
|Rich text		|説明	|description	|☐	|☑	|☑|
|Image Asset	|写真	|photo			|☑	|☐	|☐|
|Map location	|場所	|location		|☑	|☑	|☐|

コンテンツタイプの作成を確認するため、「作成」を選択します。

「Landmarks」Folder を作成し、「Landmark」をいくつか追加します。なお、「Landmark」を表現するために、写真（「写真」フィールド用）が必要です。

### 「Ride」コンテンツタイプ定義への「Landmarks」の追加
次に、「Ride」コンテンツタイプを編集して、2つのコンテンツタイプの間に Multiple Content Relation を追加します。「Landmarks」という新しい「コンテンツ関係（複数）」フィールドを作成し、識別子として `landmark` を指定し、コンテンツタイプ「Landmark」を追加できるようにします。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_ride_adding_landmarks_to_the_ride_content_type.png)

**[保存]** をクリックして確認します。

既存の「Ride」に戻り、編集し、「Landmark」をリンクさせます。 **[公開]** をクリックします。

### 「Ride」ビューの「Landmarks」一覧の表示
####  「Landmark」の line ビューの作成
「Landmark」用の `line` ビューを作成します。

`config/packages/views.yaml` で新しいオーバーライドルールを宣言します。
```yaml:config/packages/views.yaml
ibexa:
    system:
        site:
            content_view:
                #full views here
                line:
                    landmark:
                        template: line/landmark.html.twig
                        match:
                            Identifier\ContentType: landmark
```

`templates/line/landmark.html.twig` を作成し、「Landmark」の `line` ビューのテンプレートを追加します。
```twig:templates/line/landmark.html.twig
<section>
    <div class="col-xs-4 photos-box">
        <a href="#bikeModal{{ content.id }}" data-toggle="modal">
            {{ ibexa_render_field( content, 'photo', { parameters: { 'alias': 'landmark_list', 'class': 'img-rounded'}}) }}
        </a>
    </div>


    <div class="bike-modal modal fade" id="bikeModal{{ content.id }}" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-content">
            <div class="close-modal" data-dismiss="modal">
                <div class="lr">
                    <div class="rl">
                    </div>
                </div>
            </div>
            <div class="container">
                <div class="row">
                    <div class="col-xs-8 col-xs-offset-2">
                        <div class="modal-body text-center">
                            <h2>{{ content.name }}</h2>
                            <hr class="featurette-divider">
                            {{ ibexa_render_field( content, 'photo', { parameters: { 'alias': 'large'}, attr: { 'class': 'img-responsive img-rounded' }}) }}
                            {{ ibexa_render_field( content, 'description', { attr: { 'class': 'padding-box text-justify' }}) }}
                            {{ ibexa_render_field( content, 'location', { parameters: {'width': '100%', 'height': '250px', 'showMap': true, 'showInfo': false }}) }}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>
```

前回同様、ここ（4行目）で画像バリエーションを使うので、それを設定する必要があります。`config/packages/image_variations.yaml` に `ride_list` と同じ階層に以下のセクションを追加します。

```yaml:config/packages/image_variations.yaml
landmark_list:
    reference: null
    filters:
        - {name: geometry/scalewidth, params: [200]}
```

#### RideController の作成
「Ride」オブジェクトを表示する際に、追加で情報を提供する必要があります。これには、カスタムコントローラを作成する必要があります。コントローラは `ContentService` を使用して、特定の「Ride」の関連リソース（Landmark）をロードします。

`src/Controller/RideController.php` ファイルを作成します。
```php:src/Controller/RideController.php
<?php

namespace App\Controller;

use Ibexa\Bundle\Core\Controller;
use Ibexa\Core\MVC\Symfony\View\ContentView;
use Ibexa\Contracts\Core\Repository\ContentService;

class RideController extends Controller
{
    private $contentService;

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

    public function viewRideWithLandmarksAction(ContentView $view)
    {
        $currentContent = $view->getContent();
        $landmarksListId = $currentContent->getFieldValue('landmarks');
        $landmarksList = [];

        foreach ($landmarksListId->destinationContentIds as $landmarkId) {
            $landmarksList[$landmarkId] = $this->contentService->loadContent($landmarkId);
        }

        $view->addParameters(['landmarksList' => $landmarksList]);

        return $view;
    }
}
```

`config/packages/views.yaml` を更新して、 `RideController.php` に言及するように、ビュー設定に `controller` キーを含む行を追加します。

```yaml:config/packages/views.yaml
ibexa:
    system:
        site:
            content_view:
                full:
                    ride:
                        template: full/ride.html.twig
                        controller: App\Controller\RideController::viewRideWithLandmarksAction
                        match:
                            Identifier\ContentType: ride
```

#### 「Ride」の full ビューでの「ランドマーク」の追加
次に、「Ride」の `full` ビューテンプレートを変更し、「Landmark」の一覧と、先ほど作成したコントローラを追加します。`templates/full/ride.html.twig` の最後の `</div>` と終了タグ `{% endblock %}` の前に、以下の行を追加してください。
```twig:templates/full/ride.html.twig
{% if landmarksList is not empty %}
    <div class="row regular-content-size">
        <section class="photos">
            <div class="col-xs-12">
                <h4 class="underscore">{{ 'Landmarks'|trans }}</h4>
            </div>
            {% for landmark in landmarksList %}
                {{ render( controller( "ibexa_content::viewAction", { 'content': landmark, 'viewType': 'line'} )) }}
            {% endfor %}
        </section>
    </div>
{% endif %}
```

これで再び「Ride」ページを確認すると、接続されているすべての「Landmarks」が表示されるようになりました。

:::note
**ヒント**
Twig テンプレートで `dump()` を使用すると、利用可能なすべての変数を表示することができます。
:::

![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/bike_tutorial_ride_with_landmarks.png)

## ステップ8 — アカウント登録の有効化
このステップでは、他のユーザーがサイトにアカウントを作成し、管理画面にアクセスしてコンテンツを作成できるようにします。

### ユーザー登録の有効化
左メニューの **Admin** （歯車のアイコン）から、「**ロール**」の管理画面に移動し、 **Anonymous** ロールをクリックします。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/step_8_role_mgmt_screen.png)

「Anonymous User」に `User/Register` ポリシーを追加します。これにより Web サイトへの訪問者であれば誰でも登録フォームにアクセスできるようになります。

![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/step8_admin_anonymous_policies.png)

その後、 `<yourdomain>/register` にアクセスします。登録フォームはスタイルなしなので、テンプレートを追加する必要があります。

### 登録フォームのカスタマイズ
`config/packages/views.yaml` ファイルに、`content_view` と同じレベルの `site` の下に `user_registration` キーを追加してください。
```yaml:config/packages/views.yaml
ibexa:
    system:
        site:
            # existing content_view keys
            user_registration:
                templates:
                    form: user/registration_form.html.twig
```

これは、登録フォームのレンダリングに使用されるテンプレートを示します。

`templates/user/registration_form.html.twig` を作成します。

```twig:templates/user/registration_form.html.twig
{% extends "main_layout.html.twig" %}

{% block page_head %}
    {% set title = 'Register user'|trans %}

    {{ parent() }}
{% endblock %}

{% block content %}
    {% import 'user/registration_content_form.html.twig' as registrationForm %}

    <div class="container">
        <section class="user-form col-md-6 col-md-offset-3">
            <h2>{{ 'Member Registration'|trans }}</h2>
            <div class="legend">* {{ 'All fields are required'|trans }}</div>

            {{ registrationForm.display_form(form) }}
        </section>
    </div>
{% endblock %}
```

10行目では `registration_content_form.html.twig` という別のファイルがインポートされていることがわかります。2つ目のテンプレートは、登録フォームの実際のフィールドをレンダリングするものです。このファイルも作成します (`templates/user/registration_content_form.html.twig` とします)。

```twig:templates/user/registration_content_form.html.twig
{% macro display_form(form) %}
    {{ form_start(form) }}

    {% for fieldForm in form.fieldsData %}
        {% set fieldIdentifier = fieldForm.vars.data.fieldDefinition.identifier %}

        {% if fieldIdentifier == 'first_name' or fieldIdentifier == 'last_name' %}
            {% if fieldIdentifier == 'first_name' %}
                <div class="row">
            {% endif %}
            <div class="col-md-6">
                <div class="field-name">
                    <label class="required">{{ fieldForm.children.value.vars.label }}:</label>
                </div>
                {{ form_errors(fieldForm.value) }}
                {{ form_widget(fieldForm.value, {
                    'contentData': form.vars.data
                }) }}
            </div>
            {% if fieldIdentifier == 'last_name' %}
                </div>
            {% endif %}
        {% endif %}

        {% if fieldIdentifier == 'user_account' %}
            <div class="row">
                <div class="col-md-6">
                    {{ form_widget(fieldForm.value, {
                        'contentData': form.vars.data
                    }) }}
                </div>
            </div>
        {% endif %}

        {%- do fieldForm.setRendered() -%}
    {% endfor %}

    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            {{ form_widget(form.register, {'attr': {
                'class': 'btn btn-block btn-primary'
            }}) }}
        </div>
    </div>

    {{ form_end(form) }}
{% endmacro %}
```

3つ目に用意するテンプレートは、ユーザーが登録を完了したときに表示される確認ページです。まず、コンフィギュレーションで新しいテンプレートを指定します。`config/packages/views.yaml` に `confirmation` キーを追加します。

```yaml:config/packages/views.yaml
user_registration:
    templates:
        form: user/registration_form.html.twig
        confirmation: user/registration_confirmation.html.twig
```

次に `templates/user/registration_confirmation.html.twig` テンプレートを作成します。

```twig:templates/user/registration_confirmation.html.twig
{% extends "main_layout.html.twig"  %}

{% block page_head %}
  {% set title = 'Registration complete'|trans %}

  {{ parent() }}
{% endblock %}

{% block content %}
    <div class="container">
        <section class="user-form-confirmation col-md-4 col-md-offset-4">
            <h2>{{ 'Registration completed'|trans }}</h2>

            <div class="row confirmation-label">
                {{ 'You are all set up and ready to go'|trans }}
            </div>

            <div class="row">
                <div class="col-md-4 col-md-offset-4">
                    <button type="button" class="btn btn-block btn-primary" onclick="window.location='{{ path('login') }}';">{{ 'Log in'|trans }}</button>
                </div>
            </div>
        </section>
    </div>
{% endblock %}
```

ここで、 `<yourdomain>/register` に戻ります。
![](https://doc.ibexa.co/en/latest/tutorials/platform_beginner/img/step8_register_page.png)
フォームに必要事項を入力し、ユーザーを登録します。

:::note
**ヒント**
この時点で新しいユーザーでログインした場合、管理画面（`<yourdomain>/admin`）でログアウトし、再度 Admin でログインし直す必要があります。
:::

### 権限の設定
登録フォームから作成されたユーザーは、「Guest accounts」ユーザーグループに所属します。作成されたユーザーは、このグループに割り当てられたロールを持つことになります。

:::note
**ヒント**
新しいユーザーを配置するグループを変更することができます（ただし、このチュートリアルでは変更する必要はありません）。詳しくは、「[新規ユーザーの登録](https://doc.ibexa.co/en/latest/guide/user_management/user_management/#registering-new-users)」を参照してください。
:::

この時点では、登録した誰もが Web サイトにコンテンツを追加できるようにする必要はありません。そのため、追加の権限を持つ新しいユーザーグループを作成します。管理者が新しいユーザーを受け入れると、この新しいグループに移動させることができます。

#### ユーザーグループの作成
Admin のユーザー画面で、 **[＋ Create content]** ボタンをクリックし、「`Go Bike Members`」というユーザーグループを作成します。

#### 「Rids」のためのフォルダの作成
`All Rides` フォルダに移動し、その中に `Member Rides` という名前の新しいフォルダを作成します。「Go Bike Members」はこのフォルダにのみコンテンツを作成することができます。

#### 「Go Bike Members」の権限の設定
ロール画面の Admin から、「Contributors」という名前の新しいロールを作成します。

ここで、「Contributors Role」に以下のポリシーを追加します。
* `User/Login`
* `User/Password`
* `Content/Read`
* `Content/Versionread`
* `Content/Create` with Limitations: Content Type limited to `Ride` and `Landmark` Content Types and Subtree to the `Member Rides`
* `Content/Publish` with Limitations: Content Type limited to `Ride` and `Landmark` Content Types and Subtree to the `Member Rides`
* `Content/Edit` with Limitation: Owner limited to `Self`
* `Section/View`
* `Content/Reverserelatedlist`

:::note
**ヒント**
制限機能は、ユーザーの権限管理を細かく設定するための強力なツールです。[技術的な詳細の制限に関するドキュメント](https://doc.ibexa.co/en/latest/guide/limitation_reference/#content-type-group-limitation)を参照してください。
:::

ポリシーが設定できたら、「Assignments」タブで、ユーザーグループ「Go Bike Members」にロールを割り当てます。

次に、ユーザーページに移動します。先ほど作成したユーザーを選択し、「Go Bike Members」のユーザーグループに移動させます。

#### 「Go Bike Member」でのコンテンツの作成
管理者としてログアウトし、新しいユーザーの資格情報で管理画面に再ログインします。これで、選択したフォルダに新しい「Ride」や「Landmark」を作成することができるようになりました。

### Congratulations!
これで Ibexa DXP で最初の Web サイトが作成できました。

**あなたは以下のことを学びました。**
* コンテンツモデルの作成
* Ibexa DXP プロジェクトのファイルの構成
* 異なるコンテンツタイプに対応したビューの設定
* Ibexa DXP プロジェクトへのアセットの追加
* Webpack Encore の使用と設定
* Twig テンプレートとコントローラを使ったコンテンツの表示
* ユーザー登録の有効化
* ユーザー権限の管理