【Apex】ドキュメントをなるべく読まずにChatterを投稿したい人に読んでほしい記事

# はじめに

ApexでChatterを送信する時に使用するConnectApiには大量のクラスがあります。
全てのドキュメントに目を通すのはかなり辛いので、Chatterを投稿するために最低限必要な部分だけまとめました。

※2022/8/29追記：[FeedItemオブジェクトを使って投稿する](#feeditemオブジェクトを使って投稿する2022829追記)

# テキストのみを投稿する
「とりあえずテキストを投稿できればいい！急いでるんだ！」というせっかちな方向けのシンプルな例です。

```java
String subjectId = '0015h00000GHHciAAH';
String text = 'メッセージ\nメッセージ';
ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), subjectId, ConnectApi.FeedElementType.FeedItem, text);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/d1582889-9638-471e-993a-15653093c863.png)

今回はsubjectIdに取引先IDを指定したため、取引先レコードに対してChatterが投稿されました。
こちらのメソッドを用いた上記の例がChatter投稿の最小構成となります。

## `postFeedElement(communityId, subjectId, feedElementType, text)`
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_ConnectAPI_ChatterFeeds_static_methods.htm#apex_ConnectAPI_ChatterFeeds_postFeedElement_1

- communityId ： `Experience CloudサイトID | internal | null` → 例のように`Network.getNetworkId()`を指定しておけば環境に合わせてIDかnull（非コミュニティ時）が自動で設定されるので、特に指定する必要がなければ`Network.getNetworkId()`でOK
- subjectId ： 投稿先のID → `ユーザID | ChatterグループID | レコードID`
- feedElementType ： ConnectApi.​FeedElement​Type列挙型 → `FeedItem`のみ使用可
- text ： 投稿するテキスト → `\n`で改行可能（最大10,000文字）

# リッチテキストを投稿する
上記の例でも最低限の投稿は可能ですが、メンションやリンク等を用いた投稿は行えません。
これらを実現するためには[ConnectApi.​FeedItem​​Input](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_feedItem.htm)クラスを用いた投稿を行う必要があります。
`ConnectApi.​FeedItem​​Input`を用いた、最初の例と同じ投稿を行うコードが以下です。

```java
String subjectId = '0015h00000GHHciAAH';
String text = 'メッセージ\nメッセージ';

ConnectApi.FeedItemInput input = new ConnectApi.FeedItemInput();
input.subjectId = subjectId;
input.feedElementType = ConnectApi.FeedElementType.FeedItem;

ConnectApi.MessageBodyInput body = new ConnectApi.MessageBodyInput();
body.messageSegments = new List<ConnectApi.MessageSegmentInput>();
input.body = body;

ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = text;
body.messageSegments.add(textSegment);

ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), input);
```

最初の例では1行で実装できていた部分が10行になった上、見慣れないクラスが突如複数出現しました。
一見理解が難しそうですが、`feedElement`クラスの構造さえ掴めれば勝ったも同然です。
順を追って解説していきます。

## `postFeedElement(communityId, feedElement)`
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_ConnectAPI_ChatterFeeds_static_methods.htm#apex_ConnectAPI_ChatterFeeds_postFeedElement_3

順を追って、と言いつついきなり最終行に飛びます。
`feedElement`は[ConnectApi.​FeedElementInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_feed_element.htm)クラス型で、フィード要素（Chatter投稿に必要な情報の集合）のコンテナです。
`postFeedElement()`を実行するため、フィード要素を適切に設定して引数に渡すことがゴールになります。


## 関連クラスの関係図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/529d75a9-17e1-eeb6-7468-6824bc1869b3.png)

- [FeedElementInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_feed_element.htm) ： 抽象クラスのため直接インスタンスを生成することはできない。ドキュメント上はこのクラスが引数の型として記載されている。
- [FeedItemInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_feedItem.htm) ： FeedElementInputのサブクラスで実際にインスタンス化するのはこのクラス（例4行目）。FeedElementInputのプロパティである`subjectId / feedElementType`はインスタンス化した後で直接設定する。
- [MessageBodyInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_msgBody.htm) ： `FeedItemInput.body`の型で、`message​​Segments`プロパティに本文を設定する。
- [MessageSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_messageSegmentInput.htm) ： `MessageBodyInput.message​​Segments`がこのクラスのリスト型。計8つのサブクラスを組み合わせて本文を生成する。

まとめると以下の流れが基本形となります。
1. `FeedElementInput`を除く3つのクラスを使ってインスタンスを生成
1. `MessageBodyInput.messageSegments`に本文を追加
1. `postFeedElement()`の引数に`FeedItemInput`のインスタンスを渡して実行

```java
String subjectId = '0015h00000GHHciAAH';

