Регистрация  |  Вход

Глупый вопрос

Господа! Я прошу прощения за столь глупый вопрос, но тема "наивные вопросы" закрыта, а книжки по Java и интернет мне не отвечают. Короче, нужен мне триггер, который ставит галочку в поле Х (response__c - это чекбокс), при значении поля У (number__c) больше нуля. Вот так он выглядит у меня:


trigger chkbx on blablabla_s (before insert) {
if (Number__c >0) {
Response__c = true;
}

}
Т.к. мой модный триггер абсолютно не похож, на все триггеры, что я видел в интернете, очевидно, что я делаю неправильно что-то (или вообще всё). Но почему, понять не могу. Вот вам if, вот присвоение, в других моих неловких попытках мучить циклы всё нормально получается, а здесь - никак!
Спасибо.

Господа! Я прошу прощения за столь глупый вопрос, но тема "наивные вопросы" закрыта, а книжки по Java и интернет мне не отвечают. Короче, нужен мне триггер, который ставит галочку в поле Х (response__c - это чекбокс), при значении поля У (number__c) больше нуля. Вот так он выглядит у меня:


 trigger chkbx on blablabla_s (before insert) {  
        if (Number__c >0) {  
       Response__c = true;  
     }  

 }  
Т.к. мой модный триггер абсолютно не похож, на все триггеры, что я видел в интернете, очевидно, что я делаю неправильно что-то (или вообще всё). Но почему, понять не могу. Вот вам if, вот присвоение, в других моих неловких попытках мучить циклы всё нормально получается, а здесь - никак! 
Спасибо.

Привет.

Для этой задачи я бы не стал использовать триггер. Триггер не так просто пишется. Он должен учитывать что к нему придет сразу пачка (list) объектов.

Если response__c - это чекбокс ВСЕГДА будет выставляться автоматически и не требует ручного вмешательства, то смотри в сторону Formula Field.

Елси ручное выставление предусматривается, но при определенных условиях нужно этот процесс автоматизировать, то посмотри в сторону Workflow - там есть Action - Field Update. Т.е. задаешь Rule Criteria при котором срабатываем Workflow и в Action - Field Update указываешь выставить чекбокс в True.

Елси нужен именно триггер, то это будет выглядеть так:

trigger chkbx on blablabla_s (before insert) { 
for (blablabla_s bla : Trigger.New){
if (bla.Number__c >0) {
bla.Response__c = true;
}
}
}

как-то так.

Привет.

Для этой задачи я бы не стал использовать триггер. Триггер не так просто пишется. Он должен учитывать что к нему придет сразу пачка (list) объектов.

Если response__c - это чекбокс ВСЕГДА будет выставляться автоматически и не требует ручного вмешательства, то смотри в сторону [b]Formula Field[/b].

Елси ручное выставление предусматривается, но при определенных условиях нужно этот процесс автоматизировать, то посмотри в сторону [b]Workflow - там есть Action - Field Update[/b]. Т.е. задаешь Rule Criteria при котором срабатываем Workflow и в Action - Field Update указываешь выставить чекбокс в True.

Елси нужен именно триггер, то это будет выглядеть так:

[code]
trigger chkbx on blablabla_s (before insert) { 
  for (blablabla_s bla : Trigger.New){
    if (bla.Number__c >0) { 
      bla.Response__c = true; 
    }
  }
} 
[/code]

как-то так.

Ага, стало ясно, что apex не такой уж javaобразный, как мне казалось! Зато понял примерно, что не так с моими триггерам вообще!
Спасибо!

Ага, стало ясно, что apex не такой уж javaобразный, как мне казалось! Зато понял примерно, что не так с моими триггерам вообще! 
Спасибо!

Да, Apex хоть и вырос из java, но у них много отличий в основном связанных с самой платформой Salesforce. Если собираешься пользоваться apex, то лучше изучить его отдельно по Workbook и уж точно не искать ответы в документации java.

