Skip to content

Commit

Permalink
Add a Group entity
Browse files Browse the repository at this point in the history
This relates to graphp#39.

I decided to break BC and introduce a new entity to represent a Group.
Design choices:

- A group is unique in the entire graph.
- An entity doesn't have to be in a group.
- A group can use its Graph to find out which vertices are also in it.
- A group has to be created before it can be used (same for vertices).
  • Loading branch information
matthiasnoback committed Feb 6, 2019
1 parent a5455ed commit 7cc72c8
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 45 deletions.
34 changes: 34 additions & 0 deletions src/Graph.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class Graph implements DualAggregate, AttributeAware

protected $attributes = array();

/**
* @var Group[]
*/
protected $groups = array();

public function __construct()
{
$this->vertices = VerticesMap::factoryArrayReference($this->verticesStorage);
Expand Down Expand Up @@ -466,4 +471,33 @@ public function getAttributeBag()
{
return new AttributeBagReference($this->attributes);
}

/**
* @param int|string $groupId
* @param bool $returnDuplicate
* @return Group
*/
public function createGroup($groupId, $returnDuplicate = false)
{
if ($returnDuplicate && isset($this->groups[$groupId])) {
return $this->groups[$groupId];
}

if (isset($this->groups[$groupId])) {
throw new OverflowException('Group already exists: ' . $groupId);
}

$group = new Group($this, $groupId);
$this->groups[$groupId] = $group;

return $group;
}

/**
* @return array|Group[]
*/
public function getGroups()
{
return array_values($this->groups);
}
}
79 changes: 79 additions & 0 deletions src/Group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Fhaculty\Graph;

use Fhaculty\Graph\Attribute\AttributeAware;
use Fhaculty\Graph\Attribute\AttributeBagReference;
use Fhaculty\Graph\Exception\InvalidArgumentException;

class Group implements AttributeAware
{
/**
* @var int|string
*/
private $id;

/**
* @var Graph
*/
private $graph;

private $attributes = array();

public function __construct(Graph $graph, $id)
{
if (!is_int($id) && !is_string($id)) {
throw new InvalidArgumentException('Group ID has to be of type integer or string');
}

$this->id = $id;
$this->graph = $graph;
}

public function getId()
{
return $this->id;
}

public function getVerticesInGroup()
{
$thisGroup = $this;

return array_values(array_filter(
$this->graph->getVertices()->getMap(),
function (Vertex $vertex) use ($thisGroup) {
return $vertex->getGroup() instanceof Group && $vertex->getGroup()->equals($thisGroup);
}
));
}

public function getAttribute($name, $default = null)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
}

public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
}

public function removeAttribute($name)
{
unset($this->attributes[$name]);
}

public function getAttributeBag()
{
return new AttributeBagReference($this->attributes);
}

public function __toString()
{
return (string)$this->id;
}

public function equals(Group $other)
{
return $this->id === $other->id;
}
}
22 changes: 10 additions & 12 deletions src/Set/Vertices.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Fhaculty\Graph\Set;

use Fhaculty\Graph\Group;
use Fhaculty\Graph\Vertex;
use Fhaculty\Graph\Exception\InvalidArgumentException;
use Fhaculty\Graph\Exception\OutOfBoundsException;
Expand Down Expand Up @@ -511,19 +512,16 @@ private function getCallback($callback)
return $callback;
}

static $methods = array(
self::ORDER_ID => 'getId',
self::ORDER_GROUP => 'getGroup'
);

if (!is_int($callback) || !isset($methods[$callback])) {
throw new InvalidArgumentException('Invalid callback given');
if ($callback === self::ORDER_ID) {
return function (Vertex $vertex) {
return $vertex->getId();
};
} elseif ($callback === self::ORDER_GROUP) {
return function (Vertex $vertex) {
return $vertex->getGroup() instanceof Group ? $vertex->getGroup()->getId() : null;
};
}

$method = $methods[$callback];

return function (Vertex $vertex) use ($method) {
return $vertex->$method();
};
throw new InvalidArgumentException('Invalid callback given');
}
}
20 changes: 9 additions & 11 deletions src/Vertex.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ class Vertex implements EdgesAggregate, AttributeAware
private $balance;

/**
* group number
* group
*
* @var int
* @var Group|null
* @see Vertex::setGroup()
* @see Vertex::getGroup()
*/
private $group = 0;
private $group;

private $attributes = array();

Expand Down Expand Up @@ -90,26 +91,23 @@ public function setBalance($balance)
}

/**
* set group number of this vertex
* set group of this vertex
*
* @param int $group
* @param Group|null $group
* @return Vertex $this (chainable)
* @throws InvalidArgumentException if group is not numeric
*/
public function setGroup($group)
public function setGroup(Group $group = null)
{
if (!is_int($group)) {
throw new InvalidArgumentException('Invalid group number');
}
$this->group = $group;

return $this;
}

