配列の再セット例

## はじめに
テーブルから取得したデータを、配列に再セットする例を記載します。
Laravelを使用した例ですが、再セットは手動での調整を行います。


## コード例抜粋 
・ビューに渡すデータを、テーブルより取得したデータをセット
1.テーブルよりデータ取得
2.ビュー用にデータセットし直し
3.ビューで使用

1.テーブルよりデータ取得
```php
<?php
       // ユーザー10がフォローしている人たちのデータを取得
        $following = UserRelation::where('user_id', $userId)
                        ->get(['target_user_id', 'is_following', 'is_blocking']);
```
2.ビュー用にデータセットし直し
テーブルから取得した値(`$following`)を基に、配列(`$relations`)に再セット
```php
<?php

        $relations = [];
        // ユーザーがフォローしている人たちに関するデータを処理するループ
        foreach ($following as $follow) {
            // 初期化と同時にユーザーがフォローしている人たちの関係を設定
            $relations[$follow->target_user_id] = [
                'follow' => $follow->is_following,  // ユーザーが他のユーザーをフォローしているかどうか
                'follower' => false,  // この段階ではフォロワー情報は不明なのでfalse
                'mutual' => false,  // 相互フォローの情報もこの段階では不明なのでfalse
                'blocking' => $follow->is_blocking,  // ユーザーが他のユーザーをブロックしているかどうか
                'blocked_by' => false  // この段階では誰にブロックされているかは不明なのでfalse
            ];
        }
```
3.ビューで使用
以下のようにして使用
```html
        @foreach ($relations as $id => $relation)
            <tr>
                <td>{{ $id }}</td>
                <td>{{ $relation['follow'] ? '○' : '' }}</td>
                <td>{{ $relation['follower'] ? '○' : '' }}</td>
                <td>{{ $relation['mutual'] ? '○' : '' }}</td>
                <td>{{ $relation['blocking'] ? '○' : '' }}</td>
                <td>{{ $relation['blocked_by'] ? '○' : '' }}</td>
            </tr>
        @endforeach
```

<b>1,2で改善の余地があります。</b>
今回は手動で行っていますが、Laravelでの配列の加工はCollectionを使います。

## 詳細
以下は配列の再セット例を試すまでの環境構築手順例や、テーブルの詳細な意味等の記載となります。
<b>※以下sailコマンドの使用方法が違っています、読み飛ばしてください。</b>

### コード例

```app/Http/Controllers/UserRelations.php
    public function index()
    {
        $userId = 10; // 表示するユーザーIDを設定
    
        // ユーザー10がフォローしている人たちのデータを取得
        $following = UserRelation::where('user_id', $userId)
                        ->get(['target_user_id', 'is_following', 'is_blocking']);
    
        // ユーザー10をフォローしている人たちのデータを取得
        $followers = UserRelation::where('target_user_id', $userId)
                        ->get(['user_id', 'is_following', 'is_blocking']);
    
        $relations = [];
        // ユーザーがフォローしている人たちに関するデータを処理するループ
        foreach ($following as $follow) {
            // 初期化と同時にユーザーがフォローしている人たちの関係を設定
            $relations[$follow->target_user_id] = [
                'follow' => $follow->is_following,  // ユーザーが他のユーザーをフォローしているかどうか
                'follower' => false,  // この段階ではフォロワー情報は不明なのでfalse
                'mutual' => false,  // 相互フォローの情報もこの段階では不明なのでfalse
                'blocking' => $follow->is_blocking,  // ユーザーが他のユーザーをブロックしているかどうか
                'blocked_by' => false  // この段階では誰にブロックされているかは不明なのでfalse
            ];
        }

        // ユーザーをフォローしている人たちに関するデータを処理するループ
        foreach ($followers as $follower) {
            // フォロワー情報が$relationsにまだ設定されていない場合、初期設定を行う
            if (!isset($relations[$follower->user_id])) {
                $relations[$follower->user_id] = [
                    'follow' => false,  // フォロー情報はこの段階では不明なのでfalse
                    'follower' => false,  // 初期設定でfalseを指定（この後で真偽値が設定される）
                    'mutual' => false,  // 相互フォロー情報はこの段階では不明なのでfalse
                    'blocking' => false,  // ブロッキング情報はこの段階では不明なのでfalse
                    'blocked_by' => false  // ブロックされている情報はこの段階では不明なのでfalse
                ];
            }
            // フォロワーによるフォロワー情報設定
            $relations[$follower->user_id]['follower'] = $follower->is_following && !$follower->is_blocking;
             // $relations配列にフォロー情報があればそれを使い、なければfalseを設定
            $relations[$follower->user_id]['follow'] = $relations[$follower->user_id]['follow'] ?? false;
            // 互いにフォローし合っていれば相互フォローとしてtrueを設定
            $relations[$follower->user_id]['mutual'] = $relations[$follower->user_id]['follow'] && $follower->is_following;
            // フォロワーによるブロック情報を設定
            $relations[$follower->user_id]['blocked_by'] = $follower->is_blocking && !$follower->is_following;
        }

    
        // 配列をキー（ID）で昇順にソート
        ksort($relations);
    
        return view('user_relations.index', compact('relations'));
    }
```

