Google Chromeでレポート作成がうまくできなかった話
なかなか同じ状態の人はいないと思うのですが、SalesForceのレポート作成が出来ない現象が起こりハマってしまったのでメモ。
すべてのタブ>レポート
にアクセスしてレポートを作成しようと思ったら、ページ左部のフォルダの部分が延々とダウンロード中で、先に進めなくなりました。
そこから新規レポート
にアクセスして作成ボタンを押してもエラー。
なんじゃこりゃ?と色々検索しても解決策が見つからず。 諦めかけてた時に、ひょっとしたらブラウザが悪さしてるのかな?と思いIEで立ち上げると正常表示されました!(使用していたブラウザはGoogle Chrome)
ここでさらに閃き、ひょっとしたらプラグインが何か悪さしてるんじゃ!?と思いインストールしてるプラグインを見てみると怪しいモノが。
これを無効にすると・・・
表示された!
最初からブラウザを変えてやればすぐ気が付いたんでしょうが、激ハマりしてしまいました。 今後Webでこういうことがあった場合は、まずブラウザを疑ってみるという考え方を身に付けようと思います。
因みに Adblock Plus 1.11 に変更したら問題は起こりませんでした。
【Apex】DB操作を取り消す方法(ロールバック)
データベースへの更新を取り消すロールバックをApexで実装したかったので、調べた結果をまとめたいと思います。
ついでにその際に起こった欠番現象にも少し触れています。
サンプルコード
複数のオブジェクトを更新するサンプルコード。
try
文で一つでもDmlExceptionのエラーがあると、catch
文に移動してロールバックポイントまで戻ります。
今回のサンプルではaccount
もしくはcampaign
のinsert
がどちらか一方でも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で採番されてしまいます。
なんか気持ち悪い・・・。(レコードはちゃんと一個しかないんですが)
ただ、それらは気にするものでもないという意見がこちらに書かれているので、僕はそれを見習おうかと思います。 気になる方は一度見てみてください。
その他ロールバックの詳しいことは下記の参考サイトが詳しいです。
参考
【Apex】メール送信機能 (SingleEmail)
はじめに
仕事で触る機会があったので、Apexコードを使用したメール機能のサンプルを記事にしたいと思います。
ワークフロー機能等では出来ない複雑な条件時のメール送信も、ApexトリガやApexスケジュールなどと併用することで可能になります。
※今回紹介しているのはSingleEmailMessage
と呼ばれる、一人に対して一件メールを送る方法です。
for文等を使えば複数の人にメールを送ることもできますが、ガバナ制限により現在は一度のメソッド実行で10人まで、一日あたり1000件しか送れないみたいなので注意。 詳細は下記参照。
コードサンプル
- Apexクラス_SendMail
一番単純なメール送信機能の例。工夫すれば、取引先責任者オブジェクトなどから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 }); } }
- Visualforceページ_SendMail
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 リードでレコードが登録できない問題
設定 > カスタマイズ > リード > Web-to-リード >Web-to-リードフォームの作成
リードオブジェクトに登録したい情報を
選択済みの項目
に入れていきます戻りURL
にフォームの送信ボタンを押した後に遷移するページのURLを入れます作成ボタンを押すと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">
これでリードオブジェクトにレコードが登録されるはずです。
ていうかこれは生成時に正確に紐づくようにしといて欲しいです…。
参考
お気に入り機能サンプル②(完結)
引き続き、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]
ビュー
本一覧画面にお気に入り登録のリンクを記述します。
- 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>
これでひとまずログインしたユーザが本をお気に入りを登録・削除できる機能が実装できました。
以上。
お気に入り機能サンプル①
Ruby on Railsで、ログインしたユーザが本一覧の中から気になる本をお気に入りに登録・削除するというサンプルを作っていきたいと思います!
ちょっと長くなりそうなので二回に分けて書いていきたいと思います。
Rails 4.25
ruby 2.23
今回の機能はこちらの記事からの続きになっています。
テーブル設計
中間テーブルを作る事で、テーブル同士は多対多の関係を結ぶことができます。
関係を結んだ後は、お互いのテーブルから他方にアクセスできるようになります。
一人のユーザは複数のお気に入りの本を持ち、一冊の本は複数のユーザにお気に入り登録されています。
1 | 多 / 多 | 1 |
---|---|---|
User | Favorite(中間テーブル) | Book |
モデル
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_many
とthrough:
を使い、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_many
とthrough:
を使い、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>
これで各ユーザ別お気に入りリストが見れるようになりました。 次回はお気に入りの登録・削除を実装していきます。
参考
ログイン & ログアウト機能サンプル
WEBサービスで確実にお世話になるログイン&ログアウト機能のサンプルを作ってみたのでメモします。
Rails 4.25
ruby 2.23
データベース
usersテーブル
こんな感じのテーブルを用意してください。値は何でもいいです。作り方は下記を参照。
id | name | 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"
個別ルーティングの場合はこうなる。
モデル
- /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>
とても簡易的ですが、ひとまずログインしたのがどのユーザーかを認識できるようになりました。