Skip to content

Commit

Permalink
Added matrix field helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Hannes Giesenow authored and hgiesenow committed Nov 1, 2023
1 parent c3c0cba commit 34427b9
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 3 deletions.
4 changes: 4 additions & 0 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ services:
# arguments:
# $tagsService: '@eztags.api.service.tags'

elbformat_field_helper.field_helper.matrix:
class: Elbformat\FieldHelperBundle\FieldHelper\MatrixFieldHelper
tags: ['elbformat_field_helper.field_helper']

elbformat_field_helper.field_helper.number:
class: Elbformat\FieldHelperBundle\FieldHelper\NumberFieldHelper
tags: ['elbformat_field_helper.field_helper']
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.2.2
Added field helper for
* Matrix

## v1.2.1
Added forgotten field helper for
* Selection
Expand Down
2 changes: 1 addition & 1 deletion docs/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Implemented types are:
| ISBN | | |
| Keyword | | |
| MapLocation | | |
| Matrix | ezmatrix | |
| Matrix | ezmatrix | MatrixFieldHelper |
| Media | | |
| Null | | |
| Page | | |
Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ vendor/bin/psalm
To run integration tests, you need to spin up a database first. To ease this, you can use the docker-compose setup provided
```bash
docker-compose up -d
docker-compose run php bash
docker-compose exec php sh
vendor/bin/phpunit --testsuite integration
```

Expand Down
242 changes: 242 additions & 0 deletions src/FieldHelper/MatrixFieldHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
<?php

declare(strict_types=1);

namespace Elbformat\FieldHelperBundle\FieldHelper;

use Elbformat\FieldHelperBundle\Exception\FieldNotFoundException;
use Elbformat\FieldHelperBundle\Exception\InvalidFieldTypeException;
use eZ\Publish\API\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\ContentStruct;
use EzSystems\EzPlatformMatrixFieldtype\FieldType\Value;

/**
* @author Hannes Giesenow <[email protected]>
*/
class MatrixFieldHelper extends AbstractFieldHelper
{
/**
* Return a row-based 2-dimensional array.
* [
* [A1,B1,C1],
* [A2,B2,C2],
* ]
*
* @return string[][]
*
* @throws FieldNotFoundException
* @throws InvalidFieldTypeException
*/
public function getArray(Content $content, string $fieldName): array
{
$rows = $this->getValue($content, $fieldName)->getRows();
$result = [];
/** @var Value\Row $row */
foreach ($rows as $row) {
/** @var string[] $cells */
$cells = $row->getCells();
$result[] = array_values($cells);
}

return $result;
}

/**
* Return a key-value based array structure of the table. Each row has the headlines as key.
* [
* [A1 => A2, B1 => B2, C1 => C2],
* [A1 => A3, B1 => B3, C1 => C3],
* ]
*
* @return mixed[][]
*
* @throws FieldNotFoundException
* @throws InvalidFieldTypeException
*/
public function getAssoc(Content $content, string $fieldName): array
{
$rows = $this->getArray($content, $fieldName);
$headlines = $this->getHeadlineIds($content, $fieldName);
$numHeadlines = count($headlines);

$result = [];
/** @var Value\Row $row */
foreach ($rows as $cells) {
$rowData = [];
for ($i = 0; $i < $numHeadlines; $i++) {
$rowData[$headlines[$i]] = $cells[$i];
}
$result[] = $rowData;
}

return $result;
}

/**
* Return a key-value list for 2-column tables.
* [A1 => B1, A2 => B2, A3 => B3]
*
* @return mixed[]
*/
public function getKeyValue(Content $content, string $fieldName, ?string $key = null, ?string $val = null): array
{
$rows = $this->getValue($content, $fieldName)->getRows();
if (null === $key) {
$key = $this->getHeadlineIds($content, $fieldName)[0] ?? '';
}
if (null === $val) {
$val = $this->getHeadlineIds($content, $fieldName)[1] ?? '';
}

$result = [];
/** @var Value\Row $row */
foreach ($rows as $row) {
/** @var string[] $cells */
$cells = $row->getCells();
$result[$cells[$key]] = $cells[$val] ?? '';
}

return $result;
}

/**
* Return a list for 1-column tables.
* [A1,A2,A3]
*
* @return string[]
*/
public function getList(Content $content, string $fieldName, string $columnName = null): array
{
if (null === $columnName) {
$columnName = $this->getHeadlineIds($content, $fieldName)[0] ?? '';
}
$rows = $this->getValue($content, $fieldName)->getRows();
$result = [];
/** @var Value\Row $row */
foreach ($rows as $row) {
$cells = $row->getCells();
$result[] = (string) $cells[$columnName];
}

return $result;
}

public function isEmpty(Content $content, string $fieldName): bool
{
return $this->getValue($content, $fieldName)->getRows()->count() <= 0;
}

/**
* @param array<array<string,string>> $rowsWithCols
*/
public function updateAssoc(ContentStruct $struct, string $fieldName, array $rowsWithCols, ?Content $content = null): bool
{
// No changes
if (null !== $content) {
$current = $this->getArray($content, $fieldName);
if ($this->arrayEquals($current, $rowsWithCols)) {
return false;
}
}

// Convert to rows
$rows = [];
foreach ($rowsWithCols as $cols) {
$rows[] = new Value\Row($cols);
}

$struct->setField($fieldName, $rows);

return true;
}

/**
* @param string[] $values
*/
public function updateList(ContentStruct $struct, string $fieldName, array $values, string $columnName, ?Content $content = null): bool
{
// No changes
if (null !== $content) {
$field = $this->getList($content, $fieldName, $columnName);
if ($this->isListEqual($field, $values)) {
return false;
}
}
// Convert to rows
$rows = [];
foreach ($values as $value) {
$rows[] = new Value\Row([$columnName => $value]);
}

$struct->setField($fieldName, $rows);

return true;
}

protected function getValue(Content $content, string $fieldName): Value
{
$field = $this->getField($content, $fieldName);
if (!$field->value instanceof Value) {
throw InvalidFieldTypeException::fromActualAndExpected($field->value, [Value::class]);
}

return $field->value;
}

/**
* @param string[] $list1
* @param string[] $list2
*/
protected function isListEqual(array $list1, array $list2): bool
{
if (\count($list1) !== \count($list2)) {
return false;
}
for ($i = 0, $iMax = \count($list1); $i < $iMax; ++$i) {
if ($list1[$i] !== $list2[$i]) {
return false;
}
}

return true;
}

/** @return string[] */
protected function getHeadlineIds(Content $content, string $fieldName): array
{
$fieldDef = $content->getContentType()->getFieldDefinition($fieldName);
if (null === $fieldDef) {
return [];
}

$colIds = [];
/** @var array[] $columns */
$columns = $fieldDef->fieldSettings['columns'];
foreach ($columns as $colDef) {
$colIds[] = (string) $colDef['identifier'];
}

return $colIds;
}

protected function arrayEquals(array $arr1, array $arr2): bool
{
return $this->arrayContains($arr1, $arr2) && $this->arrayContains($arr2, $arr1);
}

protected function arrayContains(array $arr1, array $arr2): bool
{
return 1 > count(
array_udiff($arr1, $arr2, function ($v1, $v2): int {
if (is_array($v1) && is_array($v2)) {
return !$this->arrayContains($v1, $v2) ? 1 : 0;
}
if (is_array($v1) || is_array($v2)) {
return 1;
}

return $v1 !== $v2 ? 1 : 0;
})
);
}
}
6 changes: 6 additions & 0 deletions src/Registry/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Elbformat\FieldHelperBundle\FieldHelper\FieldHelperInterface;
use Elbformat\FieldHelperBundle\FieldHelper\FileFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\ImageFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\MatrixFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\NetgenTagsFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\NumberFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\RelationFieldHelper;
Expand Down Expand Up @@ -77,6 +78,11 @@ public function getImageFieldHelper(): ImageFieldHelper
return $this->getFieldHelper(ImageFieldHelper::class);
}