```resources/views/user_relations/index.blade.php
<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Follow</th>
            <th>Follower</th>
            <th>Mutual</th>
            <th>Blocking</th>
            <th>Blocked By</th>
        </tr>
    </thead>
    <tbody>
        @foreach ($relations as $id => $relation)
            <tr>
                <td>{{ $id }}</td>
                <td>{{ $relation['follow'] ? '○' : '' }}</td>
                <td>{{ $relation['follower'] ? '○' : '' }}</td>
                <td>{{ $relation['mutual'] ? '○' : '' }}</td>
                <td>{{ $relation['blocking'] ? '○' : '' }}</td>
                <td>{{ $relation['blocked_by'] ? '○' : '' }}</td>
            </tr>
        @endforeach
    </tbody>
</table>


```

```routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserRelationController;

Route::resource('user-relations', UserRelationController::class);
```

```app/Models/UserRelations.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class UserRelation extends Model
{
    use HasFactory;
}

```

### データセット例
#### データ例
```sql
INSERT INTO user_relations (user_id, target_user_id, is_following, is_blocking) VALUES
(10, 11, 1, 0),  -- ユーザー10はユーザー11をフォロー
(12, 10, 1, 0),  -- ユーザー12はユーザー10をフォロー
(10, 13, 1, 0),  -- ユーザー10はユーザー13をフォロー（相互フォローの一部）
(13, 10, 1, 0),  -- ユーザー13はユーザー10をフォロー（相互フォローの一部）
(10, 14, 0, 1),  -- ユーザー10はユーザー14をブロック
(15, 10, 0, 1),  -- ユーザー15はユーザー10をブロック
(10, 16, 0, 1),  -- ユーザー10はユーザー16をブロック（双方ブロックの一部）
(16, 10, 0, 1),  -- ユーザー16はユーザー10をブロック（双方ブロックの一部）
(10, 17, 1, 0),  -- ユーザー10はユーザー17をフォロー
(17, 10, 0, 1),  -- ユーザー17はユーザー10をブロック
(10, 18, 0, 1),  -- ユーザー10はユーザー18をブロック
(18, 10, 1, 0);  -- ユーザー18はユーザー10をフォロー
```

#### テーブル内容

ユーザー関係テーブル (user_relations)
- `relation_id` (INT, PK): 関係の一意識別子
- `user_id` (INT, FK): アクションを起こすユーザーのID (`users.user_id`に対する外部キー)
- `target_user_id` (INT, FK): アクションの対象となるユーザーのID (`users.user_id`に対する外部キー)
- `is_following` (BOOLEAN): フォロー状態を示すフラグ
- `is_blocking` (BOOLEAN): ブロック状態を示すフラグ
- `created_at` (DATETIME): 関係が作成された日時
- `updated_at` (DATETIME): 関係が最後に更新された日時

```sql
CREATE TABLE IF NOT EXISTS user_relations (
    relation_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    target_user_id INT NOT NULL,
    is_following BOOLEAN NOT NULL,
    is_blocking BOOLEAN NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(user_id),
    FOREIGN KEY (target_user_id) REFERENCES users(user_id)
);
```	
今回だと以下のような表示になります。
![スクリーンショット 2024-05-03 171444.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/f0c8c85a-5ad0-2677-b26c-f934d083b115.png)



