Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Squad other skill level #675

Merged
merged 2 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 80 additions & 8 deletions src/Models/Filterable.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ final public function isEligible(Model $member): bool
{
// in case no filters exists, everyone should be allowed
// this is the case with manual squads
if (! property_exists($this->getFilters(), 'and') && ! property_exists($this->getFilters(), 'or'))
if (!property_exists($this->getFilters(), 'and') && !property_exists($this->getFilters(), 'or'))
return true;

$query = new QueryGroupBuilder($member->newQuery(), true);
Expand All @@ -71,6 +71,32 @@ final public function isEligible(Model $member): bool
return $query->getUnderlyingQuery()->exists();
}

/**
* Print the sql query that will be generated to check for user eligibility.
* This is for developmental testing and tinkering.
*
* @param Model $member The entity to check
* @return string The sql query that checks eligibility
*
* @throws InvalidFilterException If a invalid filter configuration is used
*/
final public function printUnderlyingQuery(Model $member): string
{
$query = new QueryGroupBuilder($member->newQuery(), true);

// make sure we only allow results of the entity we are checking count
$query->where(function (Builder $inner_query) use ($member) {
$inner_query->where($member->getKeyName(), $member->getKey());
});

// wrap this in an inner query to ensure it is '(correct_entity_check) AND (rule1 AND/OR rule2)'
$query->where(function ($inner_query) {
$this->applyGroup($inner_query, $this->getFilters());
});

return $query->getUnderlyingQuery()->toSql();
}

/**
* Applies a filter group to $query.
*
Expand All @@ -82,13 +108,21 @@ final public function isEligible(Model $member): bool
private function applyGroup(Builder $query, stdClass $group): void
{
$query_group = new QueryGroupBuilder($query, property_exists($group, 'and'));

$rules = $query_group->isAndGroup() ? $group->and : $group->or;

foreach ($rules as $rule){
foreach ($rules as $rule) {
// check if this is a nested group or not
if(property_exists($rule, 'path')){
$this->applyRule($query_group, $rule);
if (property_exists($rule, 'path')) {
if ($rule->name == "skill_level" ) {
// Now get all the skill rules in this group
foreach ($rules as $skrule) {
if ($skrule->name == "skill") {
$this->applySkillLevelRule($query_group, $rule, $skrule);
}
}
} else {
$this->applyRule($query_group, $rule);
}
} else {
// this is a nested group
$query_group->where(function ($group_query) use ($rule) {
Expand All @@ -106,9 +140,10 @@ private function applyGroup(Builder $query, stdClass $group): void
*
* @throws InvalidFilterException
*/
private function applyRule(QueryGroupBuilder $query, stdClass $rule): void {
private function applyRule(QueryGroupBuilder $query, stdClass $rule): void
{
// 'is' operator
if($rule->operator === '=' || $rule->operator === '<' || $rule->operator === '>'){
if ($rule->operator === '=' || $rule->operator === '<' || $rule->operator === '>') {
// normal comparison operations need to relation to exist
$query->whereHas($rule->path, function (Builder $inner_query) use ($rule) {
$inner_query->where($rule->field, $rule->operator, $rule->criteria);
Expand All @@ -118,10 +153,47 @@ private function applyRule(QueryGroupBuilder $query, stdClass $rule): void {
$query->whereDoesntHave($rule->path, function (Builder $inner_query) use ($rule) {
$inner_query->where($rule->field, $rule->criteria);
});
} elseif($rule->operator === 'contains'){
} elseif ($rule->operator === 'contains') {
// contains is maybe a misleading name, since it actually checks if json contains a value
$query->whereHas($rule->path, function (Builder $inner_query) use ($rule) {
$inner_query->whereJsonContains($rule->field, $rule->criteria);
});
} else {
throw new InvalidFilterException(sprintf('Unknown rule operator: \'%s\'', $rule->operator));
}
}

/**
* Applies a skill level rule to a query group.
*
* @param QueryGroupBuilder $query the query to add the rule to
* @param stdClass $rule the rule configuration
*
* @throws InvalidFilterException
*/
private function applySkillLevelRule(QueryGroupBuilder $query, stdClass $rule, stdClass $skrule): void
{
// 'is' operator
if ($rule->operator === '=' || $rule->operator === '<' || $rule->operator === '>') {
// normal comparison operations need to relation to exist
$query->whereHas($rule->path, function (Builder $inner_query) use ($rule, $skrule) {
$inner_query->where(function ($q) use ($rule, $skrule) {
$q->where($rule->field, $rule->operator, $rule->criteria)
->where($skrule->field, $skrule->operator, $skrule->criteria);
});
});
} elseif ($rule->operator === '<>' || $rule->operator === '!=') {
// not equal is special cased since a missing relation is the same as not equal
$query->whereDoesntHave($rule->path, function (Builder $inner_query) use ($rule, $skrule) {
$inner_query->where($rule->field, $rule->criteria)
->where($skrule->field, $skrule->operator, $skrule->criteria);
//TODO TEST THIS PATH
});
} elseif ($rule->operator === 'contains') {
// contains is maybe a misleading name, since it actually checks if json contains a value
$query->whereHas($rule->path, function (Builder $inner_query) use ($rule) {
$inner_query->whereJsonContains($rule->field, $rule->criteria);
// TODO HANDLE THIS CRITERIA ((even though I dont think it is valid))
});
} else {
throw new InvalidFilterException(sprintf('Unknown rule operator: \'%s\'', $rule->operator));
Expand Down
49 changes: 49 additions & 0 deletions tests/Squads/SkillSkillLevelPairFiltersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,55 @@ public function testUserHasCharacterWithSkillAndSkillLevel()
}
}

public function testUserHasCharacterWithSkillAndOtherSkillLevel()
{
// spawn test squad
$squad = new Squad([
'name' => 'Testing Squad',
'description' => 'Some description',
'type' => 'auto',
'filters' => json_encode([
'and' => [
[
'name' => 'skill',
'path' => 'skills',
'field' => 'skill_id',
'operator' => '=',
'criteria' => 3350,
'text' => 'Random Skill',
],
[
'name' => 'skill_level',
'path' => 'skills',
'field' => 'trained_skill_level',
'operator' => '>',
'criteria' => 4,
'text' => 'Random Skill',
],
],
]),
]);

$reference_user = User::first();
$reference_user->characters->first()->skills->first()->update([
'skill_id' => 3350,
'trained_skill_level' => 1,
]);
$reference_user->characters->first()->skills->where('skill_id', '<>', 3350)
->first()->update([
'skill_id' => 3349,
'trained_skill_level' => 5,
]);

// pickup users
$users = User::all();

// ensure no users are eligible
foreach ($users as $user) {
$this->assertFalse($squad->isUserEligible($user));
}
}

public function testUserHasNoCharacterWithSkillOrSkillLevel()
{
// spawn test squad
Expand Down
Loading