Visualforce+Apexで作るSalesforce一覧検索画面

学习如何坐一览表,同时支持 ▲ ▼ 排序。
Controller

public with sharing class TravelRequestListController {

    // 抽出対象となるフィールドリスト
    static List<String> TARGET_FIELDS = new List<String>{
         'Id'
        ,'Name'
        ,'TravelRequestName__c'
        ,'Status__c'
        ,'TravelStartDate__c'
        ,'TravelEndDate__c'
        ,'Purpose_of_Travel__c'
        ,'Total__c'
        ,'LastModifiedDate'
    };

    public SearchCondition condition{ get;set; }        // 検索条件
    public List<Travel_Request__c> results { get;set; } // 検索結果リスト
    public String sortingField { get;set; }             // 見出しのソート用リンクを押された際のフィールド名

    /**
     * 初期化処理
     */
    public void init(){
        this.condition = new SearchCondition();
        this.results = new List<Travel_Request__c>();
    }

    /**
     * クリアボタン処理
     */
    public PageReference clear(){
        init();
        return null;
    }

    /**
     * 検索ボタン処理
     */
    public PageReference search() {

        // バリデーションチェック
        if( condition.validate() ){
            return null;
        }

        // 検索条件からSOQLを作成
        String soqlQuery = condition.getSoqlQuery();
        System.debug('[soql] ' + soqlQuery);

        try{
            this.results = database.query(soqlQuery);
        }catch(DmlException e){
            ApexPages.addMessages(e);
        }catch(Exception e){
            ApexPages.addMessages(e);
        }
        return null;
    }

    /**
     * ソートリンク処理
     */
    public PageReference sort(){

        // ソートを行う対象フィールドが不明な場合は何もしない
        if(this.sortingField == null ){
            return null;
        }

        // ソートを行う対象が、現在ソートしているフィールドと同じ場合はソート順を反対にする
        if(this.sortingField == this.condition.sortkey){
            this.condition.setOrderReverse();
        }
        // ソートを行う対象が、現在ソートしているフィールドと違う場合は新しい項目でソートするようにする
        else {
            this.condition.sortkey = this.sortingField;
        }

        // 検索実行
        search();
        return null;
    }

    /**
     * 現在のソートキーを取得するためのメソッド
     * 
     * ・検索結果の見出しにソート順を出すために使う
     * ・本来はgetSortOrderも含めて、ロジックもApex側で持ちたいが、
     *  VisualForceから引数ありでメソッドが呼べないため、このようにしている。
     * 
     */
    public String getSortKey(){
        return this.condition.sortkey;
    }

    /**
     * 現在のソート順を取得するためのメソッド(▲ or ▼を返す)
     */ 
    public String getSortOrder(){
        return this.condition.sortOrderToString();
    }


    /**
     * 検索条件を管理するクラス
     */ 
    public Class SearchCondition {

    private Time JST_AM0 = Time.newInstance(9, 0, 0, 0);  

        /*
         * 検索条件の入力フィールド用
         * 
         * ・カスタムフィールドとして検索条件の入力項目を作成している。
         * ・こうすることで、プルダウンリストの場合はオブジェクトの設定画面から
         *  選択リスト値を追加することで一覧画面の選択肢も追加される。
         */
        public Travel_Request__c obj {get;set;}     

        public String sortkey { get;set; }          // ソートキー
        public String order { get;set; }            // ソート順

        public SearchCondition() {
            this.obj = new Travel_Request__c();

            // デフォルトは最終更新日時の降順とする
            sortkey = 'LastModifiedDate';
            order = 'DESC';
        }


        // 検索条件のバリデーションチェック
        public boolean validate(){
            boolean isError = false;
            if( this.obj.SearchConditionLastModifiedFrom__c != null &&
                this.obj.SearchConditionLastModifiedTo__c != null &&
                this.obj.SearchConditionLastModifiedFrom__c > this.obj.SearchConditionLastModifiedTo__c ){
                    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, '最終更新日の範囲指定が逆転しています', ''));
                    isError = true;
            }
            return isError;
        }


        // 最終的なSOQLを生成(最大500件というのは固定)
        public String getSoqlQuery(){
            List<String> param = new String[]{ getFieldList(), getWhere(), getOrder() };
            return String.format('SELECT {0} FROM Travel_Request__c {1} {2} LIMIT 500', param);
        }

        // SELECT対象フィールド
        private String getFieldList(){
            return String.join(TARGET_FIELDS, ',');
        }

        // WHERE句作成
        private String getWhere(){
            List<String> param = new String[]{ };

            // ステータス
            if( !String.isBlank(this.obj.SearchConditionStatus__c) ){
                param.add('Status__c = \'' + obj.SearchConditionStatus__c + '\'');
            }
            // 最終更新日From
            if( this.obj.SearchConditionLastModifiedFrom__c != null ){
                Datetime fromDate = getStartOfDay(obj.SearchConditionLastModifiedFrom__c );
                param.add('LastModifiedDate >= ' + fromDate.format('yyyy-MM-dd\'T\'HH:mm:ss.000\'Z\''));
            }            
            // 最終更新日To
            if( this.obj.SearchConditionLastModifiedTo__c != null ){
                Datetime toDateAddOneDay = getStartOfDay( obj.SearchConditionLastModifiedTo__c.addDays(1));
                param.add('LastModifiedDate < ' + toDateAddOneDay.format('yyyy-MM-dd\'T\'HH:mm:ss.000\'Z\''));
            }

            if(param.isEmpty()){
                return '';
            }
            return 'WHERE ' + String.join(param, ' AND ');
        }

        // ORDERBY句作成
        private String getOrder(){
            List<String> param = new String[]{ sortkey, order };
            return String.format('ORDER BY {0} {1}', param);
        }

        private DateTime getStartOfDay(Date d){
            // GMT+9を考慮して、GMTで日本の0時を作成する(=GMTでは1日前の15時)
            JST_AM0 = Time.newInstance(15, 0, 0, 0);
            return Datetime.newInstance(d.addDays(-1), JST_AM0);
        }

        // ソート順を見出しに表示する記号に変換する
        public String sortOrderToString(){
            if(this.order == 'DESC'){
                return '▼';
            }
            return '▲';
        }

        // ソート順を逆にする
        public void setOrderReverse(){
            if(this.order == 'DESC'){
                this.order = 'ASC';
            }
            else {
                this.order = 'DESC';
            }
        } 
    }
}

