Skip to content

Commit

Permalink
Cleaning and refactoring the behavior of returning Block or native ar…
Browse files Browse the repository at this point in the history
…ray in loops
  • Loading branch information
roberto-butti committed Jun 21, 2024
1 parent 58be8df commit e70ddee
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 0.1.0 - 2024-06-21
- Cleaning and refactoring the behavior of returning Block or native array in loops

## 0.0.6 - 2024-06-19
- Add the `iterateBlock()` method, which allows to return of current elements as Block while looping.
- Improve the documentation and the unit tests for "query" methods like `select()`, `where()` and `orderBy()`
Expand Down
46 changes: 38 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,30 +357,60 @@ Array
```
## Looping Data
The Block class implements the Iterator interface.
While you are looping an array via Block, by default the element in the loop has the same type of the original data.
While you are looping an array via Block, by default if the current element should be an array, a Block is returned. So that you can access the Block method for handling the current array item in the loop.
For example with the previous code, if you loop through `$data` (that is a `Block` object), each element in each iteration in the loop will be an array with two elements, with the keys `product` and `price`.
If in the loop you need to manage the current element via Block class, you should manually call the `Block::make` for example:

```php
$table = Block::make($dataTable);
foreach ($table as $key => $item) {
echo $item->get("price");
}
```

You can apply filters and then loop into the result:

```php
$table = Block::make($dataTable);
$data = $table
->select('product', 'price')
->where('price', ">", 100, false);
foreach ($data as $key => $item) {
// $item is an array
//if you need a Block to manage item you should do:
$item = Block::make($item);
echo $item->get("price"); // returns an integer
}