Да, Apex хоть и вырос из java, но у них много отличий в основном связанных с самой платформой Salesforce. Если собираешься пользоваться apex, то лучше изучить его отдельно по Workbook и уж точно не искать ответы в документации java.

NOWICKED
Ага, стало ясно, что apex не такой уж javaобразный, как мне казалось! Зато понял примерно, что не так с моими триггерам вообще!
Спасибо!

Тригеры - это особо важная тема.

Сначала прочитай о них в salesforce APEX workbook.

Затем в salesforce APEX guide можно посмотреть как делать тригеры более устойчивыми к ситуации, что в тригер пришло сразу много записей. Особено важно не ставить в основном цикле тригера DML операции и SELECTы. В APEX guide все прекрасно описано.

Также чувствовать разницу в before and after, которая заключается в разных возможностях того, что можно получить из записи или вновь сделать с ней.

[quote="NOWICKED"]Ага, стало ясно, что apex не такой уж javaобразный, как мне казалось! Зато понял примерно, что не так с моими триггерам вообще! 
Спасибо![/quote]

Тригеры - это особо важная тема.

Сначала прочитай о них в salesforce APEX workbook. 

Затем в  salesforce  APEX guide можно посмотреть как делать тригеры более устойчивыми к ситуации, что в тригер пришло сразу много записей. Особено важно не ставить в основном цикле тригера DML операции и SELECTы. В APEX guide все прекрасно описано. 

Также чувствовать разницу в before and after, которая заключается в разных возможностях того, что можно получить из записи или вновь сделать с ней.

Ну и в конце подумай над таким вопросом:
"А что если..."

Например: А что, если пачка записей, а тебе и вовсе не для всех надо обновлять...или А что, если на стандартном лэйауте при Save тебе надо немного другая логика в триггере...

Ну и в конце подумай над таким вопросом:
"А что если..."

Например: А что, если пачка записей, а тебе и вовсе не для всех надо обновлять...или А что, если на стандартном лэйауте при Save тебе надо немного другая логика в триггере...

Off -topic:
на странице со списком тем у пользователей одни имена (вероятно сообщества), а в самой теме - другие (форумные). Не знаю правильно ли это.

Off -topic:
на странице со списком тем у пользователей одни имена (вероятно сообщества), а в самой теме - другие (форумные). Не знаю правильно ли это.

Off -topic:
на странице со списком тем у пользователей одни имена (вероятно сообщества), а в самой теме - другие (форумные). Не знаю правильно ли это.
неправильно, починю. Спасибо что сказал.

[quote]Off -topic:
на странице со списком тем у пользователей одни имена (вероятно сообщества), а в самой теме - другие (форумные). Не знаю правильно ли это.[/quote]
:) неправильно, починю. Спасибо что сказал.

Спасибо за ответы, мне приятно, что вы серьёзно считаете, будто я сейчас могу понять что нужно для создания более устойчивых триггеров, но познания мои всё ещё невероятно скудны и сейчас я это продемонстрирую!
Вот создал я объект, вывел его на панель, все замечательно, а как теперь мне сделать следующее:
"Apex Class должен быть покрыт тест методами на 75% или выше"?
Т.е. я знаю про тесты эти, но какой класс имела в виду эта милая дама? Какой именно класс? Где мне искать его?!
P.S. Перечитал и ужаснулся от того, как коряво я выражаю мысли. Парни, просто поясните на пальцах, где мне нужно искать, если кто-то вдруг случайно понял, что я имею в виду.

Спасибо за ответы, мне приятно, что вы серьёзно считаете, будто я сейчас могу понять что нужно для создания более устойчивых триггеров, но познания мои всё ещё невероятно скудны и сейчас я это продемонстрирую!
Вот создал я объект, вывел его на панель, все замечательно, а как теперь мне сделать следующее:
"Apex Class должен быть покрыт тест методами на 75% или выше"?
Т.е. я знаю про тесты эти, но какой класс имела в виду эта милая дама? Какой именно класс? Где мне искать его?!
P.S. Перечитал и ужаснулся от того, как коряво я выражаю мысли. Парни, просто поясните на пальцах, где мне нужно искать, если кто-то вдруг случайно понял, что я имею в виду.