### 本環境
Laravel Sailそ使用して環境を作りました。
Laravel Sail を使用している場合、`php artisan` コマンドやマイグレーションなどの Laravel コマンドを実行するには、Laravel アプリケーションが動作している Docker コンテナにログインする必要があります。
以下の手順で Docker コンテナに入り、必要な操作を実行することができます：

**前準備**
コンテナを立ち上げます
./vendor/bin/sail up

1. **コンテナにログイン**:
   Terminal で次のコマンドを実行して、Laravel アプリケーションが動作しているコンテナにログインします。 

    ```bash
    docker exec -it example-app-laravel.test-1 /bin/bash
    ```

以下の場合、`sail-8.3/app` イメージを使用しているコンテナが該当します。コンテナ名は `example-app-laravel.test-1` です。
```bash
CONTAINER ID   IMAGE                         COMMAND                  CREATED        STATUS                    PORTS                                                      NAMES
09192b250321   sail-8.3/app                  "start-container"        18 hours ago   Up 18 hours               0.0.0.0:80->80/tcp, 0.0.0.0:5173->5173/tcp, 8000/tcp       example-app-laravel.test-1
cad6d45c7718   redis:alpine                  "docker-entrypoint.s…"   18 hours ago   Up 18 hours (healthy)     0.0.0.0:6379->6379/tcp                                     example-app-redis-1
04ca98ed2a51   selenium/standalone-chrome    "/opt/bin/entry_poin…"   18 hours ago   Up 18 hours               4444/tcp, 5900/tcp                                         example-app-selenium-1
cba284f6d0ef   mysql/mysql-server:8.0        "/entrypoint.sh mysq…"   18 hours ago   Up 18 hours (healthy)     33060-33061/tcp, 0.0.0.0:3307->3306/tcp                    example-app-mysql-1
c368a370c238   axllent/mailpit:latest        "/mailpit"               18 hours ago   Up 18 hours (healthy)     0.0.0.0:1025->1025/tcp, 0.0.0.0:8025->8025/tcp, 1110/tcp   example-app-mailpit-1
842122a5726a   getmeili/meilisearch:latest   "tini -- /bin/sh -c …"   18 hours ago   Up 18 hours (unhealthy)   0.0.0.0:7700->7700/tcp                                     example-app-meilisearch-1
```
  
２. **Laravel コマンドの実行**:
   コンテナ内でシェルが開かれたら、必要な Laravel コマンド（例えば `php artisan migrate` など）を実行できます。

３. **コンテナからのログアウト**:
   作業が完了したら、`exit` コマンドを使用してコンテナからログアウトします。

これで、Laravel Sail 環境内でデータベースのマイグレーションなどを行うことができます。

### テーブル作成
Laravelで新しいテーブルを作成するためには、マイグレーションを使用するのが一般的です。以下の手順に従って`user_relations`テーブルを作成します。

1. **マイグレーションファイルの作成**:
   Laravelプロジェクトのルートディレクトリでターミナルを開き、以下のコマンドを実行して新しいマイグレーションファイルを生成します。

   ```bash
   php artisan make:migration create_user_relations_table
   ```

   このコマンドは`database/migrations`ディレクトリに新しいマイグレーションファイルを作成します。ファイル名は日付と時間のプレフィックスが付いています。

2. **マイグレーションファイルの編集**:
   生成されたマイグレーションファイルを開き、`up`メソッドを編集してテーブル定義を追加します。以下のように編集します。

   ```php
   public function up()
   {
       Schema::create('user_relations', function (Blueprint $table) {
           $table->id('relation_id');
           $table->unsignedInteger('user_id');
           $table->unsignedInteger('target_user_id');
           $table->boolean('is_following');
           $table->boolean('is_blocking');
           $table->timestamps();
       });
   }
   ```

   ここで、`$table->id()`は主キーである`relation_id`を自動増分IDとして設定します。`$table->unsignedInteger()`は外部キー用の符号なし整数型を定義しています。`$table->boolean()`は真偽値のカラムを追加します。また、`$table->timestamps()`は`created_at`と`updated_at`のタイムスタンプを自動的に追加します。

