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

Fix #213: use ListItemContext for ListView::renderItem related operations. #214

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
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
Loading