标签 salesforce 下的文章

Salesforce 未锁定包(Salesforce Unlocked Package)是Salesforce最新推广的基于包的开发最佳实践。通过Salesforce CLI,我们可以很方便的制作和部署未锁定包。本文只是简略的将核心步骤展示出来:

  1. 创建一个 SFDX 项目
sfdx force:project:create -d demo-workspace -n expenser-app -p force-app
  1. 授权Dev Hub环境,该环境必须启用Dev Hub功能和未锁定包和第二代管控包功能
sfdx auth:web:login -d -a devHub
  1. 创建一个草稿环境(scratch org)并在其中开发包
sfdx force:org:create -f config/project-scratch-def.json -u scratchOrg1
  1. 保证所有要打包的组件都已经在当前的项目文件夹内
  2. 从 SFDX 项目文件夹,直接创建未锁定包:
sfdx force:package:create -n "Demo App" -r force-app -t Unlocked -o me@devhub.org
  1. 检查项目文件夹中的sfdx-project.json文件,CLI 会自动更新项目文件,使其包含上一步创建的包的信息:
{
   "packageDirectories": [
      {
         "path": "force-app",
         "default": true,
         "package": "Demo App",
         "versionName": "ver 0.1",
         "versionNumber": "0.1.0.NEXT"
      }
   ],
   "namespace": "",
   "sfdcLoginUrl": "https://login.salesforce.com",
   "sourceApiVersion": "50.0",
   "packageAliases": {
      "Expense Manager": "0Hoxxx"
   }
}
  1. 创建一个包的版本,Salesforce CLI会自动处理包的版本号等
sfdx force:package:version:create --package "Demo App" -k lushang.me --wait 10
  1. 在另一个创建好的草稿环境中,安装并测试这个版本的包:
sfdx force:package:install -p "Demo App@0.1.0-1" -y testOrg1 -k lushang.me --wait 10 --publishwait 10
  1. 如果上一步骤没问题,则可以发布这个版本的包:
sfdx force:package:version:promote -p "Demo App@0.1.0-1"

参考:

  1. Salesforce DX Developer Guide
  2. Workflow for Unlocked Packages
  3. Release an Unlocked Package

半连接和反链接是SOQL里面一个宝,只要用的好,天天下班早

Semi-Join | 半连接

半连接(Semi-Join)指的是在SOQL语句的对比子句里面(也就是WHERE部分),使用子查询(subquery)的情况。

可能用到半连接的情况可能有:

  1. 获取某些具有特定阶段或者截至日期Opportunity的Account下面所有Contact信息
  2. 获取具有激活的Contract的Account下面所有的Opportunity信息

写法
有两种半连接:1)使用Id字段,2)使用外键(Lookup/Master-Detail关系等)

  1. 使用Id字段的半连接例子:
SELECT Id, Name 
FROM Account 
WHERE Id IN 
  ( SELECT AccountId
    FROM Opportunity
    WHERE StageName = 'Closed Lost' 
  )
  1. 使用外键的半连接例子:
SELECT Id
FROM Task 
WHERE WhoId IN 
  (
    SELECT Id
    FROM Contact
    WHERE MailingCity = 'Twin Falls'
  )

Anti-Join | 反连接

反连接(Anti-Join)与半连接非常类似,只是不同于半连接使用的比较关键字为IN, 反连接使用的比较关键字为NOT IN。其余部分包括写法、限制等是一致的,不再赘述。

两种写法

  1. 使用Id字段的半连接例子:
ELECT Id, (Select Id, Amount, CloseDate FROM Opportunities)
FROM Account
WHERE Id NOT IN
  (
    SELECT AccountId
    FROM Contact
    WHERE LastName LIKE 'Trump%'
  )
  1. 使用外键的半连接例子:
SELECT Id, Name, Phone, Email
FROM Contact
WHERE AccountId NOT IN 
  (
    SELECT AccountId
    FROM Opportunity
    WHERE CloseDate < Last_Week
  )

限制

  1. 在主语句的WHERE分句中,不能超过2INNOT IN语句
  2. 不能在半连接或者反连接前使用NOT操作符。若有这种情况,可以考虑把半连接转换为反连接,或者反连接转换为全连接
  3. 必须是Id字段或者外键,诸如... Where Account.Id IN (...)是非法的
  4. 子查询的查询字段必须与主查询对应的字段SObject一致,是且必须是对应的外键
  5. 子查询的字段条数不受限制,但是主查询条数仍收到限制
  6. 半连接或者反连接不能嵌套使用,即不能在半连接/反连接中再使用半连接或者反连接
  7. 子查询不能使用OR
  8. 子查询不支持COUNT, 'FOR UPDATE, ORDER BY, LIMIT`
  9. 下列对象不支持:

    • ActivityHistory
    • Attachments
    • Event
    • EventAttendee
    • Note
    • OpenActivity
    • Tags (AccountTag, ContactTag, and all other tag objects)
    • Task

