Two Criterias

Robust

Well-known member
So I want to have two criterias:
  • Criteria to ACHIEVE something
  • Criteria to be ABLE to achieve something
When I do this:

Code:
        $viewParams = array(
            'task' => $task,
            'userCriteria' => XenForo_Helper_Criteria::prepareCriteriaForSelection($task['user_criteria']),
            'userCriteriaData' => XenForo_Helper_Criteria::getDataForUserCriteriaSelection(),
            'visibleCriteria' => XenForo_Helper_Criteria::prepareCriteriaForSelection($task['visible_criteria']),
            'visibleCriteriaData' => XenForo_Helper_Criteria::getDataForUserCriteriaSelection()
        );

If I view the visibleCriteria tab (obviously you can't state what criteria param to use) they both sync, but the second one is a bit buggy (e.g. you can click usergroups when the checkbox isn't ticked).

Is there no way to have two criterias on one "task"?
 
xen:map doesn't seem to work because the criteria vars are still the same, so the second criteria bunch won't work. When they're processed through XenForo_Input, they're both user_criteria, but this time only the first one has the vars set, the second is just empty criteria.

Is this even possible? How?

Sorry for being so buggy, it's just I can't figure it out and it's kind of important.
 
I use my item criteria helper two ways, one to determine if a user's character can purchase a specific item, and another if a character can actually equip it.

You need to send a different argument for each time you call your criteria method; one for the user and one to determine if it is going to be visible to the user or not. That is how my shop add-on works to determine if a user meets the criteria to purchase a specific item (ie: belongs to the proper usergroup), and then equip it (ie: has the required strength, or is a wizard, etc). What you want to achieve doesn't sound much different than how I am using my criteria helper.
 
I use my item criteria helper two ways, one to determine if a user's character can purchase a specific item, and another if a character can actually equip it.

You need to send a different argument for each time you call your criteria method; one for the user and one to determine if it is going to be visible to the user or not. That is how my shop add-on works to determine if a user meets the criteria to purchase a specific item (ie: belongs to the proper usergroup), and then equip it (ie: has the required strength, or is a wizard, etc). What you want to achieve doesn't sound much different than how I am using my criteria helper.
Late following this up. I didn't remember this ever being responded to.

Sounds about the same though, what we were trying to do.

Here's some code snippets:

Code:
    <ul id="taskPanes">
        <li>
/* other stuff */
        </li>
        <li>
            <xen:include template="helper_criteria_user">
                <xen:map from="$achieveCriteria" to="$userCriteria" />
                <xen:map from="$achieveCriteriaData" to="$userCriteriaData" />
                <xen:set var="$criteriaHintHtml">{xen:phrase aupp_user_must_meet_all_criteria}</xen:set>
            </xen:include>
            <xen:include template="helper_criteria_user_date">
                <xen:map from="$achieveCriteria" to="$userCriteria" />
                <xen:map from="$achieveCriteriaData" to="$userCriteriaData" />
                <xen:set var="$noDateFields">1</xen:set>
            </xen:include>
        </li>
        <li>
            <xen:include template="helper_criteria_user_field">
                <xen:map from="$achieveCriteria" to="$userCriteria" />
                <xen:map from="$achieveCriteriaData" to="$userCriteriaData" />
            </xen:include>
        </li>
        <li>
            <xen:include template="helper_criteria_user">
                <xen:map from="$visibleCriteria" to="$userCriteria" />
                <xen:map from="$visibleCriteriaData" to="$userCriteriaData" />
                <xen:set var="$criteriaHintHtml">{xen:phrase aupp_user_must_meet_all_criteria}</xen:set>
            </xen:include>
            <xen:include template="helper_criteria_user_date">
                <xen:map from="$visibleCriteria" to="$userCriteria" />
                <xen:map from="$visibleCriteriaData" to="$userCriteriaData" />
                <xen:set var="$noDateFields">1</xen:set>
            </xen:include>
        </li>
        <li>
            <xen:include template="helper_criteria_user_field">
                <xen:map from="$visibleCriteria" to="$userCriteria" />
                <xen:map from="$visibleCriteriaData" to="$userCriteriaData" />
            </xen:include>
        </li>
    </ul>

However, when saving, 'user_criteria' is populated properly with the criteria options while 'visible_criteria' is an empty array.