3. **マイグレーションの実行**:
   マイグレーションファイルが準備できたら、以下のコマンドを実行してデータベースにテーブルを作成します。

   ```bash
   php artisan migrate
   ```

   このコマンドはマイグレーションをデータベースに適用し、`user_relations`テーブルが作成されます。

これで`user_relations`テーブルがLaravelアプリケーションのデータベースに正しく作成され、使用準備が整いました。次にこのテーブルを使用するためのモデルやコントローラーの実装に進めます。


### テーブル作成確認
Laravel でテーブルが正しく作成されたかどうかを確認するには、いくつかの方法があります。以下に主な方法をいくつか紹介します：

#### 1. Laravel Tinker を使用する
Laravel Tinker はコマンドラインから Laravel アプリケーションと対話するための強力なツールです。これを使ってデータベースにテーブルが存在するかどうかを確認できます。

1. コマンドラインで以下のコマンドを実行して Tinker を開始します：
   ```bash
   php artisan tinker
   ```

2. Tinker が開いたら、次のコマンドを実行してテーブルの存在を確認します：
   ```php
   Schema::hasTable('user_relations');
   ```

   このコマンドが `true` を返せば、テーブルは存在しています。

### 2. データベース管理ツールを使用する
データベースに直接接続して、テーブルが存在するかどうかを確認することができます。MySQL、PostgreSQLなどのデータベースであれば、phpMyAdmin、PgAdmin、または他のデータベース管理ツールを使用できます。

- データベースにログインします。
- データベースを選択し、存在するテーブルのリストを表示して `user_relations` テーブルを探します。

#### 3. SQL コマンドを実行する
ターミナルまたはコマンドラインインターフェースから SQL クエリを直接実行してテーブルの存在を確認することもできます。

- MySQL の場合は次のようにコマンドを実行します：
  ```bash
  mysql -u your_username -p -e "SHOW TABLES LIKE 'user_relations'" your_database_name
  ```

これらの方法を使用して `user_relations` テーブルがデータベースに存在しているかどうかを確認できます。

#### テーブル内容
```sql
CREATE TABLE IF NOT EXISTS user_relations (
    relation_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    target_user_id INT NOT NULL,
    is_following BOOLEAN NOT NULL,
    is_blocking BOOLEAN NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(user_id),
    FOREIGN KEY (target_user_id) REFERENCES users(user_id)
);
```	
フォローテーブルとブロックテーブルを統合したアプローチです。
フォローとブロックの両方の状態を単一のテーブルで管理することができます。

#### ユーザー関係テーブル (user_relations)

- `relation_id` (INT, PK): 関係の一意識別子
- `user_id` (INT, FK): アクションを起こすユーザーのID (`users.user_id`に対する外部キー)
- `target_user_id` (INT, FK): アクションの対象となるユーザーのID (`users.user_id`に対する外部キー)
- `is_following` (BOOLEAN): フォロー状態を示すフラグ
- `is_blocking` (BOOLEAN): ブロック状態を示すフラグ
- `created_at` (DATETIME): 関係が作成された日時
- `updated_at` (DATETIME): 関係が最後に更新された日時

このテーブルでは、`is_following`と`is_blocking`のフラグを使って、ユーザー間の関係がフォローなのかブロックなのかを識別します。例えば、あるユーザーが他のユーザーをフォローしている場合、`is_following`を`TRUE`にし、`is_blocking`は`FALSE`に設定します。同様に、ユーザーが他のユーザーをブロックしている場合、`is_blocking`を`TRUE`にし、`is_following`は`FALSE`に設定します。相互に独立しているため、通常、`is_following`と`is_blocking`が同時に`TRUE`になることはありませんが、ビジネス要件に応じて調整が必要です。

この設計により、データベースのテーブル数を減らし、ユーザー間の関係をよりシンプルに管理できる可能性があります。ただし、フォローとブロックが相互排他的な操作として扱われることが多いため、ビジネスロジックを通じてこれらの状態を適切に管理し、矛盾しないようにする必要があります。