详情可以查看SOQL and SOSL Reference以下章节:
Semi-Joins with IN and Anti-Joins with NOT IN

original source: Mutual Fund Explorer: A New Lightning Components Sample Application

Highlight

  • Caching data with storable actions
  • Caching data with a custom cache
  • Creating a dropdown box from picklist values
  • Creating a dropdown box from a list of records
  • Event bubbling
  • Using application events
  • Using component events
  • Using a third-party JavaScript library
  • Using bound vs unbound expressions
  • Building admin-friendly components

Related Github repo: sfdx-dreaminvest

The code is an improvement of this article.

  1. The main visualForce page AddmultipleAccounts code:
<apex:page controller="AddmultipleAccountsController">
<apex:form>
<apex:pageBlock title="Add multiple account" id="main_block">
<apex:pageBlockTable value="{!listAccount}" var="acc">
<apex:column headerValue="Account Name">
    <apex:inputField value="{!acc.Name}"/>
</apex:column>
<apex:column headerValue="Account Type">
    <apex:inputField value="{!acc.Type}"/>
</apex:column>
<apex:column headerValue="Industry">
    <apex:inputField value="{!acc.Industry}"/>
</apex:column>
    <!-- you can add more column here -->
</apex:pageBlockTable>
<apex:pageBlockButtons>
<apex:commandButton value="Add 1 Account Row" action="{!addAccount}" reRender="main_block"/>
<apex:commandButton value="Add 5 Account Rows" action="{!addAccounts}" reRender="main_block" />
<apex:commandButton value="Save Accounts" action="{!saveAccounts}" />
</apex:pageBlockButtons>
</apex:pageBlock>
</apex:form>
</apex:page>
  1. After insert the records, there will be a simple page to show the result, the page AllAccountsSaved will show the result, you can make more work on this page. the code:
<apex:page sidebar="false" showHeader="true">
<center>
    <h3> Congrats your accounts have been successfully saved! </h3>
</center>
</apex:page>
  1. Here the custom controller AddmultipleAccountsController code:

    public with sharing class AddmultipleAccountsController {

    public list<Account> listAccount{get;set;}
    
    public AddmultipleAccountsController() {
        listAccount = new list<Account>();
        listAccount.add(new Account());
    }
    
    //add one account at a time
    public void addAccount() {
        listAccount.add(new Account());
    }
    
    //add five account at a time
    public void addAccounts() {
        for(Integer j=0; j < 5; j++) {
        listAccount.add(new Account());
        }
    }
    
    public PageReference saveAccounts() {
        try {
            insert listAccount;
        } catch(System.DmlException e) {
            for(Integer i=0; i < e.getNumDml(); i++) {
                System.debug(e.getDmlMessage(i));
            }
        }
        return Page.AllAccountsSaved;
    }

    }

  2. A test method AddmultipleAccountsControllerTest for the custom controller above, code:
@isTest
private class AddmultipleAccountsControllerTest {

    static testMethod void testAddAccount() {
         Test.startTest();
         AddmultipleAccountsController aacontroller = new AddmultipleAccountsController();
         aacontroller.addAccount();
         list<Account> listAccount = aacontroller.listAccount;
         Test.stopTest();
         system.assert(listAccount.size() == 2);
    }

    static testMethod void testAddAccounts() {
         Test.startTest();
         AddmultipleAccountsController aacontroller = new AddmultipleAccountsController();
         aacontroller.addAccounts();
         list<Account> listAccount = aacontroller.listAccount;
         Test.stopTest();
         system.assert(listAccount.size() == 6);
    }

    static testMethod void testSaveAccounts() {
        Test.startTest();
         AddmultipleAccountsController aacontroller = new AddmultipleAccountsController();
         aacontroller.listAccount[0].Name = 'lushang test account';
         aacontroller.saveAccounts();
         Test.stopTest();
         Account[] acc = [SELECT id,name from Account where name = 'lushang test account'];
         system.assert(acc.size() == 1);
    }
}
  1. Create a visualForce Tab for the page, the user can use this tab to add multiple records.
  • You can change the tab permission settings in certain profile's "Object Settings"
  • You can translate the tab name in the Translation Workbench's translate bar, section is : "Web Tab"