Skip to content

Commit

Permalink
Fix #213: use ListItemContext for ListView::renderItem related oper…
Browse files Browse the repository at this point in the history
…ations. (#214)
  • Loading branch information
glpzzz authored Sep 11, 2024
1 parent e9eec42 commit c26babd
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 39 deletions.
22 changes: 22 additions & 0 deletions src/ListItemContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Yii\DataView;

final class ListItemContext
{
/**
* @param array|object $data The current data being rendered.
* @param int|string $key The key value associated with the current data.
* @param int $index The zero-based index of the data in the array.
* @param ListView $widget The list view object.
*/
public function __construct(
public readonly array|object $data,
public readonly int|string $key,
public readonly int $index,
public readonly ListView $widget,
) {
}
}
53 changes: 22 additions & 31 deletions src/ListView.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,9 @@ public function afterItem(Closure $value): self
* It should have the following signature:
*
* ```php
* function ($data, $key, $index, $widget)
* function (ListItemContext $context)
* ```
*
* - `$data`: The current data being rendered.
* - `$key`: The key value associated with the current data.
* - `$index`: The zero-based index of the data in the array.
* - `$widget`: The list view object.
*
* The return result of the function will be rendered directly.
*
* Note: If the function returns `null`, nothing will be rendered before the item.
Expand Down Expand Up @@ -173,13 +168,13 @@ public function itemViewTag(?string $tag): self
* signature:
*
* ```php
* function ($data, $key, $index, $widget)
* function (ListItemContext $context)
* ```
* Also, each attribute value can be a function too, with the same signature as in:
*
* ```php
* [
* 'class' => static fn($data, $key, $index, $widget) => "custom-class-{$data['id']}",
* 'class' => static fn(ListItemContext $context) => "custom-class-{$context->data['id']}",
* ]
* ```
* @return ListView
Expand Down Expand Up @@ -223,13 +218,11 @@ public function viewParams(array $viewParams): self
/**
* Renders a single data model.
*
* @param array|object $data The data to be rendered.
* @param mixed $key The key value associated with the data.
* @param int $index The zero-based index of the data array.
* @param ListItemContext $context
*
* @throws ViewNotFoundException If the item view file doesn't exist.
*/
protected function renderItem(array|object $data, mixed $key, int $index): string
protected function renderItem(ListItemContext $context): string
{
$content = '';

Expand All @@ -242,9 +235,9 @@ protected function renderItem(array|object $data, mixed $key, int $index): strin
$this->itemView,
array_merge(
[
'data' => $data,
'index' => $index,
'key' => $key,
'data' => $context->data,
'index' => $context->index,
'key' => $context->key,
'widget' => $this,
],
$this->viewParams
Expand All @@ -253,16 +246,16 @@ protected function renderItem(array|object $data, mixed $key, int $index): strin
}

if ($this->itemView instanceof Closure) {
$content = (string)call_user_func($this->itemView, $data, $key, $index, $this);
$content = (string)($this->itemView)($context);
}

$itemViewAttributes = is_callable($this->itemViewAttributes)
? (array)call_user_func($this->itemViewAttributes, $data, $key, $index, $this)
? (array)($this->itemViewAttributes)($context)
: $this->itemViewAttributes;

foreach ($itemViewAttributes as $i => $attribute) {
if (is_callable($attribute)) {
$itemViewAttributes[$i] = $attribute($data, $key, $index, $this);
$itemViewAttributes[$i] = $attribute($context);
}
}

Expand All @@ -289,13 +282,15 @@ protected function renderItems(array $items, \Yiisoft\Validator\Result $filterVa
foreach (array_values($items) as $index => $value) {
$key = $keys[$index];

if ('' !== ($before = $this->renderBeforeItem($value, $key, $index))) {
$itemContext = new ListItemContext($value, $key, $index, $this);

if ('' !== ($before = $this->renderBeforeItem($itemContext))) {
$rows[] = $before;
}

$rows[] = $this->renderItem($value, $key, $index);
$rows[] = $this->renderItem($itemContext);

if ('' !== ($after = $this->renderAfterItem($value, $key, $index))) {
if ('' !== ($after = $this->renderAfterItem($itemContext))) {
$rows[] = $after;
}
}
Expand All @@ -314,20 +309,18 @@ protected function renderItems(array $items, \Yiisoft\Validator\Result $filterVa
*
* If {@see afterItem} is not a closure, `null` will be returned.
*
* @param array|object $data The data to be rendered.
* @param mixed $key The key value associated with the data.
* @param int $index The zero-based index of the data.
* @param ListItemContext $context context of the item to be rendered.
*
* @return string call result when {@see afterItem} is not a closure.
*
* {@see afterItem}
*/
private function renderAfterItem(array|object $data, mixed $key, int $index): string
private function renderAfterItem(ListItemContext $context): string
{
$result = '';

if (!empty($this->afterItem)) {
$result = (string)call_user_func($this->afterItem, $data, $key, $index, $this);
$result = (string)($this->afterItem)($context);
}

return $result;
Expand All @@ -338,20 +331,18 @@ private function renderAfterItem(array|object $data, mixed $key, int $index): st
*
* If {@see beforeItem} is not a closure, `null` will be returned.
*
* @param array|object $data The data to be rendered.
* @param mixed $key The key value associated with the data.
* @param int $index The zero-based index of the data.
* @param ListItemContext $context context of the item to be rendered.
*
* @return string call result or `null` when {@see beforeItem} is not a closure.
*
* {@see beforeItem}
*/
private function renderBeforeItem(array|object $data, mixed $key, int $index): string
private function renderBeforeItem(ListItemContext $context): string
{
$result = '';

if (!empty($this->beforeItem)) {
$result = (string)call_user_func($this->beforeItem, $data, $key, $index, $this);
$result = (string)($this->beforeItem)($context);
}

return $result;
Expand Down
46 changes: 38 additions & 8 deletions tests/ListView/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Yiisoft\Definitions\Exception\InvalidConfigException;
use Yiisoft\Definitions\Exception\NotInstantiableException;
use Yiisoft\Factory\NotFoundException;
use Yiisoft\Yii\DataView\ListItemContext;
use Yiisoft\Yii\DataView\ListView;
use Yiisoft\Yii\DataView\Tests\Support\Assert;
use Yiisoft\Yii\DataView\Tests\Support\TestTrait;
Expand Down Expand Up @@ -51,6 +52,35 @@ public function testAfterItemBeforeItem(): void
);
}

public function testAfterItemBeforeItemWithClosure(): void
{
Assert::equalsWithoutLE(
<<<HTML
<div>
<ul>
<span class="testMe" data-item-id="1">
<li>
<div>Id: 1</div><div>Name: John</div><div>Age: 20</div>
</li>
<span data-item-id="1">just for test</span></span>
<span class="testMe" data-item-id="2">
<li>
<div>Id: 2</div><div>Name: Mary</div><div>Age: 21</div>
</li>
<span data-item-id="2">just for test</span></span>
</ul>
<div>Page <b>1</b> of <b>1</b></div>
</div>
HTML,
ListView::widget()
->afterItem(static fn (ListItemContext $context) => '<span data-item-id="' . $context->data['id'] . '">just for test</span></span>')
->beforeItem(static fn (ListItemContext $context) => '<span class="testMe" data-item-id="' . $context->data['id'] . '">')
->itemView(dirname(__DIR__) . '/Support/view/_listview.php')
->dataReader($this->createOffsetPaginator($this->data, 10))
->render(),
);
}

public function testItemViewAttributes(): void
{
Assert::equalsWithoutLE(
Expand Down Expand Up @@ -122,7 +152,7 @@ public function testItemViewAsCallable(): void
HTML,
ListView::widget()
->itemView(
fn (array $data) => '<div>' . $data['id'] . '</div><div>' . $data['name'] . '</div>' . PHP_EOL
fn (ListItemContext $context) => '<div>' . $context->data['id'] . '</div><div>' . $context->data['name'] . '</div>' . PHP_EOL
)
->dataReader($this->createOffsetPaginator($this->data, 10))
->render(),
Expand Down Expand Up @@ -426,10 +456,10 @@ public function testClosureForItemViewAttributes(): void
HTML,
ListView::widget()
->itemView(dirname(__DIR__) . '/Support/view/_listview.php')
->itemViewAttributes(static fn (array $data, $key, $index, $widget) => [
'data-item-id' => $data['id'],
'data-item-key' => $key,
'data-item-index' => $index,
->itemViewAttributes(static fn (ListItemContext $context) => [
'data-item-id' => $context->data['id'],
'data-item-key' => $context->key,
'data-item-index' => $context->index,
])
->dataReader($this->createOffsetPaginator($this->data, 10))
->separator(PHP_EOL)
Expand All @@ -456,7 +486,7 @@ public function testItemViewAttributesWithClosure(): void
ListView::widget()
->itemView(dirname(__DIR__) . '/Support/view/_listview.php')
->itemViewAttributes([
'class' => static fn(array $data, $key, $index) => "id-{$data['id']}-key-{$key}-index-{$index}",
'class' => static fn(ListItemContext $context) => "id-{$context->data['id']}-key-{$context->key}-index-{$context->index}",
])
->dataReader($this->createOffsetPaginator($this->data, 10))
->separator(PHP_EOL)
Expand All @@ -482,8 +512,8 @@ public function testClosureForItemViewAttributesWithClosure(): void
HTML,
ListView::widget()
->itemView(dirname(__DIR__) . '/Support/view/_listview.php')
->itemViewAttributes(static fn (array $data, $key, $index) => [
'class' => static fn(array $data, $key, $index) => "id-{$data['id']}-key-{$key}-index-{$index}",
->itemViewAttributes(static fn (ListItemContext $context) => [
'class' => static fn(ListItemContext $context) => "id-{$context->data['id']}-key-{$context->key}-index-{$context->index}",
])
->dataReader($this->createOffsetPaginator($this->data, 10))
->separator(PHP_EOL)
Expand Down

0 comments on commit c26babd

Please sign in to comment.