APEX

<apex:page controller="TravelRequestListController" action="{!init}" sidebar="false" Id="TravelRequestList" >
<style type="text/css">
    #conditionTable { width: 50%; border: 0; margin-left: 5%; }
    #conditionTable .label { text-align: right; }
    #conditionTable .dateFormat { display: none; }
</style>
<script type="text/javascript">
    // ページを開いたときに初期フォーカスをしない
    beenFocused = true;
</script>
<apex:form id="form1">
    <apex:pageBlock title="出張一覧">
        <apex:pageMessages id="messagearea" showDetail="false"/>
        <apex:pageblockSection id="conditionSection" title="検索" columns="1">
            <apex:outputpanel id="searchcondition">
                <table id="conditionTable">
                    <tr>
                        <td class="label"><apex:outputLabel value="ステータス" for="scStatus" styleClass="labelCol" /></td>
                        <td><apex:inputField id="scStatus" value="{!condition.obj.SearchConditionStatus__c}" /></td>
                    </tr>
                    <tr>
                        <td class="label"><apex:outputLabel value="最終更新日"  styleClass="labelCol" /></td>
                        <td>
                            <apex:inputField id="scLastModifiedFrom" value="{!condition.obj.SearchConditionLastModifiedFrom__c}" />
                            <apex:outputLabel value=" ~ "  styleClass="labelCol" />
                            <apex:inputField id="scLastModifiedTo" value="{!condition.obj.SearchConditionLastModifiedTo__c}" />
                        </td>
                    </tr>
                </table>
            </apex:outputpanel>
        </apex:pageblockSection>
        <apex:pageBlockButtons id="buttonSection" location="bottom" >
            <apex:commandButton value="検索" action="{!search}" style="font-size:12pt;width:100px;height:30px;" reRender="searchresult,messagearea"/>
            <apex:commandButton value="クリア" action="{!clear}" style="font-size:12pt;width:100px;height:30px;" reRender="searchcondition,searchresult,messagearea"/>
        </apex:pageBlockButtons>
    </apex:pageBlock>