Глупых вопросов не бывает Так что задавай смело - всем понятно что это процесс обучения, все через это проходят.

"Apex Class должен быть покрыт тест методами на 75% или выше"?

Тут все очень просто. Все классы, а также триггеры, которые ты переносишь на продакшен, должны быть покрыты тестами на 75%.
Т.е. берешь бумажку, открываешь eclipse и выписываешь имена всех классов и триггеров которые ты делал. Дальше открываешь Apex Classes в Setup и нажимаешь Run All Tests. После этого идешь в Developer Console на вкладку Tests. В правом нижнем углу будет список классов и триггеров с процентами покрытия. Ищи там свои классы и выписывай на бумажку проценты. Если класса или триггера нет, то ставь возле него 0%.

После этого будет видно какие классы у тебя не покрыты тестами. Остается их написать.

Глупых вопросов не бывает :) Так что задавай смело - всем понятно что это процесс обучения, все через это проходят.

[code]"Apex Class должен быть покрыт тест методами на 75% или выше"?[/code]
Тут все очень просто. Все классы, а также триггеры, которые ты переносишь на продакшен, должны быть покрыты тестами на 75%. 
Т.е. берешь бумажку, открываешь eclipse и выписываешь имена всех классов и триггеров которые ты делал. Дальше открываешь Apex Classes в Setup и нажимаешь Run All Tests. После этого идешь в Developer Console на вкладку Tests. В правом нижнем углу будет список классов и триггеров с процентами покрытия. Ищи там свои классы и выписывай на бумажку проценты. Если класса или триггера нет, то ставь возле него 0%.

После этого будет видно какие классы у тебя не покрыты тестами. Остается их написать.

тестирование - это большая тема.

в блоге есть небольшая статья об этом, но ищи и другие в интернете.

суть тестирования - програмно симитировать рабочую ситуацию и заставить (тестируемый) код отработать И сколько строк твоего кода отработало в результате этого тестирования - такое и покрытие.

применительно к тригеру ты должен симитировать ситуацию при которой он в норме и срабатывает.
для этого програмно создаются запись(и) с трубемыми данными для объекта на котором стоит тригер.
и выполянй ДМЛ операцию - вставить. обновить - и твой тригер сработвает. Это и есть твой тест-класс.

результат будет, например, в описании тригера - там есть пунтк - покрытие тестами.

физически эту работу (запуск тригера) можно выполнять в орге, в косоли разработчика или проще всего в эклипсе, так как там проще настраивать тест класс - а настраивать его придется.

тестирование - это не небольшая жанровая тема, это одна из ключевых тем, поэтому на его изучение нужно потратить время и это стоит того

тестирование - это большая тема.

в блоге есть небольшая статья об этом, но ищи и другие в интернете.

суть тестирования - програмно симитировать рабочую ситуацию и заставить (тестируемый) код отработать И сколько строк твоего кода отработало в результате этого тестирования - такое и покрытие.

применительно к тригеру ты должен [b]симитировать ситуацию при которой он в норме и срабатывает[/b].
для этого [b]програмно[/b] создаются запись(и) с трубемыми данными для объекта на котором стоит тригер.
и выполянй ДМЛ операцию - вставить. обновить  - и твой тригер сработвает. Это и есть твой тест-класс.

результат будет, например, в описании тригера - там есть пунтк - покрытие тестами.

физически эту работу (запуск тригера) можно выполнять в орге, в косоли разработчика или проще всего в эклипсе, так как там проще настраивать тест класс  - а настраивать его придется. 

тестирование - это не небольшая жанровая тема, это одна из ключевых тем, поэтому на его изучение нужно потратить время  и это стоит того

Раз уж про триггеры... Если есть два триггера, обращающиеся к одному методу классу (метод соответственно делает одно и тоже, только для разных кастом обжектов), то правильно будет в методе на вход указать

public static void addToStore(List<sObject> obj)
???