ConnectApi.FeedItemInput input = new ConnectApi.FeedItemInput();
input.subjectId = subjectId;
input.feedElementType = ConnectApi.FeedElementType.FeedItem;

ConnectApi.MessageBodyInput body = new ConnectApi.MessageBodyInput();
body.messageSegments = new List<ConnectApi.MessageSegmentInput>();
input.body = body;

/**
本文を生成
*/

ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), input);
```

## `ConnectApi.​Message​Segment​Input`のサブクラス使用例

続いて、本文の生成に関わる8つのクラスについて説明します。
これらは一部の例外を除きそれぞれ組み合わせて使用することが可能です。
コード例は全て1つ前のコード例「本文を生成」箇所に挿入して実行するものとします。

### 1. [TextSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_TxtMsgSeg.htm)
最も基本的な、単なるテキストを生成するクラスです。
`text`プロパティに本文を指定します。

※「リッチテキストを投稿する」直後のコードとほぼ同様
```java
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ\nメッセージ';
body.messageSegments.add(textSegment);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/bb4da5fa-35a3-2064-49db-a59c1bd3bca7.png)

### 2. [MentionSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_mentionMsgSeg.htm)
ユーザをメンションする場合は`id / username`どちらのプロパティでも使用可能ですが、グループをメンションする場合は`id`でのみ指定可能です。

#### ユーザへのメンション
```java
// idを用いたメンション
ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
mentionSegment.id = '0055h000005kMrEAAU';
body.messageSegments.add(mentionSegment);

// usernameを用いたメンション
ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
mentionSegment.username = 'hoge-YamadaHanako@example.com';
body.messageSegments.add(mentionSegment);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/eb11d964-399a-9bbe-41c8-52cc62a33732.png)

#### グループへのメンション（ユーザへのメンションと併用可）
```java
// グループへのメンション
ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
mentionSegment.id = '0F95h0000016FVaCAM';
body.messageSegments.add(mentionSegment);

// ユーザへのメンション
ConnectApi.MentionSegmentInput mentionSegment2 = new ConnectApi.MentionSegmentInput();
mentionSegment2.username = 'hoge-YamadaHanako@example.com';
body.messageSegments.add(mentionSegment2);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/83bc6395-756c-9b18-7b08-7a836c4d2366.png)


### 3. [LinkSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_linkMsgSeg.htm)
`url`プロパティに有効なURLを渡すとリンクが生成されます。

```java
ConnectApi.LinkSegmentInput linkSegment = new ConnectApi.LinkSegmentInput();
linkSegment.url = 'https://www.salesforce.com/jp/';
body.messageSegments.add(linkSegment);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/e768514c-4b9d-c61f-064c-38e9a7f54c61.png)

### 4. [EntityLinkSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_entity_link_segment.htm)
`LinkSegmentInput`と混同しがちですが、こちらはURLではなくSalesforce上のレコードIDへのリンクを生成します。
`entityId`にレコードIDを渡すと、そのレコードの`Name`項目がリンクとして表示されます。
※レコードへのアクセス権が無い場合は表示されないので注意
```java
ConnectApi.EntityLinkSegmentInput entityLinkSegment = new ConnectApi.EntityLinkSegmentInput();
entityLinkSegment.entityId = '0015h00000u16rFAAQ';
body.messageSegments.add(entityLinkSegment);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/fa71050f-b647-cfab-fbfe-0d1a2c0d1e94.png)


### 5. [InlineImageSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_inline_image_segment.htm)
`fileId`プロパティに`ContentDocument`のIDを渡すことでインライン画像が生成されます。（`ContentVersion`のIDは使用不可）
`altText`プロパティは任意です。

```java
ConnectApi.InlineImageSegmentInput inlineImageSegment = new ConnectApi.InlineImageSegmentInput();
inlineImageSegment.altText = 'ロゴ';
inlineImageSegment.fileId = '0695h00000DRoUJAA1';
body.messageSegments.add(inlineImageSegment);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/d9aa61d7-a04c-eace-44e9-9c8eb25ec0c2.png)


### 6. [HashtagSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_hashtag.htm)
`tag`プロパティにタグ名を指定することでハッシュタグが生成されます。
```java
ConnectApi.HashtagSegmentInput hashtagSegment = new ConnectApi.HashtagSegmentInput();
hashtagSegment.tag = 'ハッシュタグだよ';
body.messageSegments.add(hashtagSegment);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/3d85856d-f741-45c2-08fd-832b1db42aeb.png)

