Labyrinth of Wisdom

-This is My Archive-


【Apex】スケジュールを操作する

Apexコードを使ってスケジュールの作成・削除を行うことが出来ます。

これらを使えば、特定のクラスが呼び出された後にスケジュールをセットしたり削除したりすることが出来ます。

サンプルコード

CreateScheduleクラスを呼び出すと、ジョブ名「3時のおやつ」で2016年中毎日15時にScheduledTestクラスを呼び出すスケジュールが作成されます。

global class CreateSchedule {

    public static void execute(){
      String jobName = '3時のおやつ';
      
      // 2016年毎月毎日15時(年は省略可)
      String jobTime = '0 0 15 * * ? 2016';

      // スケジュールを作成
      System.schedule(jobName, jobTime, new ScheduledTest());
    }
}

今回は開発者コンソールから呼び出せるクラスを書いてみました。

開発者コンソール>Debug>Open Execute Anonymous Window

にアクセスし、下記のコードを記述してExecuteをクリックしてください。 CreateScheduleクラスが呼び出されます。

CreateSchedule.execute();

スケジュールが作られたかどうかを確認するには、

設定>ジョブ>スケジュール済みジョブ

にアクセスしてください。作成されたスケジュールが一覧で表示されています。

毎日15時になるとScheduledTestクラスが呼び出されます。 ただ毎日呼び出されると鬱陶しいので、呼ばれたらセットされたスケジュールを消すようにしてみました。 このクラスが呼ばれると、セットされたスケジュールが消され、コンソールにメッセージが表示されます。

global class ScheduledTest implements Schedulable {

    global void execute(SchedulableContext sc) {
      String jobName = '3時のおやつ';

      // スケジュールオブジェクトからレコードを取得
      List<CronTrigger> ct = [SELECT Id
                                FROM CronTrigger
                                WHERE CronJobDetail.JobType = '7'
                                AND CronJobDetail.Name = :jobName
                                LIMIT 1];

      // スケジュール削除
      System.abortJob(ct[0].Id);

      System.debug('今日のおやつはカールだよ');
    }
}

このスケジュールクラスは特殊で、Schedulableインターフェースを実装していたりします。スケジュールオブジェクトにアクセスするSOQL文もかなり特殊ですよね。

これらは決まった様式なので、最初の内はあまり深く考えなくてもいいかもしれません。 とりあえずはこの様式に当てはめて、自分の行いたい処理を書けばOKだと思います。

参考

おまけ

スケジュールの管理画面を作成されている方がおられました。 ここまでいけばかなりユーザも使いやすいですね。

www.terrasky.co.jp

【Apex】クラス呼び出し後の画面遷移 (PageReference型の使い方)

クラス呼び出し後の画面遷移の仕方がよくわからなかったので調べてみました。

サンプルコードを使って、PageReference型の簡単な使い方を解説します。

VisualForceページ_Page1

コントローラにJumpTestを指定し、ボタンでjumpメソッドを呼び出します。

<apex:page controller="JumpTest">
  <h1>ここはPage1です</h1>
  <apex:form >
    <apex:commandButton action="{!jump}" value="Page2へジャンプ!" />
  </apex:form>
</apex:page>

Apexクラス_JumpTest

JumpTestクラスの中に、戻り値PageReference型のjumpメソッドがあります。

メソッドの最後にreturnでVisualForceページPage2を返します。 ここで指定した画面に遷移します。

因みにreturn nullだと元の画面に遷移します。(今回で言うとPage1)

public class JumpTest(){
  public PageReference jump(){
    return Page.Page2;
  }
}

VisualForceページ_Page2

<apex:page>
  <h1>ジャンプ成功!ここはPage2です!</h1>
</apex:page>

Google Chromeでレポート作成がうまくできなかった話

なかなか同じ状態の人はいないと思うのですが、SalesForceのレポート作成が出来ない現象が起こりハマってしまったのでメモ。

すべてのタブ>レポート

にアクセスしてレポートを作成しようと思ったら、ページ左部のフォルダの部分が延々とダウンロード中で、先に進めなくなりました。

f:id:Labyrinth_of_Wisdom:20160510173015p:plain

そこから新規レポートにアクセスして作成ボタンを押してもエラー。

f:id:Labyrinth_of_Wisdom:20160510173056p:plain

なんじゃこりゃ?と色々検索しても解決策が見つからず。 諦めかけてた時に、ひょっとしたらブラウザが悪さしてるのかな?と思いIEで立ち上げると正常表示されました!(使用していたブラウザはGoogle Chrome)

ここでさらに閃き、ひょっとしたらプラグインが何か悪さしてるんじゃ!?と思いインストールしてるプラグインを見てみると怪しいモノが。

AdBlock 2.56 (広告ブロックのプラグイン)

f:id:Labyrinth_of_Wisdom:20160510173141p:plain

これを無効にすると・・・

表示された!

f:id:Labyrinth_of_Wisdom:20160510173209p:plain