</apex:form>
<apex:outputpanel id="searchresult">
    <apex:pageBlock title="検索結果:" rendered="{!(results.size == 0)}">
         検索条件に該当するデータがありません
    </apex:pageBlock>

    <apex:form id="resultForm">
        <apex:pageBlock id="resultBlock" title="出張一覧" rendered="{!(results.size > 0)}">
            <apex:outputtext style="width:110px" value="結果 : {!results.size}件"/>
            <apex:pageblockTable id="resultTable" value="{!results}" var="o" frame="box">
                <apex:column style="width:80px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="出張申請番号{!IF(sortKey == 'Name', sortOrder, ' ')}">
                            <apex:param value="Name" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet>
                    <apex:outputlink value="/{!o.Id}"><apex:outputField style="width:80px" value="{!o.Name}"/></apex:outputlink>
                </apex:column>
                <apex:column style="width:160px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="出張申請名{!IF(sortKey == 'TravelRequestName__c', sortOrder, ' ')}">
                            <apex:param value="TravelRequestName__c" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet> 
                    <apex:outputField style="width:150px" value="{!o.TravelRequestName__c}"/>
                </apex:column>
                <apex:column style="width:80px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="ステータス{!IF(sortKey == 'Status__c', sortOrder, ' ')}">
                            <apex:param value="Status__c" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet>
                    <apex:outputField style="width:150px" value="{!o.Status__c}"/>
                </apex:column>
                <apex:column style="width:80px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="出張開始日{!IF(sortKey == 'TravelStartDate__c', sortOrder, ' ')}">
                            <apex:param value="TravelStartDate__c" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet> 
                    <apex:outputField style="width:80px" value="{!o.TravelStartDate__c}"/>
                </apex:column>
                <apex:column style="width:80px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="出張終了日{!IF(sortKey == 'TravelEndDate__c', sortOrder, ' ')}">
                            <apex:param value="TravelEndDate__c" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet> 
                    <apex:outputField style="width:80px" value="{!o.TravelEndDate__c}"/>
                </apex:column>
                <apex:column style="width:120px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="出張目的{!IF(sortKey == 'Purpose_of_Travel__c', sortOrder, ' ')}">
                            <apex:param value="Purpose_of_Travel__c" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet> 
                    <apex:outputField style="width:150px" value="{!o.Purpose_of_Travel__c}"/>
                </apex:column>
                <apex:column style="width:120px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="総費用{!IF(sortKey == 'Total__c', sortOrder, ' ')}">
                            <apex:param value="Total__c" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet> 
                    <apex:outputField style="width:150px" value="{!o.Total__c}"/>
                </apex:column>
                <apex:column style="width:80px">
                    <apex:facet name="header">
                        <apex:commandLink action="{!sort}" value="最終更新日時{!IF(sortKey == 'LastModifiedDate', sortOrder, ' ')}">
                            <apex:param value="LastModifiedDate" name="String" assignTo="{!sortingField}" />
                        </apex:commandLink>
                    </apex:facet> 
                    <apex:outputField style="width:80px" value="{!o.LastModifiedDate}"/>
                </apex:column>
            </apex:pageblockTable>
        </apex:pageBlock>
    </apex:form>
</apex:outputpanel>
</apex:page>

程序测试代码

@isTest
public class TravelRequestListControllerTest {

    /**
     * 検索条件なしで検索
     */ 
    @isTest
    static void noKeywordSearch(){
        createRecords();
        TravelRequestListController sut = new TravelRequestListController();

        Test.startTest();
        sut.init();
        sut.search();
        Test.stopTest();

        System.assert(sut.results.size() == 3);
    }

    /**
     * ステータスを指定した検索の実施
     */ 
    @isTest
    static void statusSearch(){
        createRecords();
        TravelRequestListController sut = new TravelRequestListController();

        Test.startTest();
        sut.init();
        sut.condition.obj.SearchConditionStatus__c = '承認完了';
        sut.search();
        Test.stopTest();

        System.debug(sut.results.size());
        System.debug(sut.results);
        System.assert(sut.results.size() == 1);
    }

    /**
     * ソートキー変更、ソート順変更など
     */ 
    @isTest
    static void lastModifiedDateSearchWithSorting(){
        createRecords();
        TravelRequestListController sut = new TravelRequestListController();

        Test.startTest();
        sut.init();
        sut.condition.obj.SearchConditionLastModifiedFrom__c = Date.today();
        sut.condition.obj.SearchConditionLastModifiedTo__c = Date.today();
        sut.search();

        // 出張申請名の降順ソート
        sut.sortingField = 'TravelRequestName__c'; 
        sut.sort();                    
        Travel_Request__c actual1 = sut.results.get(0);
        System.assert(actual1.TravelRequestName__c == '出張3');

        // 昇順ソートに変更
        sut.sort();
        Travel_Request__c actual2 = sut.results.get(0);
        System.assert(actual2.TravelRequestName__c == '出張1');
        System.assert(sut.getSortKey() == 'TravelRequestName__c');
        System.assert(sut.getSortOrder() == '▲');

        Test.stopTest();
    }

    /**
     * クリアボタン処理
     */ 
    @isTest
    static void clear(){
        createRecords();
        TravelRequestListController sut = new TravelRequestListController();

        Test.startTest();
        sut.init();
        sut.search();
        System.assert(sut.results.size() > 0);
        sut.clear();
        Test.stopTest();

        System.assert(sut.results.size() == 0);
    }


    /**
     * テスト用に3レコード作成する
     */ 
    private static void createRecords(){
        List<Travel_Request__c> records = new Travel_Request__c[]{
              createRecord(1, '申請中')
            , createRecord(2, '承認完了')
            , createRecord(3, '却下')
        };
        insert records;
    }

    private static Travel_Request__c createRecord(Integer index, String status){
        Travel_Request__c tr = new Travel_Request__c();
    tr.TravelRequestName__c = '出張' + String.valueOf(index);
        tr.Status__c = status;
        tr.TravelStartDate__c = Date.newInstance(2017, 1, 1).addDays(index);
        tr.TravelEndDate__c = Date.newInstance(2017, 1, 4).addDays(index);
        tr.Airfare__c = 1000;
        return tr;
    }

}

参考
https://developer.salesforce.com/ja/worldtour2016/minihacks
https://qiita.com/nyasba/items/6b27db8af6926ddccd87

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值