### 7 / 8. [MarkupBeginSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_markup_begin_segment.htm) / [MarkupEndSegmentInput](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_markup_end_segment.htm)
これらはそれぞれHTMLタグの開始 / 終了を宣言することができます。
タグの入れ子も一部例外を除いて可能なため、書式調整の自由度はかなり高いです。
また、ハイパーリンクを使用する場合もこちらのクラスを使用する必要があります。
使用可能なタグ一覧は[こちら](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_connectapi_input_markup_begin_segment.htm)から確認できます。

#### 太字テキスト
最もシンプルな例です。
```java
// ①マークアップタグ開始
ConnectApi.MarkupBeginSegmentInput boldBegin = new ConnectApi.MarkupBeginSegmentInput();
boldBegin.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin);

// ②タグで囲むテキストを挿入
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ';
body.messageSegments.add(textSegment);

// ③マークアップタグ終了
ConnectApi.MarkupEndSegmentInput boldEnd = new ConnectApi.MarkupEndSegmentInput();
boldEnd.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd);

// 結果
// <b>メッセージ</b>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/ab9b61fb-d3ae-c9a5-49fd-d17aaa06ac48.png)

こちらの例を言語化すると、以下のような手順となります。
1. 本文に`<b>`を挿入（Chatter投稿画面の「太字」ボタンをONにしている状態）
1. テキストを挿入（Chatter投稿画面で文字を入力）
1. 本文に`</b>`を挿入（Chatter投稿画の「太字」ボタンをOFFに変更）

この手順の結果`<b>メッセージ</b>`が本文に設定されている状態になり、画面へ太字で表示されます。

#### ハイパーリンク
`markupType`に`Hyperlink`を設定した時のみ、`altText / url（必須）`プロパティが使用可能です。
表示テキストとして`TextSegmentInput`も必須です。
```java
// マークアップタグ開始
ConnectApi.MarkupBeginSegmentInput linkBegin = new ConnectApi.MarkupBeginSegmentInput();
linkBegin.markupType = ConnectApi.MarkupType.Hyperlink;
linkBegin.altText = '代替テキスト';
linkBegin.url = 'https://www.salesforce.com/jp/';
body.messageSegments.add(linkBegin);

// リンク表示するテキストを設定
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = '表示テキスト';
body.messageSegments.add(textSegment);

// マークアップタグ終了
ConnectApi.MarkupEndSegmentInput linkEnd = new ConnectApi.MarkupEndSegmentInput();
linkEnd.markupType = ConnectApi.MarkupType.Hyperlink;
body.messageSegments.add(linkEnd);

// 結果
// <a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト</a>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/4c7810ad-ae9e-7a4d-339c-78e381048493.png)

#### ハイパーリンク + リスト
上記例の前後にリストを挿入したパターンです。
```java
for(Integer i = 0; i < 5; i++){
    // リスト開始
    ConnectApi.MarkupBeginSegmentInput listItemBegin = new ConnectApi.MarkupBeginSegmentInput();
    listItemBegin.markupType = ConnectApi.MarkupType.ListItem;
    body.messageSegments.add(listItemBegin);

    // ハイパーリンク開始
    ConnectApi.MarkupBeginSegmentInput linkBegin = new ConnectApi.MarkupBeginSegmentInput();
    linkBegin.markupType = ConnectApi.MarkupType.Hyperlink;
    linkBegin.altText = '代替テキスト';
    linkBegin.url = 'https://www.salesforce.com/jp/';
    body.messageSegments.add(linkBegin);

    // ハイパーリンクのテキスト挿入
    ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
    textSegment.text = '表示テキスト' + String.valueOf(i);
    body.messageSegments.add(textSegment);

    // ハイパーリンク終了
    ConnectApi.MarkupEndSegmentInput linkEnd = new ConnectApi.MarkupEndSegmentInput();
    linkEnd.markupType = ConnectApi.MarkupType.Hyperlink;
    body.messageSegments.add(linkEnd);

    // リスト終了
    ConnectApi.MarkupEndSegmentInput listItemEnd = new ConnectApi.MarkupEndSegmentInput();
    listItemEnd.markupType = ConnectApi.MarkupType.ListItem;
    body.messageSegments.add(listItemEnd);
}

// 結果
// <ul>
//   <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト0</a></li>
//   <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト1</a></li>
//   <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト2</a></li>
//   <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト3</a></li>
//   <li><a href="https://www.salesforce.com/jp/" alt="代替テキスト" target="_blank">表示テキスト4</a></li>
// </ul>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/5a3e790b-cac3-0a8c-fa64-4f964fd7def4.png)


#### 注意点（改行について）
`MarkupBeginSegmentInput`と`MarkupEndSegmentInput`で囲まれているテキストは、`\n`による改行が無効になります。（`MarkupType.Code`を除く）
以下の例は[太字テキスト](#太字テキスト)のコードに改行を加えたものです。
今まで通り改行されてほしいところですが、太字のみ適用され改行は無視されている状態です。

```java
ConnectApi.MarkupBeginSegmentInput boldBegin = new ConnectApi.MarkupBeginSegmentInput();
boldBegin.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin);

ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = 'メッセージ\nメッセージ';
body.messageSegments.add(textSegment);