また、性能面やクエリの複雑性、将来的な拡張性を考慮して、この統合アプローチが適切かどうかを慎重に評価することが重要です。特に大規模なデータセットや多くのユーザー間の関係を扱う場合、インデックスの設計やクエリの最適化に特に注意を払う必要があります。


### データ追加
`is_following` カラムがデータ挿入の SQL に抜けているため、まずはこれを適切に追記する必要があります。各行に `is_following` の値を適切に追加していきましょう。フォローの関係がある場合は `1` (true) 、ない場合は `0` (false) を設定します。

以下の SQL コマンドで `user_relations` テーブルにデータを追加します。各行に `is_following` の値を追加しました。

```sql
INSERT INTO user_relations (user_id, target_user_id, is_following, is_blocking) VALUES
(10, 11, 1, 0),  -- ユーザー10はユーザー11をフォロー
(12, 10, 1, 0),  -- ユーザー12はユーザー10をフォロー
(10, 13, 1, 0),  -- ユーザー10はユーザー13をフォロー（相互フォローの一部）
(13, 10, 1, 0),  -- ユーザー13はユーザー10をフォロー（相互フォローの一部）
(10, 14, 0, 1),  -- ユーザー10はユーザー14をブロック
(15, 10, 0, 1),  -- ユーザー15はユーザー10をブロック
(10, 16, 0, 1),  -- ユーザー10はユーザー16をブロック（双方ブロックの一部）
(16, 10, 0, 1),  -- ユーザー16はユーザー10をブロック（双方ブロックの一部）
(10, 17, 1, 0),  -- ユーザー10はユーザー17をフォロー
(17, 10, 0, 1),  -- ユーザー17はユーザー10をブロック
(10, 18, 0, 1),  -- ユーザー10はユーザー18をブロック
(18, 10, 1, 0);  -- ユーザー18はユーザー10をフォロー
```

この SQL スクリプトをデータベースに対して実行することで、データがテーブルに挿入されます。Laravel Sail を使用している場合、コンテナ内でこの SQL を実行する必要があります。コンテナ内で SQL コマンドを実行するには、以下のようにします：

1. Laravel アプリケーションが動作している Docker コンテナにログインします。
   ```bash
   docker exec -it example-app-laravel.test-1 /bin/bash
   ```

2. MySQL コマンドラインにアクセスします（MySQL コンテナに接続するための情報が必要です）。.envの設定によります。
```bash
mysql -h mysql -P 3306 -u sail -p
```

３. 上記の SQL コマンドを実行します。

これでデータが適切に挿入されます。何か問題が発生した場合、またはさらなる支援が必要な場合はお知らせください。


### ルーティング等準備
Laravelでモデルとコントローラを作成するための `artisan` コマンドを使った準備を行います。

#### モデルの作成

`UserRelation` モデルを作成するには、以下のコマンドを実行します。このコマンドは、モデルのファイルを `app/Models` ディレクトリに生成します。

```bash
php artisan make:model UserRelation
```

#### コントローラの作成

`UserRelationController` コントローラを作成するには、以下のコマンドを実行します。このコマンドは、コントローラのファイルを `app/Http/Controllers` ディレクトリに生成し、基本的な RESTful アクションを備えたコントローラを作成します。

```bash
php artisan make:controller UserRelationController --resource
```

このコマンドにより、`index`, `create`, `store`, `show`, `edit`, `update`, `destroy` メソッドが持つリソースコントローラが生成されます。ただし、今回のケースでは主に `index` メソッドだけを使います。

#### ルーティングの設定

コントローラが生成されたら、ルートを設定する必要があります。`routes/web.php` ファイルを開き、以下のルートを追加してアクセスできるようにします。

```php
Route::resource('user-relations', UserRelationController::class);
```

このルート定義により、`user-relations` への URL パスが `UserRelationController` の各アクションにマッピングされます。

これで、モデルとコントローラの準備が整いました。次に、コントローラの `index` メソッドを編集して、ビジネスロジックを実装していく必要があります。これには、データベースから関連情報を取得してビューに渡す処理を記述します。

### コード例
#### コントローラ
以下に、解説したコードの各部分を詳細に説明します。これはユーザー間の関係（フォロー、フォロワー、相互フォロー、ブロック、ブロックされる）を把握し、ID順にソートして表示するための Laravel のコントローラ関数です。