Раз уж про триггеры... Если есть два триггера, обращающиеся к одному  методу классу (метод соответственно делает одно и тоже, только для разных кастом обжектов), то правильно будет в методе  на вход указать
 [code] public static void addToStore(List<sObject> obj) [/code]  ???

Привет! Да. Можно. И даже, если ты это видишь, то это хорошо)

Привет! Да. Можно. И даже, если ты это видишь, то это хорошо)

Согласен. sObject использовать хорошая практика. Сам часто применяю в проектах. Просто потом нужно сделать приведение к типу чтобы использовать, но это уже нюансы.

Согласен. sObject использовать хорошая практика. Сам часто применяю в проектах. Просто потом нужно сделать приведение к типу чтобы использовать, но это уже нюансы.

Еще вопрос есть. Если кому не лень, объясните доходчиво. У меня есть две таблицы, нужно выбрать данные из одной таблицы соответствующие данным из другой. Джоинов нет, SOQL или DML запрос в цикле нельзя(как и метод в котором есть такое), короче печаль-беда. Почитал http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#CSHID=apex_dynamic_soql.htm|StartTopic=Content%2Fapex_dynamic_soql.htm|SkinName=webhelp Использование мапа осложняется тем, что из одной таблиц надо получить 3 значения(ID-ключ, начальная дата и конечная-значения), хочется живого, так сказать объяснения.

Еще вопрос есть. Если кому  не лень, объясните доходчиво. У меня есть две таблицы, нужно выбрать данные из одной таблицы соответствующие данным из другой. Джоинов нет, SOQL или DML запрос в цикле нельзя(как и метод в котором есть такое), короче печаль-беда. Почитал [url]http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#CSHID=apex_dynamic_soql.htm|StartTopic=Content%2Fapex_dynamic_soql.htm|SkinName=webhelp[/url]  Использование мапа осложняется тем, что из одной таблиц надо получить 3 значения(ID-ключ, начальная дата и конечная-значения), хочется живого, так сказать объяснения.

А как связаны данные из двух таблиц? Вернее на Salesforce принято говорить как связаны два объекта?
Lookup?
то что ты почитал по ссылке не то что надо.
Тебе надо изучить
Relationship Queries