/**
* get group number
* get group
*
* @return int
* @return Group|null
*/
public function getGroup()
{
Expand Down
42 changes: 40 additions & 2 deletions tests/GraphTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Fhaculty\Graph\Tests;

use Fhaculty\Graph\Exception\OutOfBoundsException;
use Fhaculty\Graph\Exception\OverflowException;
use Fhaculty\Graph\Exception\InvalidArgumentException;
use Fhaculty\Graph\Graph;
use Fhaculty\Graph\Group;
use Fhaculty\Graph\Tests\Attribute\AbstractAttributeAwareTest;

class GraphTest extends AbstractAttributeAwareTest
Expand All @@ -17,7 +19,7 @@ public function setup()
public function testVertexClone()
{
$graph = new Graph();
$vertex = $graph->createVertex(123)->setBalance(10)->setGroup(4);
$vertex = $graph->createVertex(123)->setBalance(10)->setGroup($graph->createGroup(4));

$newgraph = new Graph();
$newvertex = $newgraph->createVertexClone($vertex);
Expand Down Expand Up @@ -56,7 +58,7 @@ public function testGetVertexNonexistant()
public function testGraphClone()
{
$graph = new Graph();
$graph->createVertex(123)->setBalance(10)->setGroup(4);
$graph->createVertex(123)->setBalance(10)->setGroup($graph->createGroup(4));

$newgraph = $graph->createGraphClone();

Expand Down Expand Up @@ -391,6 +393,42 @@ public function testCreateGraphCloneVertices()
$this->assertEquals(1, count($graphClone->getEdges()));
}

public function testGetGroups()
{
$graph = new Graph();
$graph->createGroup(1);
$graph->createGroup(2);

$this->assertEquals(
array(
new Group($graph, 1),
new Group($graph, 2)
),
$graph->getGroups()
);
}

public function testCreateGroupReturnDuplicate()
{
$graph = new Graph();
$alreadyCreated = $graph->createGroup(1);

$this->assertSame(
$alreadyCreated,
$graph->createGroup(1, true)
);
}

/**
* @expectedException OverflowException
*/
public function testCreateGroupAlreadyExists()
{
$graph = new Graph();
$graph->createGroup(1);
$graph->createGroup(1);
}

protected function createAttributeAware()
{
return new Graph();
Expand Down
77 changes: 77 additions & 0 deletions tests/GroupTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Fhaculty\Graph\Tests;

use Fhaculty\Graph\Exception\InvalidArgumentException;
use Fhaculty\Graph\Graph;
use Fhaculty\Graph\Group;
use Fhaculty\Graph\Tests\Attribute\AbstractAttributeAwareTest;

final class GroupTest extends AbstractAttributeAwareTest
{
public function testGroupIdCanBeAnInteger()
{
$group = new Group(new Graph(), 1);

$this->assertSame(1, $group->getId());
}

public function testGroupIdCanBeAString()
{
$group = new Group(new Graph(), 'string');

$this->assertSame('string', $group->getId());
}

/**
* @expectedException InvalidArgumentException
*/
public function testGroupIdCanNotBeSomethingElse()
{
new Group(new Graph(), null);
}

public function testGroupEquals()
{
$group = new Group(new Graph(), 1);

$this->assertTrue($group->equals(new Group(new Graph(), 1)));
}

public function testGroupIsNotEqual()
{
$group = new Group(new Graph(), 1);

$this->assertFalse($group->equals(new Group(new Graph(), 2)));
}

public function testFindAllVerticesInGroup()
{
$graph = new Graph();
$group1 = $graph->createGroup(1);
$vertex1 = $graph->createVertex(1)->setGroup($group1);
$vertex2 = $graph->createVertex(2)->setGroup($group1);
$group2 = $graph->createGroup(2);
$vertex3 = $graph->createVertex(3)->setGroup($group2);

$this->assertEquals(
array(
$vertex1,
$vertex2
),
$group1->getVerticesInGroup()
);

$this->assertEquals(
array(
$vertex3
),
$group2->getVerticesInGroup()
);
}

protected function createAttributeAware()
{
return new Group(new Graph(), 1);
}
}
20 changes: 10 additions & 10 deletions tests/Set/BaseVerticesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,26 +198,26 @@ public function returnTrue(Vertex $vertex)
public function testOrderByGroup()
{
$graph = new Graph();
$graph->createVertex()->setGroup(1);
$graph->createVertex()->setGroup(100);
$graph->createVertex()->setGroup(5);
$graph->createVertex()->setGroup(100);
$graph->createVertex()->setGroup(100);
$graph->createVertex()->setGroup(2);
$biggest = $graph->createVertex()->setGroup(200);
$graph->createVertex()->setGroup($graph->createGroup(1));
$graph->createVertex()->setGroup($graph->createGroup(100));
$graph->createVertex()->setGroup($graph->createGroup(5));
$graph->createVertex()->setGroup($graph->createGroup(100, true));
$graph->createVertex()->setGroup($graph->createGroup(100, true));
$graph->createVertex()->setGroup($graph->createGroup(2));
$biggest = $graph->createVertex()->setGroup($graph->createGroup(200));

$vertices = $graph->getVertices();
$verticesOrdered = $vertices->getVerticesOrder(Vertices::ORDER_GROUP);

$this->assertInstanceOf('Fhaculty\Graph\Set\Vertices', $verticesOrdered);
$this->assertEquals(1, $verticesOrdered->getVertexFirst()->getGroup());
$this->assertEquals(200, $verticesOrdered->getVertexLast()->getGroup());
$this->assertEquals(1, $verticesOrdered->getVertexFirst()->getGroup()->getId());
$this->assertEquals(200, $verticesOrdered->getVertexLast()->getGroup()->getId());

$this->assertSame($biggest, $verticesOrdered->getVertexLast());
$this->assertSame($biggest, $vertices->getVertexOrder(Vertices::ORDER_GROUP, true));

$sumgroups = function(Vertex $vertex) {
return $vertex->getGroup();
return $vertex->getGroup()->getId();
};
$this->assertSame(508, $vertices->getSumCallback($sumgroups));
$this->assertSame(508, $verticesOrdered->getSumCallback($sumgroups));
Expand Down
Loading

0 comments on commit 7cc72c8

Please sign in to comment.