#### コード全体の流れ

1. **データ取得**:
   ユーザー10の関係を示すデータをデータベースから取得します。`following` はユーザー10がフォローしている人たち、`followers` はユーザー10をフォローしている人たちです。

2. **配列の初期化と設定**:
   ユーザー間の関係を格納するための `$relations` 配列を初期化し、各関係を設定します。

3. **フォロー情報の処理**:
   ユーザー10がフォローしている人たちに関する情報を `$relations` 配列に追加します。

4. **フォロワー情報の処理**:
   ユーザー10をフォローしている人たちに関する情報を処理し、必要に応じて `$relations` 配列を更新します。

5. **配列のソート**:
   `ksort()` 関数を使用して、`$relations` 配列をキー（ユーザーID）で昇順にソートします。

6. **ビューへのデータ渡し**:
   ソートされた `$relations` 配列をビューに渡し、ビューでこれを表示します。

### コードの詳細解説

```php
public function index()
{
    $userId = 10; // 表示するユーザーIDを設定

    // ユーザー10がフォローしている人たちのデータを取得
    $following = UserRelation::where('user_id', $userId)
                    ->get(['target_user_id', 'is_following', 'is_blocking']);

    // ユーザー10をフォローしている人たちのデータを取得
    $followers = UserRelation::where('target_user_id', $userId)
                    ->get(['user_id', 'is_following', 'is_blocking']);

    // ユーザ間の関係を格納
    $relations = [];

    // フォロー情報
    // 表示するユーザがフォロー、もしくはブロックしている状況を記載
    foreach ($following as $follow) {
        $relations[$follow->target_user_id] = [
            'follow' => $follow->is_following,
            'follower' => false, // 初期値はfalse
            'mutual' => false, // 初期値はfalse
            'blocking' => $follow->is_blocking,
            'blocked_by' => false // 初期値はfalse
        ];
    }

    // フォロワー情報
    // 表示するユーザがフォローされている、ブロックされている、また相互フォロー状態かを記載
    foreach ($followers as $follower) {
        if (!isset($relations[$follower->user_id])) {
            $relations[$follower->user_id] = [
                'follow' => false, // 初期値はfalse
                'follower' => false, // 初期値はfalse
                'mutual' => false, // 初期値はfalse
                'blocking' => false, // 初期値はfalse
                'blocked_by' => false // 初期値はfalse
            ];
        }
        $relations[$follower->user_id]['follower'] = true && !$follower->is_blocking;
        $relations[$follower->user_id]['follow'] = $relations[$follower->user_id]['follow'] ?? false;
        $relations[$follower->user_id]['mutual'] = $relations[$follower->user_id]['follow'] && $follower->is_following;
        $relations[$follower->user_id]['blocked_by'] = $follower->is_blocking;
    }

    // 配列をキー（ID）で昇順にソート
    ksort($relations);

    return view('user_relations.index', compact('relations'));
}
```

### 重要ポイント
- **配列の初期化とデフォルト値**: 各ユーザー

IDに対して、フォロー、フォロワーなどの状態を示すキーと値のペアを設定します。<b>これにより、ビューで表示する際に未定義のキーによるエラーを避けます。</b>
- **条件付き更新**: `$follower->is_blocking`が真の場合、そのユーザーはフォロワーとしてカウントされません。
- **データのソート**: `ksort()`を使用して、IDに基づいて配列をソートします。これにより、ビューでの表示順がIDの昇順になります。

このような処理フローにより、ユーザー間の複雑な関係を効果的に管理し、視覚的にわかりやすく表示することができます。

- 要件が複雑になる場合の対応
このロジックの主な目的は、ユーザー間の関係が複雑になるケース（例えば、相互にフォローしあっているが、片方がもう片方をブロックしている状況）を正確に表示することです。ユーザーが他のユーザーをブロックしている場合、通常はそのユーザーのアクション（例えば投稿の表示など）を制限したいと考えるため、そのようなユーザーを「フォロワー」として表示しない対応が必要になることがあります。




## 参考記事
https://laravel.com/docs/11.x/installation