ConnectApi.MarkupEndSegmentInput boldEnd = new ConnectApi.MarkupEndSegmentInput();
boldEnd.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd);

// 結果
// <b>メッセージメッセージ</b>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/ac6be106-110d-10ba-1b85-4050024c5beb.png)

改行する場合は以下のどちらかで対応してください。

##### 1. タグを分ける
少々面倒ですが、行ごとに`MarkupType.Paragraph`で囲い段落を分けることで改行と同様の表示を実現できます。
```java
// 1行目の段落を挿入
ConnectApi.MarkupBeginSegmentInput paragraphBegin1 = new ConnectApi.MarkupBeginSegmentInput();
paragraphBegin1.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphBegin1);

ConnectApi.MarkupBeginSegmentInput boldBegin1 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin1);

ConnectApi.TextSegmentInput textSegment1 = new ConnectApi.TextSegmentInput();
textSegment1.text = 'メッセージ';
body.messageSegments.add(textSegment1);

ConnectApi.MarkupEndSegmentInput boldEnd1 = new ConnectApi.MarkupEndSegmentInput();
boldEnd1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd1);

ConnectApi.MarkupEndSegmentInput paragraphEnd1 = new ConnectApi.MarkupEndSegmentInput();
paragraphEnd1.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphEnd1);

// 2行目の段落を挿入
ConnectApi.MarkupBeginSegmentInput paragraphBegin2 = new ConnectApi.MarkupBeginSegmentInput();
paragraphBegin2.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphBegin2);

ConnectApi.MarkupBeginSegmentInput boldBegin2 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin2);

ConnectApi.TextSegmentInput textSegment2 = new ConnectApi.TextSegmentInput();
textSegment2.text = 'メッセージ';
body.messageSegments.add(textSegment2);

ConnectApi.MarkupEndSegmentInput boldEnd2 = new ConnectApi.MarkupEndSegmentInput();
boldEnd2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd2);

ConnectApi.MarkupEndSegmentInput paragraphEnd2 = new ConnectApi.MarkupEndSegmentInput();
paragraphEnd2.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphEnd2);

// 結果
// <p><b>メッセージ</b></p>
// <p><b>メッセージ</b></p>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/39214f26-c673-f946-d3e5-6422f0ca77d0.png)

##### 2. 改行用の段落を挿入する
こちらは改行したい箇所に段落を差し込むことで行を分けています。
間に空行が入っても問題ない場合はこちらの方がお手軽かもしれません。
```java
// 1行目の太字テキスト
ConnectApi.MarkupBeginSegmentInput boldBegin1 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin1);

ConnectApi.TextSegmentInput textSegment1 = new ConnectApi.TextSegmentInput();
textSegment1.text = 'メッセージ';
body.messageSegments.add(textSegment1);

ConnectApi.MarkupEndSegmentInput boldEnd1 = new ConnectApi.MarkupEndSegmentInput();
boldEnd1.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd1);

// 段落を挿入
ConnectApi.MarkupBeginSegmentInput paragraphBegin = new ConnectApi.MarkupBeginSegmentInput();
paragraphBegin.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphBegin);

ConnectApi.TextSegmentInput textSegmentBreak = new ConnectApi.TextSegmentInput();
textSegmentBreak.text = '&nbsp;';
body.messageSegments.add(textSegmentBreak);

ConnectApi.MarkupEndSegmentInput paragraphEnd = new ConnectApi.MarkupEndSegmentInput();
paragraphEnd.markupType = ConnectApi.MarkupType.Paragraph;
body.messageSegments.add(paragraphEnd);

// 2行目の太字テキスト
ConnectApi.MarkupBeginSegmentInput boldBegin2 = new ConnectApi.MarkupBeginSegmentInput();
boldBegin2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldBegin2);

