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

[5.x] Add includes and doesnt_include conditions and modifiers #9491

Open
wants to merge 4 commits into
base: 5.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/Modifiers/CoreModifiers.php
Expand Up @@ -910,6 +910,50 @@ public function headline($value, $params)
}
}

/**
* Returns true if the array contains $needle, false otherwise
*
* @param string|array $haystack
* @param array $params
* @param array $context
* @return bool
*/
public function includes($haystack, $params, $context)
{
$needle = $this->getFromContext($context, $params);

if ($needle instanceof Collection) {
$needle = $needle->values()->all();
}

if (! is_array($needle)) {
$needle = [$needle];
}

if ($haystack instanceof Collection) {
$haystack = $haystack->values()->all();
}

if (! is_array($haystack)) {
return false;
}

return count(array_intersect($haystack, $needle)) > 0;
}

/**
* Returns false if the array contains $needle, true otherwise
*
* @param string|array $haystack
* @param array $params
* @param array $context
* @return bool
*/
public function doesnt_include($haystack, $params, $context)
{
return ! $this->includes($haystack, $params, $context);
}

private function renderAPStyleHeadline($value)
{
$exceptions = [
Expand Down
22 changes: 22 additions & 0 deletions src/Tags/Concerns/QueriesConditions.php
Expand Up @@ -122,6 +122,10 @@ protected function queryCondition($query, $field, $condition, $value)
return $this->queryIsBeforeCondition($query, $field, $value);
case 'is_numberwang':
return $this->queryIsNumberwangCondition($query, $field, $regexOperator);
case 'includes':
return $this->queryIncludesCondition($query, $field, $value);
case 'doesnt_include':
return $this->queryDoesntIncludeCondition($query, $field, $value);
}
}

Expand Down Expand Up @@ -300,6 +304,24 @@ protected function queryIsNumberwangCondition($query, $field, $regexOperator)
return $query->where($field, $regexOperator, "^(1|22|7|9|1002|2\.3|15|109876567|31)$");
}

protected function queryIncludesCondition($query, $field, $value)
{
if (is_string($value)) {
$value = $this->getPipedValues($value);
}

return $query->whereJsonContains($field, $value);
}

protected function queryDoesntIncludeCondition($query, $field, $value)
{
if (is_string($value)) {
$value = $this->getPipedValues($value);
}

return $query->whereJsonDoesntContain($field, $value);
}

/**
* This is for backwards compatibility, because v2's regex conditions required delimiters.
* Passing delimiters doesn't work with Eloquent and `regexp`, so we remove them from
Expand Down
62 changes: 62 additions & 0 deletions tests/Modifiers/DoesntIncludeTest.php
@@ -0,0 +1,62 @@
<?php

namespace Tests\Modifiers;

use Statamic\Modifiers\Modify;
use Tests\TestCase;

/**
* @group array
*/
class DoesntIncludeTest extends TestCase
{
/** @test */
public function it_returns_false_if_needle_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['b'], []);
$this->assertFalse($modified);
}

/** @test */
public function it_returns_true_if_needle_is_not_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['d'], []);
$this->assertTrue($modified);
}

/** @test */
public function it_returns_true_if_haystack_is_not_an_array(): void
{
$haystack = 'this is a string';

$modified = $this->modify($haystack, ['d'], []);
$this->assertTrue($modified);
}

/** @test */
public function it_returns_false_if_needle_is_an_array_and_is_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['array'], ['array' => ['a', 'b']]);
$this->assertFalse($modified);
}

/** @test */
public function it_returns_false_if_needle_is_an_array_and_some_are_not_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['array'], ['array' => ['d', 'b']]);
$this->assertFalse($modified);
}

private function modify($value, array $params, array $context)
{
return Modify::value($value)->context($context)->doesntInclude($params)->fetch();
}
}
62 changes: 62 additions & 0 deletions tests/Modifiers/IncludesTest.php
@@ -0,0 +1,62 @@
<?php

namespace Tests\Modifiers;

use Statamic\Modifiers\Modify;
use Tests\TestCase;

/**
* @group array
*/
class IncludesTest extends TestCase
{
/** @test */
public function it_returns_true_if_needle_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['b'], []);
$this->assertTrue($modified);
}

/** @test */
public function it_returns_false_if_needle_is_not_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['d'], []);
$this->assertFalse($modified);
}

/** @test */
public function it_returns_false_if_haystack_is_not_an_array(): void
{
$haystack = 'this is a string';

$modified = $this->modify($haystack, ['d'], []);
$this->assertFalse($modified);
}

/** @test */
public function it_returns_true_if_needle_is_an_array_and_is_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['array'], ['array' => ['a', 'b']]);
$this->assertTrue($modified);
}

/** @test */
public function it_returns_true_if_needle_is_an_array_and_some_are_not_found_in_array(): void
{
$haystack = ['a', 'b', 'c'];

$modified = $this->modify($haystack, ['array'], ['array' => ['d', 'b']]);
$this->assertTrue($modified);
}

private function modify($value, array $params, array $context)
{
return Modify::value($value)->context($context)->includes($params)->fetch();
}
}
24 changes: 24 additions & 0 deletions tests/Tags/Concerns/QueriesConditionsTest.php
Expand Up @@ -584,6 +584,30 @@ public function it_filters_by_is_numberwang_condition()
$this->assertCount(1, $this->getEntries(['age:is_numberwang' => false]));
}

/** @test */
public function it_filters_by_includes_condition()
{
$this->makeEntry('a')->set('ages', [22, 52, 72])->save();
$this->makeEntry('b')->set('ages', [57, 72])->save();
$this->makeEntry('c')->set('ages', [2, 31, 22])->save();

$this->assertCount(3, $this->getEntries());
$this->assertCount(2, $this->getEntries(['ages:includes' => 72]));
$this->assertCount(1, $this->getEntries(['ages:includes' => 31]));
}

/** @test */
public function it_filters_by_doesnt_include_condition()
{
$this->makeEntry('a')->set('ages', [22, 52, 72])->save();
$this->makeEntry('b')->set('ages', [57, 72])->save();
$this->makeEntry('c')->set('ages', [2, 31, 22])->save();

$this->assertCount(3, $this->getEntries());
$this->assertCount(1, $this->getEntries(['ages:doesnt_include' => 72]));
$this->assertCount(2, $this->getEntries(['ages:doesnt_include' => 31]));
}

/** @test */
public function when_the_value_is_an_augmentable_object_it_will_use_the_corresponding_value()
{
Expand Down