Skip to content

Commit

Permalink
[5.x] Bring back select modifier (#10219)
Browse files Browse the repository at this point in the history
Co-authored-by: John Koster <[email protected]>
  • Loading branch information
jasonvarga and JohnathonKoster authored Aug 1, 2024
1 parent c907c55 commit 2d7bc9f
Show file tree
Hide file tree
Showing 4 changed files with 316 additions and 1 deletion.
35 changes: 35 additions & 0 deletions src/Modifiers/CoreModifiers.php
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,41 @@ public function pluck($value, $params)
return $wasArray ? $items->all() : $items;
}

/**
* Selects certain values from each item in a collection.
*
* @param array|Collection $value
* @param array $params
* @return array|Collection
*/
public function select($value, $params)
{
$keys = Arr::wrap($params);

if ($wasArray = is_array($value)) {
$value = collect($value);
}

if (Compare::isQueryBuilder($value)) {
$value = $value->get();
}

$items = $value->map(function ($item) use ($keys) {
return collect($keys)->mapWithKeys(function ($key) use ($item) {
$value = null;
if (is_array($item) || $item instanceof ArrayAccess) {
$value = Arr::get($item, $key);
} else {
$value = method_exists($item, 'value') ? $item->value($key) : $item->get($key);
}

return [$key => $value];
})->all();
});

return $wasArray ? $items->all() : $items;
}

/**
* Get the plural form of an English word with access to $context.
*
Expand Down
2 changes: 1 addition & 1 deletion src/View/Antlers/Language/Runtime/NodeProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,7 @@ public function reduce($processNodes)
if (! empty($recursiveParent->parameters)) {
$lockData = $this->data;
foreach ($recursiveParent->parameters as $param) {
if (ModifierManager::isModifier($param)) {
if ($param->name === 'scope') {
$childDataToUse = $this->runModifier($param->name, $parentParameterValues, $childDataToUse, $rootData);
}
}
Expand Down
270 changes: 270 additions & 0 deletions tests/Modifiers/SelectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
<?php

namespace Tests\Modifiers;

use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Mockery;
use Statamic\Contracts\Query\Builder;
use Statamic\Entries\EntryCollection;
use Statamic\Modifiers\Modify;
use Tests\TestCase;

class SelectTest extends TestCase
{
/** @test */
public function it_selects_certain_values_from_array_of_items()
{
$items = $this->items();

$modified = $this->modify($items, ['title', 'type']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
],
$modified,
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
],
$modified,
);
}

/** @test */
public function it_selects_certain_values_from_collections_of_items()
{
$items = Collection::make($this->items());

$modified = $this->modify($items, ['title', 'type']);
$this->assertInstanceOf(Collection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
],
$modified->all(),
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertInstanceOf(Collection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
],
$modified->all(),
);
}

/** @test */
public function it_selects_certain_values_from_query_builder()
{
$builder = Mockery::mock(Builder::class);
$builder->shouldReceive('get')->andReturn(Collection::make($this->items()));

$modified = $this->modify($builder, ['title', 'type']);
$this->assertInstanceOf(Collection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
],
$modified->all(),
);

$modified = $this->modify($builder, ['title', 'stock']);
$this->assertInstanceOf(Collection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
],
$modified->all(),
);
}

/** @test */
public function it_selects_certain_values_from_array_of_items_with_origins()
{
$items = $this->itemsWithOrigins();

$modified = $this->modify($items, ['title', 'type']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Pan', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
['title' => 'Cafe', 'type' => 'drink'],
],
$modified,
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Pan', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
['title' => 'Cafe', 'stock' => 2],
],
$modified,
);
}

/** @test */
public function it_selects_certain_values_from_collections_of_items_with_origins()
{
$items = EntryCollection::make($this->itemsWithOrigins());

$modified = $this->modify($items, ['title', 'type']);
$this->assertInstanceOf(EntryCollection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Pan', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
['title' => 'Cafe', 'type' => 'drink'],
],
$modified->all(),
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertInstanceOf(EntryCollection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Pan', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
['title' => 'Cafe', 'stock' => 2],
],
$modified->all(),
);
}

/** @test */
public function it_selects_certain_values_from_array_of_items_of_type_array()
{
$items = $this->itemsOfTypeArray();

$modified = $this->modify($items, ['title', 'type']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
],
$modified,
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
],
$modified,
);
}

/** @test */
public function it_selects_certain_values_from_collections_of_items_of_type_array()
{
$items = EntryCollection::make($this->itemsOfTypeArray());

$modified = $this->modify($items, ['title', 'type']);
$this->assertInstanceOf(EntryCollection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
],
$modified->all(),
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertInstanceOf(EntryCollection::class, $modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
],
$modified->all(),
);
}

/** @test */
public function it_selects_certain_values_from_array_of_items_of_type_arrayaccess()
{
$items = $this->itemsOfTypeArrayAccess();

$modified = $this->modify($items, ['title', 'type']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'type' => 'food'],
['title' => 'Coffee', 'type' => 'drink'],
],
$modified,
);

$modified = $this->modify($items, ['title', 'stock']);
$this->assertIsArray($modified);
$this->assertEquals(
[
['title' => 'Bread', 'stock' => 1],
['title' => 'Coffee', 'stock' => 2],
],
$modified,
);
}

private function items()
{
return [
new Item(['title' => 'Bread', 'type' => 'food', 'stock' => 1]),
new Item(['title' => 'Coffee', 'type' => 'drink', 'stock' => 2]),
];
}

private function itemsWithOrigins()
{
return [
$breadEn = new ItemWithOrigin(['title' => 'Bread', 'type' => 'food', 'stock' => 1]),
$breadEs = new ItemWithOrigin(['title' => 'Pan'], $breadEn),
$coffeeEn = new ItemWithOrigin(['title' => 'Coffee', 'type' => 'drink', 'stock' => 2]),
$coffeeEs = new ItemWithOrigin(['title' => 'Cafe'], $coffeeEn),
];
}

private function itemsOfTypeArray()
{
return [
['title' => 'Bread', 'type' => 'food', 'stock' => 1],
['title' => 'Coffee', 'type' => 'drink', 'stock' => 2],
];
}

private function itemsOfTypeArrayAccess()
{
return [
new ArrayAccessType(['title' => 'Bread', 'type' => 'food', 'stock' => 1]),
new ArrayAccessType(['title' => 'Coffee', 'type' => 'drink', 'stock' => 2]),
];
}

private function modify($value, ...$keys)
{
return Modify::value($value)->select(Arr::flatten($keys))->fetch();
}
}
10 changes: 10 additions & 0 deletions tests/Tags/StructureTagTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ public function it_renders_a_nav()
]));
}

#[Test]
public function it_renders_a_nav_with_selected_fields()
{
$this->createCollectionAndNav();

$template = '{{ nav:test select="title" }}{{ *recursive children* }}{{ /nav:test }}';

$this->assertNotNull(Antlers::parse($template));
}

#[Test]
public function it_renders_a_nav_with_scope()
{
Expand Down

0 comments on commit 2d7bc9f

Please sign in to comment.