А как связаны данные из двух таблиц? Вернее на Salesforce принято говорить как связаны два объекта?
Lookup?
то что ты почитал по ссылке не то что надо.
Тебе надо изучить 
[url=http://www.salesforce.com/us/developer/docs/soql_sosl/Content/sforce_api_calls_soql_relationships.htm]Relationship Queries[/url]

Связь только в том, что в одном объекте есть id от другого (конкретно у каждого объекта Product есть Id склада ). Так вот, смысл в том, что когда приходит пачка продуктов, срабатывает триггер (before insert), который вызывает метод класса и вот в нем, переданная ему пачка продуктов(List) должна по своей дате создания, попасть на склад, у которого есть дата создания и дата окончания хранения. Отсюда и вопрос, ведь дата создания продукта должна попасть в промежуток действия склада, соответственно все продукты, надо прогнать по складам и найти соответствующий. Прошу пардона за полотно, но после Джавы у меня разрыв шаблонов :D

Связь только в том, что в одном объекте есть id от другого (конкретно у каждого объекта Product есть Id склада ). Так вот, смысл в том, что когда приходит пачка продуктов, срабатывает триггер (before insert), который вызывает метод класса и вот в нем, переданная ему пачка продуктов(List) должна по своей дате создания, попасть на склад, у которого есть дата создания и дата окончания хранения. Отсюда и вопрос, ведь дата создания продукта должна попасть в промежуток действия склада, соответственно все продукты, надо прогнать по складам и найти соответствующий. Прошу пардона за полотно, но после Джавы у меня разрыв шаблонов :D

Я так понимаю, делаешь джуниорское задание, где есть объекты Store, Product итд... Там склад и продукт связаны как Master Detail
Я его тоже делал не так давно сравнительно. Причем в первый раз я сделал абсолютно по дилетантски - в цикле были и запросы и DML. Когда пришла пора вставить пачку из 1000 записей, все естественно вывалилось по лимитам. Тут то и пришлось придумать правильный ответ.
алгоритм реализации несложен - сперва генерируем динамический запрос вида

'SELECT Id, Start_period__c, End_period__c FROM Store__c WHERE ....'
, чтобы получить лист всех подходящих складов для пачки продуктов, затем создаем новые склады для тех продуктов, которые не попадают ни в один склад. а потом просто разбрасываем всю пачку по получившимся складам. Придется в ходе реализации немного поизворачиваться, но весь триггер лично у меня занял всего около 50 строк кода

Я так понимаю, делаешь джуниорское задание, где есть объекты Store, Product итд... Там склад и продукт связаны как Master Detail
Я его тоже делал не так давно сравнительно. Причем в первый раз я сделал абсолютно по дилетантски - в цикле были и запросы и DML. Когда пришла пора вставить пачку из 1000 записей, все естественно вывалилось по лимитам. Тут то и пришлось придумать правильный ответ. 
алгоритм реализации несложен - сперва генерируем динамический запрос вида 
[code]'SELECT Id, Start_period__c, End_period__c FROM Store__c WHERE ....'[/code], чтобы получить лист всех подходящих складов для пачки продуктов, затем создаем новые склады для тех продуктов, которые не попадают ни в один склад. а потом просто разбрасываем всю пачку по получившимся складам. Придется в ходе реализации немного поизворачиваться, но весь триггер лично у меня занял всего около 50 строк кода

Связь только в том, что в одном объекте есть id от другого

Это не Salesforce подход или ты не теми словами описал.
В Salesforce есть Lookup и Master-Details связи. Они отвечают за связывание объектов и по ним осуществляется доступ к родителям или детям в SQOL запросе. Для тех кто пришел из обычного SQL будет необычно. Тоже самое можно сказать при переходе из Salesforce в обычный SQL.

[quote]Связь только в том, что в одном объекте есть id от другого [/quote]
Это не Salesforce подход :) или ты не теми словами описал.
В Salesforce есть Lookup и Master-Details связи. Они отвечают за связывание объектов и по ним осуществляется доступ к родителям или детям в SQOL запросе. Для тех кто пришел из обычного SQL будет необычно. Тоже самое можно сказать при переходе из Salesforce в обычный SQL.

Это не Salesforce подход

Это точно, стараюсь меняться <!-- s:mrgreen: --><img src="{SMILIES_PATH}/icon_mrgreen.gif" alt=":mrgreen:" title="Зелёный" /><!-- s:mrgreen: -->

[quote]Это не Salesforce подход[/quote]
Это точно, стараюсь меняться <!-- s:mrgreen: --><img src="{SMILIES_PATH}/icon_mrgreen.gif" alt=":mrgreen:" title="Зелёный" /><!-- s:mrgreen: -->

Если я правильно понял автора, то эта ситуация описана как в учебника по апекс, так и в гайде по апекс в разделе Лимиты.

вот пример плохого варианта:

trigger LimitExample on Invoice_Statement__c (before insert, before update) {
for(Invoice_Statement__c inv : Trigger.new) {
// This SOQL query executes once for each item in Trigger.new.
// It gets the line items for each invoice statement.
List<Line_Item__c> liList = [SELECT Id,Units_Sold__c,Merchandise__c
FROM Line_Item__c
WHERE Invoice_Statement__c = :inv.Id];
for(Line_Item__c li : liList) {
// Do something
}
}
}

как видим нужные связанные записи просто наивно кверятся в "основном цикле" тригера.

а вот правильный вариант:

trigger EnhancedLimitExample on Invoice_Statement__c (before insert, before update) {
// Perform SOQL query outside of the for loop.
// This SOQL query runs once for all items in Trigger.new.
List<Invoice_Statement__c> invoicesWithLineItems =
[SELECT Id,Description__c,(SELECT Id,Units_Sold__c,Merchandise__c from Line_Items__r)
FROM Invoice_Statement__c WHERE Id IN :Trigger.newMap.KeySet()];

for(Invoice_Statement__c inv : invoicesWithLineItems) {
for(Line_Item__c li : inv.Line_Items__r) {
// Do something
}
}
}