```

If you want to loop through `$data` and obtain the current $item as Block you should use the method `iterateBlock()`:

If you want to loop through `$data` and obtain the current `$item` variable as an array you should set `false` as a second parameter in the static `make()` method:

```php
foreach ($data->iterateBlock() as $key => $item) {
// $item is a Block object
echo $item->get("price"); // returns an integer
$table = Block::make($dataTable, false);
$data = $table->select('product', 'price')->where('price', ">", 100, false);
foreach ($data as $key => $item) {
print_r($item); // $item is an array
}
```

### The `iterateBlock()` method
With the `iterateBlock()` method, you can decide to switch from array or Block for nested lists inside the main Block object in the case you already instanced as a Block object.
In the example above, you have the `$table` Block object.
You can loop across the items of the `$table` object.
If each item in the loop is itself an array (so an array of arrays), you can retrieve it as an array or a Block, depending on your needs:

```php
$table = Block::make($dataTable);
foreach ($table as $key => $item) {
expect($item)->toBeInstanceOf(Block::class);
expect($key)->toBeInt();
expect($item->get("price"))->toBeGreaterThan(10);
}

// iterateBlock(false if you need array instad of a nested Block)
foreach ($table->iterateBlock(false) as $key => $item) {
expect($item)->toBeArray();
expect($key)->toBeInt();
expect($item["price"])->toBeGreaterThan(10);
}
```

Expand Down
14 changes: 10 additions & 4 deletions src/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ final class Block implements Iterator, ArrayAccess, Countable
/** @var array<int|string, mixed> */
private array $data;

private bool $iteratorReturnsBlock = false;
private bool $iteratorReturnsBlock = true;

/** @param array<int|string, mixed> $data */
public function __construct(array $data = [])
public function __construct(array $data = [], bool $iteratorReturnsBlock = true)
{
$this->data = $data;
$this->iteratorReturnsBlock = $iteratorReturnsBlock;
}

public function iterateBlock(bool $returnsBlock = true): self
Expand All @@ -39,6 +40,7 @@ public function iterateBlock(bool $returnsBlock = true): self
return $this;
}


public function current(): mixed
{
if ($this->iteratorReturnsBlock) {
Expand Down Expand Up @@ -103,11 +105,13 @@ public function offsetUnset(mixed $offset): void
}

/** @param array<int|string, mixed> $data */
public static function make(array $data = []): self
public static function make(array $data = [], bool $iteratorReturnsBlock = true): self
{
return new self($data);
return new self($data, $iteratorReturnsBlock);
}



public function count(): int
{
return count($this->data);
Expand Down Expand Up @@ -162,6 +166,8 @@ public function get(mixed $key, mixed $defaultValue = null, string $charNestedKe
foreach (explode($charNestedKey, $keyString) as $nestedKey) {
if (is_array($nestedValue) && array_key_exists($nestedKey, $nestedValue)) {
$nestedValue = $nestedValue[$nestedKey];
} elseif ($nestedValue instanceof Block) {
$nestedValue = $nestedValue->get($nestedKey);
} else {
return $defaultValue;
}
Expand Down
12 changes: 6 additions & 6 deletions src/Traits/QueryableBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public function where(
foreach ($this as $key => $element) {
$elementToCheck = $element;
if (is_array($element)) {
$elementToCheck = Block::make($element);
$elementToCheck = Block::make($element, $this->iteratorReturnsBlock);
}
if (! $elementToCheck instanceof Block) {
return Block::make();
return Block::make([], $this->iteratorReturnsBlock);
}
$found = match ($operator) {
'==' => ($elementToCheck->get($field) == $value),
Expand All @@ -50,7 +50,7 @@ public function where(
}
}

return self::make($returnData);
return self::make($returnData, $this->iteratorReturnsBlock);
}

public function orderBy(string|int $field, string $order = 'asc'): self
Expand All @@ -65,17 +65,17 @@ public function orderBy(string|int $field, string $order = 'asc'): self
}

usort($array, $closure);
return self::make($array);
return self::make($array, $this->iteratorReturnsBlock);
}

public function select(int|string ...$columns): self
{
$table = self::make();
$table = self::make([], $this->iteratorReturnsBlock);

foreach ($this->data as $row) {
if (is_array($row)) {
/** @var Block $row */
$row = self::make($row);
$row = self::make($row, $this->iteratorReturnsBlock);
}
$newRow = [];
foreach ($columns as $column) {
Expand Down
44 changes: 37 additions & 7 deletions tests/Unit/BidimensionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,42 +67,72 @@ function () use ($dataTable): void {
$data = $table
->select('product', 'price')
->where('price', ">", 100, false);

//->calc('new_field', fn ($item) => $item['price'] * 2)
foreach ($data->iterateBlock(true) as $key => $item) {
foreach ($data as $key => $item) {
expect($item)->toBeInstanceOf(Block::class);
expect($key)->toBeInt();
expect($item->get("price"))->toBeGreaterThan(100);
}

expect($data->get("0.price"))->toBe(200);
expect($data->get("1.price"))->toBe(300);

$table->iterateBlock(true);

expect($table->get("0.price"))->toBe(200);
expect($table->get("1.price"))->toBe(100);

$table = Block::make($dataTable, false);
$data = $table
->select('product', 'price')
->where('price', ">", 100, false);

//->calc('new_field', fn ($item) => $item['price'] * 2)
foreach ($data->iterateBlock(false) as $key => $item) {
foreach ($data as $key => $item) {
expect($item)->toBeArray();
expect($key)->toBeInt();
expect($item["price"])->toBeGreaterThan(100);
expect($data[$key]["price"])->toBeGreaterThan(100);
}


// re-using the $data object with the previous state
foreach ($data as $key => $item) {
expect($item)->toBeArray();
expect($key)->toBeInt();
expect($item["price"])->toBeGreaterThan(100);
expect($data[$key]["price"])->toBeGreaterThan(100);
}
// resetting the default behaviour (so returns Block in case of iterators/array)
$data->iterateBlock();
foreach ($data as $key => $item) {

$table = Block::make($dataTable, true);
foreach ($table as $key => $item) {
expect($item)->toBeInstanceOf(Block::class);
expect($key)->toBeInt();
expect($item->get("price"))->toBeGreaterThan(100);
expect($item->get("price"))->toBeGreaterThan(10);
}

foreach ($table->iterateBlock(false) as $key => $item) {
expect($item)->toBeArray();
expect($key)->toBeInt();
expect($item["price"])->toBeGreaterThan(10);
}
// keep the previous state iterateBlock(false)
foreach ($table as $key => $item) {
expect($item)->toBeArray();
expect($key)->toBeInt();
expect($item["price"])->toBeGreaterThan(10);
}


foreach ($table->iterateBlock(true) as $key => $item) {
expect($item)->toBeInstanceOf(Block::class);
expect($key)->toBeInt();
expect($item->get("price"))->toBeGreaterThan(10);
}
foreach ($table->iterateBlock(false) as $key => $item) {
expect($item)->toBeArray();
expect($key)->toBeInt();
expect($item["price"])->toBeGreaterThan(10);
}

},
Expand Down

0 comments on commit e70ddee

Please sign in to comment.