Labyrinth of Wisdom

-This is My Archive-


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

お気に入り機能サンプル①

Ruby on Railsで、ログインしたユーザが本一覧の中から気になる本をお気に入りに登録・削除するというサンプルを作っていきたいと思います!

ちょっと長くなりそうなので二回に分けて書いていきたいと思います。

Rails 4.25 ruby 2.23

今回の機能はこちらの記事からの続きになっています。

テーブル設計

中間テーブルを作る事で、テーブル同士は多対多の関係を結ぶことができます。
関係を結んだ後は、お互いのテーブルから他方にアクセスできるようになります。
一人のユーザは複数のお気に入りの本を持ち、一冊の本は複数のユーザにお気に入り登録されています。

1 多 / 多 1
User Favorite(中間テーブル) Book

モデル

  • Favoriteモデル作成

    Favoriteモデルを作成します。コマンドラインで下記コマンドを入力します。
    カラムの設定が複雑なので、後ほどマイグレーションファイルに直接書き込みます。

rails g model favorite
  • app/models/favorites.rb

    belongs_toでusers、booksテーブルとそれぞれ1対多の関係を結びます。

class Favorite < ActiveRecord::Base
    belongs_to :user #User:Favorite => 1:多
    belongs_to :book #Book:Favorite => 1:多
end
  • app/models/users.rb

    has_manyでfavaritesテーブルと1対多の関係を結びます。
    has_manythrough:を使い、favoritesテーブルを通してbooksテーブルと多対多の関係を結びます。
    ここでbooks複数なことに注意しましょう。

class User < ActiveRecord::Base
    has_many :favorites #User:Favorite => 1:多
    has_many :books, through: :favorites
    #クラスメソッド省略
end
  • Bookモデル作成

    Bookモデルを作成します。コマンドラインで下記コマンドを入力します。

rails g model book name:string price:integer
  • app/models/books.rb

    先ほどと同様に、has_manyでfavaritesテーブルと1対多の関係を結びます。
    has_manythrough:を使い、favoritesテーブルを通してusersテーブルと多対多の関係を結びます。
    ここでもusers複数なことに注意しましょう。

class Book < ActiveRecord::Base
    has_many :favorites #User:Favorite => 1:多
    has_many :users, through: :favorites
end

これでひとまずモデル同士の紐づけは完了しました。

マイグレーションファイル/テーブル作成

favoritesテーブルを作っていきます。

  • db/migrate/yyyymmddhhmmss_create_favorites.rb

    カラムの設定をしてなかったので、マイグレーションファイルに直接記述していきます。

class CreateFavorites < ActiveRecord::Migration
  def change
    create_table :favorites do |t|
      t.references :user, null:false #外部キー
      t.references :book, null:false #外部キー
      t.timestamps null: false
    end

    add_index :favorites, :user_id #インデックス
    add_index :favorites, :book_id #インデックス
  end
end

次にマイグレーションファイルを実行してテーブルを作成します。

rake db:migrate

これでfavoritesテーブルができました。テーブル同士の紐づけも完了しました。

シードデータ

  • db/seeds.rb

    シードデータに適当なデータを準備しておきます。

Book.create!(name: "マギ", price: 500)
Book.create!(name: "シンドバッドの冒険", price: 600)
Book.create!(name: "海皇紀", price: 580)

User.create!(name: "アラジン", email: "magi@hoge.com", password: "magimagi")

Favorite.create!(user_id: 1, book_id: 1)
Favorite.create!(user_id: 1, book_id: 3)

テーブルに反映します。

rake db:reset

ルーティング

ユーザへのルーティングを行います。

  root "top#index"
  resource :sessions, only: [:new, :create, :destroy]
  resources :users #追加分

コントローラ

コントローラを作成していきます。

  • app/controllers/users_controller.rb

    今回は特定のユーザの詳細ページにアクセスすると、ユーザの登録したお気に入り一覧が出る所まで実装します。
    ※今回の機能とは直接関係ないので、ユーザ一覧のコード等は省略します。

def show
    @user = User.find(params[:id])
    #特定のユーザーが登録したお気に入りを全て取得する
    @favorites = Favorite.where("user_id = ?", @user)
end