как видно в нем нет вообще "классичекого" тригерного цикла.

все кверится одним махом с помощью IN :Trigger.newMap.KeySet()

а потом цииклом-в-цикле ты делаешь что хочешь с этими полученными записями


--------------
есть еще второй, более хитрый вариант в апекс гайде раздел Common Bulk Trigger Idioms:

// When a new line item is added to an opportunity, this trigger copies the value of the

// associated product's color to the new record.

trigger oppLineTrigger on OpportunityLineItem (before insert) {

// For every OpportunityLineItem record, add its associated pricebook entry
// to a set so there are no duplicates.
Set<Id> pbeIds = new Set<Id>();
for (OpportunityLineItem oli : Trigger.new)
pbeIds.add(oli.pricebookentryid);

// Query the PricebookEntries for their associated product color and place the results
// in a map.
Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry>(
[select product2.color__c from pricebookentry
where id in :pbeIds]);

// Now use the map to set the appropriate color on every OpportunityLineItem processed
// by the trigger.
for (OpportunityLineItem oli : Trigger.new)
oli.color__c = entries.get(oli.pricebookEntryId).product2.color__c;
}

Если я правильно понял автора, то эта ситуация описана как в учебника по апекс, так и в гайде по апекс в разделе Лимиты.

вот пример плохого варианта:
[code]trigger LimitExample on Invoice_Statement__c (before insert, before update) {
    for(Invoice_Statement__c inv : Trigger.new) {
        // This SOQL query executes once for each item in Trigger.new.
        // It gets the line items for each invoice statement.
        List<Line_Item__c> liList = [SELECT Id,Units_Sold__c,Merchandise__c 
                                     FROM Line_Item__c 
                                     WHERE Invoice_Statement__c = :inv.Id];
        for(Line_Item__c li : liList) {
            // Do something
        }
    }
}[/code]

как видим нужные связанные записи просто наивно кверятся в "основном цикле" тригера.

а вот правильный вариант:
[code]
trigger EnhancedLimitExample on Invoice_Statement__c (before insert, before update) {
    // Perform SOQL query outside of the for loop.
    // This SOQL query runs once for all items in Trigger.new.
    List<Invoice_Statement__c> invoicesWithLineItems = 
        [SELECT Id,Description__c,(SELECT Id,Units_Sold__c,Merchandise__c from Line_Items__r)
         FROM Invoice_Statement__c WHERE Id IN :Trigger.newMap.KeySet()];
    
    for(Invoice_Statement__c inv : invoicesWithLineItems) {
        for(Line_Item__c li : inv.Line_Items__r) {
            // Do something
        }
    }
}[/code]

как видно в нем нет вообще "классичекого" тригерного цикла.

все кверится одним махом с помощью IN :Trigger.newMap.KeySet()

а потом цииклом-в-цикле ты делаешь что хочешь с этими полученными записями


--------------
есть еще второй, более хитрый вариант в апекс  гайде раздел Common Bulk Trigger Idioms:

[code]// When a new line item is added to an opportunity, this trigger copies the value of the
                        
// associated product's color to the new record.
                        
trigger oppLineTrigger on OpportunityLineItem (before insert) {

    // For every OpportunityLineItem record, add its associated pricebook entry
    // to a set so there are no duplicates.
    Set<Id> pbeIds = new Set<Id>();
    for (OpportunityLineItem oli : Trigger.new) 
        pbeIds.add(oli.pricebookentryid);

    // Query the PricebookEntries for their associated product color and place the results
    // in a map.
    Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry>(
        [select product2.color__c from pricebookentry 
         where id in :pbeIds]);
         
    // Now use the map to set the appropriate color on every OpportunityLineItem processed
    // by the trigger.
    for (OpportunityLineItem oli : Trigger.new) 
        oli.color__c = entries.get(oli.pricebookEntryId).product2.color__c;  
}[/code]