f:id:Labyrinth_of_Wisdom:20160510173219p:plain

最初からブラウザを変えてやればすぐ気が付いたんでしょうが、激ハマりしてしまいました。 今後Webでこういうことがあった場合は、まずブラウザを疑ってみるという考え方を身に付けようと思います。

因みに Adblock Plus 1.11 に変更したら問題は起こりませんでした。

f:id:Labyrinth_of_Wisdom:20160510173230p:plain

【Apex】DB操作を取り消す方法(ロールバック)

データベースへの更新を取り消すロールバックをApexで実装したかったので、調べた結果をまとめたいと思います。

ついでにその際に起こった欠番現象にも少し触れています。

サンプルコード

複数のオブジェクトを更新するサンプルコード。

try文で一つでもDmlExceptionのエラーがあると、catch文に移動してロールバックポイントまで戻ります。

今回のサンプルではaccountもしくはcampaigninsertがどちらか一方でもDmlExceptionを引き起こした場合、両方のインサートが取り消されます。

Account account = new Account(Name = 'aaa');
Campaign campaign = new campaign(Name = 'bbb');

// ロールバックした際に戻るポイント
Savepoint sp = Database.setSavepoint();

try{
    insert account;
    insert campaign;
}catch(DmlException e){
    // 引数に戻るポイントを指定
    Database.rollback(sp);
}

欠番の可能性

オブジェクトのカラムに自動連番がある、かつロールバックが何回か発生した後でinsertに成功した場合には番号に抜け(欠番)が生じる可能性があります。

例:

  • 1回目

    insert account => 成功 , insert campaign => 失敗

    ロールバック実行 => レコード登録取り消し

  • 2回目

    insert account => 成功 , insert campaign => 成功

    レコード登録成功

  • 結果

Accountオブジェクト

No. Name
2 aaa

Campaignオブジェクト

No. Name
1 bbb

このようにロールバックをしていても、Accountオブジェクトレコードの番号は2で採番されてしまいます。 なんか気持ち悪い・・・。(レコードはちゃんと一個しかないんですが)

ただ、それらは気にするものでもないという意見がこちらに書かれているので、僕はそれを見習おうかと思います。 気になる方は一度見てみてください。

その他ロールバックの詳しいことは下記の参考サイトが詳しいです。

参考

[salesforce]APEXコードにおけるトランザクションとロールバック – deferloader

【Apex】メール送信機能 (SingleEmail)

はじめに

仕事で触る機会があったので、Apexコードを使用したメール機能のサンプルを記事にしたいと思います。

ワークフロー機能等では出来ない複雑な条件時のメール送信も、ApexトリガやApexスケジュールなどと併用することで可能になります。

※今回紹介しているのはSingleEmailMessageと呼ばれる、一人に対して一件メールを送る方法です。

for文等を使えば複数の人にメールを送ることもできますが、ガバナ制限により現在は一度のメソッド実行で10人まで、一日あたり1000件しか送れないみたいなので注意。 詳細は下記参照。

コードサンプル

一番単純なメール送信機能の例。工夫すれば、取引先責任者オブジェクトなどからselect文で値を取り出し、for文などで取り出した人全員にメールを送るなんてこともできます。(ただし先ほども述べた通り、一度の呼び出しで10人まで)

public with sharing class SendMail {

  public void SendMail() {

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

    // ここであて先を指定
    mail.setToAddresses(new String[]{'fuga@fugafuga.co.jp'});

    // ここでCC送り先を指定
    mail.setCcAddresses(false);

    // こでBCC送り先を指定
    mail.setBccSender(false);

    // ここで送信元を指定
    mail.setReplyTo('hoge@hogehoge.co.jp');

    // ここで送信元名前を指定
    mail.setSenderDisplayName('管理者');

    // 必要に応じて電子メールにsalesforce.comのメールの署名を追加します。
    // Apexコードを実行しているユーザのメールアドレスが使用されます。
    mail.setUseSignature(false);

    //ここで題名を指定
    mail.setSubject('ここに題名を入力');

    //ここで本文を指定
    mail.setPlainTextBody('ここに本文を入力');

    // ここで送る
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
  }
}

Apexクラス_SendMailを実行する為の画面を作成します。今回はボタンを押すとSendMailメソッドが実行されるという仕組みになります。

<apex:page controller="SendMail">
    <apex:form >
        <apex:commandButton action="{!SendMail}" value="送信"/>
    </apex:form>
</apex:page>

※Sandbox使用時の注意点

Sandbox環境の場合はメールの設定をしないと送信ができないみたいです。 しかもリフレッシュするとデフォルトでアクセス権限なしになるので毎回設定が必要らしい。

メール管理 > 送信 > アクセス権 > すべてのメール

下記エラーが出た場合は設定を変える必要があります。

NO_MASS_MAIL_PERMISSION

https://odekakeshimasyo.io/salesforce-no_mass_mail_permission.html