public function getMatrixFieldHelper(): MatrixFieldHelper
{
return $this->getFieldHelper(MatrixFieldHelper::class);
}

public function getNetgenTagsFieldHelper(): NetgenTagsFieldHelper
{
return $this->getFieldHelper(NetgenTagsFieldHelper::class);
Expand Down
2 changes: 2 additions & 0 deletions src/Registry/RegistryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Elbformat\FieldHelperBundle\FieldHelper\FieldHelperInterface;
use Elbformat\FieldHelperBundle\FieldHelper\FileFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\ImageFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\MatrixFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\NetgenTagsFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\NumberFieldHelper;
use Elbformat\FieldHelperBundle\FieldHelper\RelationFieldHelper;
Expand All @@ -30,6 +31,7 @@ public function getBoolFieldHelper(): BoolFieldHelper;
public function getDateTimeFieldHelper(): DateTimeFieldHelper;
public function getFileFieldHelper(): FileFieldHelper;
public function getImageFieldHelper(): ImageFieldHelper;
public function getMatrixFieldHelper(): MatrixFieldHelper;
public function getNetgenTagsFieldHelper(): NetgenTagsFieldHelper;
public function getNumberFieldHelper(): NumberFieldHelper;
public function getRelationFieldHelper(): RelationFieldHelper;
Expand Down
2 changes: 1 addition & 1 deletion tests/DependencyInjection/Compile/FieldHelperPassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function testProcess(): void
if (BoolFieldHelper::class !== array_keys($arg)[0]) {
throw new \Exception('Invalid helper name: '.array_keys($arg)[0]);
}
if (! array_values($arg)[0] instanceof Reference) {
if (!array_values($arg)[0] instanceof Reference) {
throw new \Exception('Helper not a reference');
}
return ('elbformat_field_helper.field_helper.test' === (string) array_values($arg)[0]);
Expand Down
47 changes: 47 additions & 0 deletions tests/FieldHelper/MatrixFieldHelperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Elbformat\FieldHelperBundle\Tests\FieldHelper;

use Elbformat\FieldHelperBundle\FieldHelper\MatrixFieldHelper;
use eZ\Publish\API\Repository\Values\Content\Field;
use eZ\Publish\Core\Repository\Values\Content\Content;
use EzSystems\EzPlatformMatrixFieldtype\FieldType\Value as MatrixValue;
use PHPUnit\Framework\TestCase;

/**
* @author Hannes Giesenow <[email protected]>
*/
class MatrixFieldHelperTest extends TestCase
{
/**
* @dataProvider isEmptyProvider
*/
public function testIsEmpty(MatrixValue $value, bool $expected): void
{
$fh = new MatrixFieldHelper();
$content = $this->createContentFromValue($value);
$this->assertSame($expected, $fh->isEmpty($content, 'matrix_field'));
}


public function isEmptyProvider(): array
{
return [
[new MatrixValue(), true],
[new MatrixValue([]), true],
[new MatrixValue([new MatrixValue\Row(['x'])]), false],
];
}

protected function createContentFromValue(MatrixValue $value): Content
{
$field = new Field(['value' => $value]);

$content = $this->createMock(Content::class);
$content->method('getField')->with('matrix_field')->willReturn($field);

return $content;
}
}

0 comments on commit 34427b9

Please sign in to comment.