ConnectApi.TextSegmentInput textSegment2 = new ConnectApi.TextSegmentInput();
textSegment2.text = 'メッセージ';
body.messageSegments.add(textSegment2);

ConnectApi.MarkupEndSegmentInput boldEnd2 = new ConnectApi.MarkupEndSegmentInput();
boldEnd2.markupType = ConnectApi.MarkupType.Bold;
body.messageSegments.add(boldEnd2);

// 結果
// <b>メッセージ</b>
// <p> </p>
// <b>メッセージ</b>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/4f7d00e3-aa63-0e4d-489a-3cba51679b07.png)

# FeedItemオブジェクトを使って投稿する（2022/8/29追記）
ConnectApiを使わずにFeedItemをinsertしてもいいんじゃない？というコメントを複数見掛けました。
確かに、特に簡単な投稿内容であればこちらの方が使いやすいと感じました、、
完っ全に頭から抜け落ちていたので追記させてください、、！
ご意見を下さった方々へ、この場を借りてお礼申し上げます！！！

## そもそもFeedItemって？
Chatterの投稿内容を保持するオブジェクトです。
当然ですが、Salesforceの画面上や前述のConnectApiによる投稿など方法に依らず、Chatterが投稿された際はこのFeedItemのレコードが作成されます。
ここでは、今回の記事に関連する主要な項目を少しだけ紹介します。

- ParentId ： 投稿先のID（ConnectApiによる投稿時の`subjectId`と同様
- body ： 投稿本文（ConnectApiによる投稿時の`text | ConnectApi.MessageBodyInput`）と同様
- IsRichText ： 本文がリッチテキストか判定する真偽値

これまたそれなりの項目数があったので、気になる方はドキュメントをご覧ください。（丸投げ）
ただConnectApi周りのドキュメントに比べると、こちらは単なる標準項目の説明なのでまだ読みやすいかもしれません。

https://developer.salesforce.com/docs/atlas.ja-jp.object_reference.meta/object_reference/sforce_api_objects_feeditem.htm

## テキスト
[テキストのみを投稿する](#テキストのみを投稿する)と同じ内容を投稿するシンプルな例です。
普通のオブジェクトを作成する時と同じ記法なので直感的で分かりやすいですね。
特に`ConnectApi.ChatterFeeds.postFeedElement()`のような、見る人によっては呪文のようなメソッドを使う必要が無い点は大きな利点だと感じました。

```java
String subjectId = '0015h00000GHHciAAH';
String text = 'メッセージ\nメッセージ';

FeedItem feed = new FeedItem(
    ParentId = subjectId,
    Body = text
);

insert feed;
```

ここで作成されたFeedItemレコードをSOQLで取得すると以下のような結果が得られます。
`IsRichText`の初期値はfalseなので、特に違和感無い結果だと思います。

| ParentId | Body | IsRichText |
|:-:|:-:|:-:|
| 0015h00000GHHciAAH | メッセージ メッセージ | false |

また、[テキストのみを投稿する](#テキストのみを投稿する)・[リッチテキストを投稿する（冒頭の例）](#リッチテキストを投稿する)で投稿を行った場合に作成されるFeedItemレコードも同じ結果となります。

## リッチテキスト
[こちら](#7--8-markupbeginsegmentinput--markupendsegmentinput)で紹介した太字テキストを投稿する例です。
`IsRichText = true`を追加しすることでリッチテキストとして解釈されます。

```java
String subjectId = '0015h00000GHHciAAH';
String text = '<b>メッセージ</b>';

FeedItem feed = new FeedItem(
    ParentId = subjectId,
    Body = text,
    IsRichText = true
);

insert feed;
```

|ParentId | Body | IsRichText |
|:-:|:-:|:-:|
| 0015h00000GHHciAAH | \<b>メッセージ\</b> | true |

`IsRichText`によりBodyの解釈が変わるだけなので、レコードの作成時か作成後の更新で`IsRichText = false`が指定されてしまうとただの文字列として表示されてしまうので注意が必要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/6e209bf8-8ae0-394d-6fee-e52881135689.png)

**----------------------- 2022/8/29追記分ここまで -----------------------**

# おわりに
Chatterの投稿に関してはある程度説明できたんじゃないかと思います。
しかし、コメントやいいねなどApexで出来ることはまだまだたくさんあるので興味があれば以下のリンクからご確認ください。
何ができるのかだけでも知っておくといつか役に立つ日が来るかもしれません、、！

https://developer.salesforce.com/docs/atlas.ja-jp.200.0.apexcode.meta/apexcode/connectapi_examples.htm