Code:
        $viewParams = array(
            'task' => $task,
            'achieveCriteria' => XenForo_Helper_Criteria::prepareCriteriaForSelection($task['user_criteria']),
            'achieveCriteriaData' => XenForo_Helper_Criteria::getDataForUserCriteriaSelection(),
            'visibleCriteria' => XenForo_Helper_Criteria::prepareCriteriaForSelection($task['visible_criteria']),
            'visibleCriteriaData' => XenForo_Helper_Criteria::getDataForUserCriteriaSelection()
        );

I even have some redundant code there to be sure, but it doesn't work. The criteria to view (aka to be able to achieve) is always just an empty array.

Probably because the variable name is hardcoded: name="user_criteria[is_guest][rule]"

No way to change what it's bound to.
 
The criteria I use is custom for the armory (shops) I have created, so I may not be that helpful as I never had the need to use/extend existing ones. I created mine the same way XF handles them:

First step is to prepare each criteria for the preSave, if they exist.

PHP:
    public static function prepareCriteriaForSave($criteria)
    {
        $criteria = self::unserializeCriteria($criteria);

        $criteriaPreSave = array();
        foreach ($criteria AS $itemCriteria)
        {
            if (!empty($itemCriteria['rule']))
            {
                if (empty($itemCriteria['data']) || !is_array($itemCriteria['data']))
                {
                    $itemCriteria['data'] = array();
                }

                $criteriaPreSave[] = array(
                    'rule' => $itemCriteria['rule'],
                    'data' => $itemCriteria['data']
                );
            }
        }
        return $criteriaPreSave;
    }

Next, when a user visits the shop page, each items criteria (if any), is prepared to be checked against:

PHP:
    public static function prepareCriteriaForSelection($criteria)
    {
        $criteria = self::unserializeCriteria($criteria);

        $output = array();
        foreach ($criteria AS $itemCriteria)
        {
            $data = (!empty($itemCriteria['data']) ? $itemCriteria['data'] : true);
            $output[$itemCriteria['rule']] = $data;
        }
        return $output;
    }

after that, the criteria for the item is checked against the users data (usergroup, user id, created character class, race, gender, etc). Below is a snippet of the code I use. All I want is a true or false to be returned as quickly as possible.

PHP:
    public static function charMatchesCriteria($criteria, array $charData = null, $visitorsName = '', $secondaryGroups = '')
    {
        if (!$criteria = self::unserializeCriteria($criteria))
        {
            return true; // no criteria
        }

        foreach ($criteria AS $criterion)
        {
            $data = $criterion['data'];

            switch ($criterion['rule'])
            {
                case 'username':
                    if (utf8_strtolower($data['name']) != utf8_strtolower($visitorsName))
                    {
                        return false;
                    }
                    else
                    {
                        return true; // made for a member, no other criteria matters
                    }
                break;
                case 'usergroup':
                    $secondaryGroups = ($secondaryGroups ? explode(',', $secondaryGroups) : array());

                    if (!in_array($data['user_group_id'], $secondaryGroups))
                    {
                        return false;
                    }
                break;
                case 'strength_score':
                case 'dexterity_score':
                case 'constitution_score':
                case 'intelligence_score':
                case 'wisdom_score':
                case 'charisma_score':
                    $abilityKey = substr($criterion['rule'], 0, strlen($criterion['rule']) -6);

                    if ($charData['iwd_stats'][$abilityKey]['score'] < $data['score'])
                    {
                        return false;
                    }
                break;

... and a few more checks...

For each item a user may be able to purchase, the above check is done. If false, the item can not be selected for purchase (or is not visible depending if it is made for a specific usergroup or person), if true, it is displayed for possible purchase.

Just a note: I do not use any template helpers for criteria checks as they are completed before the data is sent to the appropriate template.

I don't know if the above will help, as it is used for page callbacks (to generate a list of displayed-able items for purchase), and then again for the controller public part to ensure a user is not trying to cheat.
 
I see your method, @Lawrence, but in my case the main problem is the trophy template only passes data under the user_criteria array in the form request, so there is only one variable regardless of there being 2 criteria.

@Chris D @Mike Sorry for tagging you, but really need some assistance here if you have any idea.
 
Top Bottom