Sandbox環境時にWeb to リードでレコードが登録できない問題

  1. 設定 > カスタマイズ > リード > Web-to-リード >Web-to-リードフォームの作成

  2. リードオブジェクトに登録したい情報を選択済みの項目に入れていきます

  3. 戻りURLにフォームの送信ボタンを押した後に遷移するページのURLを入れます

  4. 作成ボタンを押すとHTMLコードが生成されます

ここまでが通常の手順。


ここからがSandbox環境独自の手順。

生成されたコードのformタグ内の送信先URLを一部変更する必要があります。

www=>test

<!-- 修正前 -->
<form action="https://www.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">

<!-- 修正後 -->
<form action="https://test.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">

これでリードオブジェクトにレコードが登録されるはずです。

ていうかこれは生成時に正確に紐づくようにしといて欲しいです…。

参考

SANDBOXでのweb to leadテスト: Notes of salesforce

お気に入り機能サンプル②(完結)

引き続き、Railsでお気に入り機能のサンプルを作成していきます。

前回は各ユーザが持っているお気に入り一覧を表示させるところまで作成しました。

今回はログインしたユーザがお気に入りの本を登録・削除する、という仕上げの部分を行っていきます。

コントローラ

まずコマンドラインからfavoritesコントローラーを作成します。

rails g controller favorites

次にお気に入りを登録するcreate、お気に入りを削除するdestroyアクションを記述します。

セッションに格納したログインユーザのIDと、本一覧画面から選んだ特定の本のIDを初期値として新しいレコードを作ることで、ユーザ別のお気に入りを登録できる様になります。

お気に入りの削除は、ユーザ別のお気に入り一覧(ユーザ詳細画面内)から特定のお気に入りを選びdestroyメソッドを使うことで可能になります。

  • app/controllers/favorites_controller.rb
  #お気に入り登録用アクション
  def create
    @user_id = session[:id] #ログインしたユーザのID
    @book_id = Book.find(params[:id]).id #特定の本のID
    #book_idに@book_id、user_idに@user_idを入れて、Favoriteモデルに新しいオブジェクトを作る
    @favorite = Favorite.new(book_id: @book_id, user_id: @user_id)
    if @favorite.save
      #保存に成功した場合、本一覧画面に戻る
      redirect_to books_path
    end
  end

  #お気に入り削除用アクション
  def destroy
    @favorite = Favorite.find(params[:id])
    if @favorite.destroy
      #削除に成功した場合、ログインしているユーザの詳細画面に戻る
      redirect_to user_path(session[:id])
  end

ルーティング

先ほど作成したfavoritesコントローラと各アクションにルーティングをしていきます。

お気に入りを登録するcreateアクションは、本一覧から特定の本を選んで登録するので、resources booksの入れ子でルーティングをしています。その際アクション名をaddに指定しています。

お気に入りを削除するdestroyアクションは複数リソースのonlyオプションでルーティングしています。

  • config/routes.rb
  root "top#index"
  resource :sessions, only: [:new, :create, :destroy]
  resources :users

  #以下今回追加分
  resources :books do
    member do #本一覧画面からお気に入り登録をする
      post "add", to: "favorites#create"
    end
  end
  #個人ページからお気に入りを削除する
  resources :favorites, only: [:destroy]

f:id:Labyrinth_of_Wisdom:20160304092540p:plain

ビュー

本一覧画面にお気に入り登録のリンクを記述します。

  • app/views/books/index.html.erb
<table>
  <thead>
    <tr>
      <th></th><th>タイトル</th><th>価格</th>
    </tr>
  </thead>
  <tbody>
    <% @books.each do |book| %>
      <tr>
        <!-- お気に入り登録ボタン ルートに従いパスを記述、POSTメソッドに指定 -->
        <td><%= link_to "登録", add_book_path(book), method: :post %></td>
        <!-- 本の詳細へのリンク -->
        <td><%= link_to book.name,book_path(book) %></td>
        <td><%= book.price %></td>
      </tr>
    <% end %>
  </tbody>
</table>

ユーザの詳細画面を修正していきます。 削除リンクのパスfavorite_pathの引数を@favorite[i][:id]にすることで、お気に入りレコードのIDが取得できるので、これでレコードを削除するパスが生成されます。(実は@favorite[i]でもいけます)

  • app/views/users/show.html.erb
<h3>お気に入りの本</h3>
<table>
  <thead>
    <tr>
      <th>削除</th>
      <th>タイトル</th>
      <th>価格</th>
    </tr>
  </thead>
  <tbody>
    <!-- 削除リンクの為にindex付きのメソッドを使用 -->
    <% @user.books.each_with_index do |book, i| %>
      <tr>
        <td><%= link_to "削除",favorite_path(@favorites[i][:id]), method: :delete %></td>
        <td><%= book.name %></td>
        <td><%= book.price %></td>
      </tr>
    <% end %>
  </tbody>
</table>

これでひとまずログインしたユーザが本をお気に入りを登録・削除できる機能が実装できました。

以上。

参考 ルーティングを極める (後編) | TechRacho