ビュー

  • app/views/user/show.html.erb

    ユーザ詳細ページにユーザが登録したお気に入りの本一覧を表示します。
    @user.booksで特定のユーザが持つお気に入りの本にアクセスできます。

    <h3>お気に入りの本</h3>
    <table>
      <thead>
          <tr>
            <th>削除</th>
            <th>タイトル</th>
            <th>価格</th>
          </tr>
      </thead>
      <tbody>
        <% @user.books.each do |book| %>
          <tr>
            <td><%= book.name %></td>
            <td><%= book.price %></td>
          </tr>
        <% end %>
      </tbody>
    </table>

これで各ユーザ別お気に入りリストが見れるようになりました。 次回はお気に入りの登録・削除を実装していきます。

参考

Rails4で多対多のリレーションをモデルに実装する - Rails Webook

ログイン & ログアウト機能サンプル

WEBサービスで確実にお世話になるログイン&ログアウト機能のサンプルを作ってみたのでメモします。

Rails 4.25 ruby 2.23

データベース

  • usersテーブル

    こんな感じのテーブルを用意してください。値は何でもいいです。
    作り方は下記を参照。

id name email password
1 アラジン magi@hoge.com magimagi

ルーティング

  • /config/routes.rb
#ルートにアクセスするとtopコントローラーのindexアクションを呼び出す。
root "top#index"
#CRUD操作のnew/create/destroyのみ使用する。リソースが単数形なのに注意。
resource :sessions, only: [:new, :create, :destroy]

#個別にルーティングする方法もある。
#get "signin", to: "sessions#new"
#post "signin", to: "sessions#create"
#delete "signout", to: "sessions#destroy"

f:id:Labyrinth_of_Wisdom:20160225160208p:plain 個別ルーティングの場合はこうなる。 f:id:Labyrinth_of_Wisdom:20160225115708p:plain

モデル

  • /app/models/user.rb
#クラスメソッド
class << self
  def check(email, password)
    user = self.find_by(email: email)
    user_pass = self.find_by(password: password)
    #変数userとuser_passのidが一致すれば、変数userを返す
    if user == user_pass
      user
    else
      nil
    end
  end
end

コントローラー

  • /app/controllers/top_controller.rb

    中身は空で大丈夫です。

  • /app/controllers/sessions_controller.rb

#ログイン画面を呼び出す為のアクションなので中身は何もない
def new
end

#セッションを取得/ログイン
def create
  #user.rbに書いたクラスメソッドcheckを使う。フォームからの値をparamsメソッドで受け取る。
  @user = User.check(params[:session][:email], params[:session][:password])

  #@userがtrueなら、@userのnameをハッシュsessionにキー[:name]でセットする。
  if @user
    session[:name] = @user.name
    #メッセージをハッシュflashにキー[:success]でセットする。
    flash[:success] = "ログインに成功しました。"
    #rootのページに遷移する。
    redirect_to root_path
  else
    flash.now[:error] = "メールアドレスとパスワードが一致しません。"
    render "new"
  end
end

#セッションを破棄/ログアウト
def destroy
  #session[:name]に入れた値をdeleteメソッドで削除する。
  session.delete(:name)
  redirect_to new_sessions_path
  #個別ルーティングの場合
  #redirect_to signin_path
end

ビュー

  • /app/views/sessions/new.html.erb
<h1>Sign in</h1>

<!-- ログイン失敗のメッセージを出す -->
<%= flash[:error] %>

<%= form_for(:session, url: sessions_path) do |f|  %>
<!-- 個別ルーティングの場合 -->
<%#= form_for(:session, url: signin_path) do |f|  %>
  <%= f.label :email %>
  <%= f.text_field :email %>

  <%= f.label :password %>
  <%= f.password_field :password %>

  <%= f.submit "Sign in" %>
<% end %>
  • /app/views/top/index.html.erb
<!-- ログアウトリンク -->
<%= link_to "Log Out", :sessions, method: :delete %>
<!-- 個別ルーティングの場合 -->
<%# link_to "Log Out", signout_path, method: :delete %>

<!-- ログイン成功のメッセージを出す -->
<p><%= flash[:success] %></p>

<!-- ログインしたユーザーの名前を表示する -->
<p>Welcom<%= session[:name] %>さん</p>

とても簡易的ですが、ひとまずログインしたのがどのユーザーかを認識できるようになりました。