Skip to content

Commit

Permalink
Merge pull request #66 from rvanlaak/item-interface-encapsulate-list
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanlaak committed Jun 22, 2021
2 parents 513b923 + 5e0058b commit 79c3ceb
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 113 deletions.
72 changes: 72 additions & 0 deletions src/Micrometa/Ports/Item/Collection.php
@@ -0,0 +1,72 @@
<?php

namespace Jkphl\Micrometa\Ports\Item;

use Jkphl\Micrometa\Ports\Exceptions\InvalidArgumentException;
use Jkphl\Micrometa\Ports\Exceptions\OutOfBoundsException;

/**
* Abstract getter functionality that is applicable for implementations of ItemCollectionFacade.
*
* The implementation remains responsible for implementing the collection methods itself.
*
* Thereby is usable by both the Item for it's properties, as for an ItemList with it's items.
*
* @package Jkphl\Micrometa
* @subpackage Jkphl\Micrometa\Ports
*/
abstract class Collection implements CollectionFacade
{
/**
* Generic item getter
*
* @param string $type Item type
* @param array $arguments Arguments
*
* @return ItemInterface Item
* @throws InvalidArgumentException If the item index is invalid
* @api
*/
public function __call($type, $arguments)
{
$index = 0;
if (count($arguments)) {
// If the item index is invalid
if (!is_int($arguments[0]) || ($arguments[0] < 0)) {
throw new InvalidArgumentException(
sprintf(InvalidArgumentException::INVALID_ITEM_INDEX_STR, $arguments[0]),
InvalidArgumentException::INVALID_ITEM_INDEX
);
}

$index = $arguments[0];
}

// Return the item by type and index
return $this->getItemByTypeAndIndex($type, $index);
}

/**
* Return an item by type and index
*
* @param string $type Item type
* @param int $index Item index
*
* @return ItemInterface Item
* @throws OutOfBoundsException If the item index is out of bounds
*/
private function getItemByTypeAndIndex($type, $index)
{
$typeItems = $this->getItems($type);

// If the item index is out of bounds
if (count($typeItems) <= $index) {
throw new OutOfBoundsException(
sprintf(OutOfBoundsException::INVALID_ITEM_INDEX_STR, $index),
OutOfBoundsException::INVALID_ITEM_INDEX
);
}

return $typeItems[$index];
}
}
46 changes: 46 additions & 0 deletions src/Micrometa/Ports/Item/CollectionFacade.php
@@ -0,0 +1,46 @@
<?php

namespace Jkphl\Micrometa\Ports\Item;

/**
* Interface to hides all other methods that ItemListInterface inherits from PHP core.
*
* Thereby is usable by both the Item for it's properties, as for an ItemList with it's items.
*
* Prefer type-hinting against this facade over the ItemList interface.
*
* @see ItemListInterface
*
* @package Jkphl\Micrometa
* @subpackage Jkphl\Micrometa\Ports
*/
interface CollectionFacade extends \Countable
{
/**
* Return an object representation of the item list
*
* @return \stdClass Micro information items
* @api
*/
public function toObject();

/**
* Filter the items by item type(s)
*
* @param string[] $types Item types
*
* @return ItemInterface[] Items matching the requested types
* @api
*/
public function getItems(...$types);

/**
* Return the first item, optionally of particular types
*
* @param string[] $types Item types
*
* @return ItemInterface Item
* @api
*/
public function getFirstItem(...$types);
}
43 changes: 41 additions & 2 deletions src/Micrometa/Ports/Item/Item.php
Expand Up @@ -55,7 +55,7 @@
* @package Jkphl\Micrometa
* @subpackage Jkphl\Micrometa\Ports
*/
class Item extends ItemList implements ItemInterface
class Item extends Collection implements ItemInterface
{
/**
* Application item
Expand All @@ -64,6 +64,11 @@ class Item extends ItemList implements ItemInterface
*/
protected $item;

/**
* @var ItemList
*/
private $itemList;

/**
* Item constructor
*
Expand All @@ -72,7 +77,7 @@ class Item extends ItemList implements ItemInterface
public function __construct(ApplicationItemInterface $item)
{
$this->item = $item;
parent::__construct(ItemFactory::createFromApplicationItems($this->item->getChildren()));
$this->itemList = new ItemList(ItemFactory::createFromApplicationItems($this->item->getChildren()));
}

/**
Expand Down Expand Up @@ -328,4 +333,38 @@ public function getValue()
{
return $this->item->getValue();
}

/**
* Filter the items by item type(s).
*
* @param array ...$types Item types
*
* @return ItemInterface[] Items matching the requested types
*/
public function getItems(...$types)
{
return $this->itemList->getItems(...$types);
}

/**
* Return the first item, optionally of particular types.
*
* @param array ...$types Item types
*
* @return ItemInterface Item
*/
public function getFirstItem(...$types)
{
return $this->itemList->getFirstItem(...$types);
}

/**
* Count the elements in the item list.
*
* @return int
*/
public function count()
{
return $this->itemList->count();
}
}
2 changes: 1 addition & 1 deletion src/Micrometa/Ports/Item/ItemInterface.php
Expand Up @@ -45,7 +45,7 @@
* @package Jkphl\Micrometa
* @subpackage Jkphl\Micrometa\Ports
*/
interface ItemInterface extends ItemListInterface
interface ItemInterface
{
/**
* Return whether the item is of a particular type (or contained in a list of types)
Expand Down
58 changes: 2 additions & 56 deletions src/Micrometa/Ports/Item/ItemList.php
Expand Up @@ -36,17 +36,16 @@

namespace Jkphl\Micrometa\Ports\Item;

use Jkphl\Micrometa\Ports\Exceptions\InvalidArgumentException;
use Jkphl\Micrometa\Ports\Exceptions\OutOfBoundsException;
use Jkphl\Micrometa\Ports\Exceptions\RuntimeException;

/**
* Abstract item list
* Item list
*
* @package Jkphl\Micrometa
* @subpackage Jkphl\Micrometa\Ports
*/
class ItemList implements ItemListInterface
class ItemList extends Collection implements ItemListInterface
{
/**
* Items
Expand Down Expand Up @@ -255,57 +254,4 @@ public function count()
{
return count($this->items);
}

/**
* Generic item getter
*
* @param string $type Item type
* @param array $arguments Arguments
*
* @return ItemInterface Item
* @throws InvalidArgumentException If the item index is invalid
* @api
*/
public function __call($type, $arguments)
{
$index = 0;
if (count($arguments)) {
// If the item index is invalid
if (!is_int($arguments[0]) || ($arguments[0] < 0)) {
throw new InvalidArgumentException(
sprintf(InvalidArgumentException::INVALID_ITEM_INDEX_STR, $arguments[0]),
InvalidArgumentException::INVALID_ITEM_INDEX
);
}

$index = $arguments[0];
}

// Return the item by type and index
return $this->getItemByTypeAndIndex($type, $index);
}

/**
* Return an item by type and index
*
* @param string $type Item type
* @param int $index Item index
*
* @return ItemInterface Item
* @throws OutOfBoundsException If the item index is out of bounds
*/
protected function getItemByTypeAndIndex($type, $index)
{
$typeItems = $this->getItems($type);

// If the item index is out of bounds
if (count($typeItems) <= $index) {
throw new OutOfBoundsException(
sprintf(OutOfBoundsException::INVALID_ITEM_INDEX_STR, $index),
OutOfBoundsException::INVALID_ITEM_INDEX
);
}

return $typeItems[$index];
}
}
31 changes: 3 additions & 28 deletions src/Micrometa/Ports/Item/ItemListInterface.php
Expand Up @@ -39,36 +39,11 @@
/**
* Item list interface
*
* Prefer type-hinting against CollectionFacade over this interface to narrow-down the number of public methods.
*
* @package Jkphl\Micrometa
* @subpackage Jkphl\Micrometa\Ports
*/
interface ItemListInterface extends \Iterator, \Countable, \ArrayAccess
interface ItemListInterface extends CollectionFacade, \Iterator, \ArrayAccess
{
/**
* Return an object representation of the item list
*
* @return \stdClass Micro information items
* @api
*/
public function toObject();

/**
* Filter the items by item type(s)
*
* @param array ...$types Item types
*
* @return ItemInterface[] Items matching the requested types
* @api
*/
public function getItems(...$types);

/**
* Return the first item, optionally of particular types
*
* @param array ...$types Item types
*
* @return ItemInterface Item
* @api
*/
public function getFirstItem(...$types);
}
71 changes: 71 additions & 0 deletions src/Micrometa/Tests/Micrometa/Ports/Item/ItemListTest.php
@@ -0,0 +1,71 @@
<?php

namespace Micrometa\Ports\Item;

use Jkphl\Micrometa\Infrastructure\Factory\MicroformatsFactory;
use Jkphl\Micrometa\Ports\Exceptions\OutOfBoundsException;
use Jkphl\Micrometa\Ports\Item\ItemInterface;
use Jkphl\Micrometa\Ports\Item\ItemList;
use Jkphl\Micrometa\Tests\MicroformatsFeedTrait;
use PHPUnit\Framework\TestCase;

class ItemListTest extends TestCase
{
/**
* Use the Microformats feed method
*/
use MicroformatsFeedTrait;

private $feedItemList;

protected function setUp(): void
{
$this->feedItemList = new ItemList([$this->getFeedItem(), $this->getFeedItem()]);
}

public function testNestedItemsCounts()
{
// Test the number of nested items
self::assertCount(2, $this->feedItemList);
self::assertCount(2, $this->feedItemList->getItems());
}

public function testNestedItemsIteration()
{
foreach ($this->feedItemList->getItems() as $itemIndex => $entryItem) {
self::assertInstanceOf(ItemInterface::class, $entryItem);
self::assertIsInt($itemIndex);
}
}

public function testNestedItemsRetrievalViaArrayAccess()
{
$entryItems = $this->feedItemList->getFirstItem()->getItems('h-entry');
$entryItem = $entryItems[1];

self::assertInstanceOf(ItemInterface::class, $entryItem);
}

public function testFirstNestedItemRetrieval()
{
self::assertInstanceOf(ItemInterface::class, $this->feedItemList[0]->getFirstItem('h-entry'));
self::assertInstanceOf(
ItemInterface::class,
$this->feedItemList[0]->getFirstItem('h-entry', MicroformatsFactory::MF2_PROFILE_URI)
);
}

public function testExistingFirstNestedItem()
{
self::assertEquals('John Doe', $this->feedItemList[0]->hEntry()->author->name);
self::assertEquals('John Doe', $this->feedItemList->getFirstItem()->hEntry()->author->name);
}

public function testNonExistingSecondNestedItem()
{
$this->expectException(OutOfBoundsException::class);
$this->expectExceptionCode('1492418999');

$this->feedItemList->hEntry(2);
}
}

0 comments on commit 79c3ceb

Please sign in to comment.