mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 09:48:40 +00:00
fix bugs and start impl. additional functions
This commit is contained in:
parent
e2f6061fa7
commit
522db1d462
|
|
@ -416,11 +416,11 @@ class DataMapperAbstract implements DataMapperInterface
|
|||
}
|
||||
|
||||
if ($condValue['value'] !== null) {
|
||||
$where1->andWhere(static::$table . '_' . $searchDepth . '.' . $column, $condValue['comparison'], $condValue['value']);
|
||||
$where1->andWhere(static::$table . '_d' . $searchDepth . '.' . $column, $condValue['comparison'], $condValue['value']);
|
||||
}
|
||||
|
||||
if ($condValue['orderBy'] !== null) {
|
||||
$where1->orderBy(static::$table . '_' . $searchDepth . '.' . static::getColumnByMember($condValue['orderBy']), $condValue['sortOrder']);
|
||||
$where1->orderBy(static::$table . '_d' . $searchDepth . '.' . static::getColumnByMember($condValue['orderBy']), $condValue['sortOrder']);
|
||||
}
|
||||
|
||||
if ($condValue['limit'] !== null) {
|
||||
|
|
@ -433,7 +433,7 @@ class DataMapperAbstract implements DataMapperInterface
|
|||
$hasAutocompletes = false;
|
||||
foreach (static::$columns as $col) {
|
||||
if (isset($col['autocomplete']) && $col['autocomplete']) {
|
||||
$where2->where(static::$table . '_' . $searchDepth . '.' . $col['name'], 'LIKE', '%' . $search . '%', 'OR');
|
||||
$where2->where(static::$table . '_d' . $searchDepth . '.' . $col['name'], 'LIKE', '%' . $search . '%', 'OR');
|
||||
$hasAutocompletes = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace phpOMS\Message;
|
||||
|
||||
use phpOMS\Localization\ISO639x1Enum;
|
||||
|
||||
/**
|
||||
* Response abstract class.
|
||||
*
|
||||
|
|
@ -100,6 +102,10 @@ abstract class ResponseAbstract implements \JsonSerializable, MessageInterface
|
|||
*/
|
||||
public function getLanguage() : string
|
||||
{
|
||||
if (!isset($this->header)) {
|
||||
return ISO639x1Enum::_EN;
|
||||
}
|
||||
|
||||
return $this->header->l11n->getLanguage();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class Edge
|
|||
* @var float
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private float $weight = 0.0;
|
||||
private float $weight = 1.0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
|
@ -70,7 +70,7 @@ class Edge
|
|||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct(Node $node1, Node $node2, float $weight = 0.0, bool $isDirected = false)
|
||||
public function __construct(Node $node1, Node $node2, float $weight = 1.0, bool $isDirected = false)
|
||||
{
|
||||
$this->node1 = $node1;
|
||||
$this->node2 = $node2;
|
||||
|
|
|
|||
|
|
@ -21,29 +21,6 @@ namespace phpOMS\Stdlib\Graph;
|
|||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @todo Orange-Management/phpOMS#10
|
||||
* * Count all paths between 2 nodes
|
||||
* * Return all paths between 2 nodes
|
||||
* * Find cycles using graph coloring
|
||||
* * Find a negative cycle
|
||||
* * Find cycles with n length
|
||||
* * Find cycles with odd length
|
||||
* * Find shortest path between 2 nodes
|
||||
* * Find longest path between 2 nodes
|
||||
* * Find islands
|
||||
* * Find all unreachable nodes
|
||||
* * Check if strongly connected
|
||||
* * Find longest path between 2 nodes
|
||||
* * Find longest path
|
||||
* * Get the girth
|
||||
* * Get the circuit rank
|
||||
* * Get the node connectivity
|
||||
* * Get the edge connectivity
|
||||
* * Is the graph connected
|
||||
* * Get the unconnected nodes as their own graph
|
||||
* * Check if bipartite
|
||||
* * Check if triangle free
|
||||
*/
|
||||
class Graph
|
||||
{
|
||||
|
|
@ -511,15 +488,79 @@ class Graph
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform depth first traversal.
|
||||
* Perform depth first traversal
|
||||
*
|
||||
* @param int|string|Node $node1 Graph node
|
||||
* @param int|string|Node $node2 Graph node
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function depthFirstTraversal() : array
|
||||
private function depthFirstTraversal(
|
||||
int | string | Node $node1,
|
||||
int | string | Node $node2 = null,
|
||||
array &$visited,
|
||||
array &$path,
|
||||
array &$paths
|
||||
) : array
|
||||
{
|
||||
return [];
|
||||
$visited[$node1->getId()] = true;
|
||||
|
||||
if ($node1->isEquals($node2)) {
|
||||
$paths[] = $path;
|
||||
}
|
||||
|
||||
$neighbors = $node1->getNeighbors();
|
||||
foreach ($neighbors as $neighbor) {
|
||||
if (!isset($visited[$neighbor->getId()]) || !$visited[$neighbor->getId()]) {
|
||||
$path[] = $neighbor;
|
||||
$this->depthFirstTraversal($neighbor, $node2, $visited, $path);
|
||||
\array_pop($path);
|
||||
}
|
||||
}
|
||||
|
||||
$visited[$node1->getId()] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all reachable nodes with depth first traversal (iterative)
|
||||
*
|
||||
* @param int|string|Node $node1 Graph node
|
||||
*
|
||||
* @return Node[]
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function findAllReachableNodesDFS(int | string | Node $node1) : array
|
||||
{
|
||||
$nodes = [];
|
||||
|
||||
if (!($node1 instanceof Node)) {
|
||||
$node1 = $this->getNode($node1);
|
||||
}
|
||||
|
||||
$visited = [];
|
||||
$stack = [];
|
||||
$stack[] = $node1;
|
||||
|
||||
while (!empty($stack)) {
|
||||
$cNode = \array_pop($stack);
|
||||
$nodes[] = $cNode;
|
||||
|
||||
if (!isset($visited[$cNode->getId()]) || !$visited[$cNode->getId()]) {
|
||||
$visited[$cNode->getId()] = true;
|
||||
}
|
||||
|
||||
$neighbors = $cNode->getNeighbors();
|
||||
foreach ($neighbors as $neighbor) {
|
||||
if (!isset($visited[$cNode->getId()]) || !$visited[$cNode->getId()]) {
|
||||
$stack[] = $neighbor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -546,6 +587,54 @@ class Graph
|
|||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all paths between two nodes
|
||||
*
|
||||
* @param int|string|Node $node1 Graph node
|
||||
* @param int|string|Node $node2 Graph node
|
||||
*
|
||||
* @return array<int, Node[]>
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getAllPathsBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
|
||||
{
|
||||
if (!($node1 instanceof Node)) {
|
||||
$node1 = $this->getNode($node1);
|
||||
}
|
||||
|
||||
if (!($node2 instanceof Node)) {
|
||||
$node2 = $this->getNode($node2);
|
||||
}
|
||||
|
||||
if ($node1 === null || $node2 === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$path = [];
|
||||
$paths = [];
|
||||
$visited = [];
|
||||
|
||||
$this->depthFirstTraversal($node1, $node2, $visited, $path, $paths);
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count possible paths between two nodes.
|
||||
*
|
||||
* @param int|string|Node $node1 Graph node
|
||||
* @param int|string|Node $node2 Graph node
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function countAllPathsBetweenNodes(int | string | Node $node1, int | string | Node $node2) : int
|
||||
{
|
||||
return \count($this->getAllPathsBetweenNodes($node1, $node2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get longest path between two nodes.
|
||||
*
|
||||
|
|
@ -558,15 +647,48 @@ class Graph
|
|||
*/
|
||||
public function longestPathBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
|
||||
{
|
||||
if (!($node1 instanceof Node)) {
|
||||
$node1 = $this->getNode($node1);
|
||||
$paths = $this->getAllPathsBetweenNodes($node1, $node2);
|
||||
|
||||
if (empty($paths)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!($node2 instanceof Node)) {
|
||||
$node2 = $this->getNode($node2);
|
||||
foreach ($paths as $key => $path) {
|
||||
$edges[$key] = 0;
|
||||
foreach ($path as $node) {
|
||||
$edges[$key] += $node->getEdgeByNeighbor()->getWeight();
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
\arsort($edges);
|
||||
|
||||
return $paths[\array_key_first($edges)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortest path between two nodes.
|
||||
*
|
||||
* @param int|string|Node $node1 Graph node
|
||||
* @param int|string|Node $node2 Graph node
|
||||
*
|
||||
* @return Node[]
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function shortestPathBetweenNodes(int | string | Node $node1, int | string | Node $node2) : array
|
||||
{
|
||||
$paths = $this->getAllPathsBetweenNodes($node1, $node2);
|
||||
|
||||
foreach ($paths as $key => $path) {
|
||||
$edges[$key] = 0;
|
||||
foreach ($path as $node) {
|
||||
$edges[$key] += $node->getEdgeByNeighbor()->getWeight();
|
||||
}
|
||||
}
|
||||
|
||||
\asort($edges);
|
||||
|
||||
return $paths[\array_key_first($edges)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -684,21 +806,13 @@ class Graph
|
|||
*/
|
||||
public function isConnected() : bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (empty($this->nodes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unconnected sub graphs
|
||||
*
|
||||
* @return Graph[]
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getUnconnected() : array
|
||||
{
|
||||
// get all unconnected sub graphs
|
||||
$nodes = $this->findAllReachableNodesDFS(\reset($this->nodes));
|
||||
|
||||
return [];
|
||||
return \count($nodes) === \count($this->nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -172,6 +172,26 @@ class Node
|
|||
return $this->edges[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get graph edge by neighbor.
|
||||
*
|
||||
* @param Node $node Neighbor node
|
||||
*
|
||||
* @return null|Edge
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getEdgeByNeighbor(self $node) : ?Edge
|
||||
{
|
||||
foreach ($this->edges as $edge) {
|
||||
if ($edge->getNode1()->isEqual($node) || $edge->getNode2()->isEqual($node)) {
|
||||
return $edge;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get graph edges
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace phpOMS\tests\Message;
|
|||
require_once __DIR__ . '/../Autoloader.php';
|
||||
|
||||
use phpOMS\Message\ResponseAbstract;
|
||||
use phpOMS\Localization\ISO639x1Enum;
|
||||
|
||||
/**
|
||||
* @testdox phpOMS\tests\Message\ResponseAbstractTest: Abstract response
|
||||
|
|
@ -52,6 +53,7 @@ class ResponseAbstractTest extends \PHPUnit\Framework\TestCase
|
|||
{
|
||||
self::assertNull($this->response->get('asdf'));
|
||||
self::assertEquals('', $this->response->getBody());
|
||||
self::assertTrue(ISO639x1Enum::isValidValue($this->response->getLanguage()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class EdgeTest extends \PHPUnit\Framework\TestCase
|
|||
self::assertEquals([new Node('1'), new Node('2')], $edge->getNodes());
|
||||
self::assertTrue($edge->getNode1()->isEqual(new Node('1')));
|
||||
self::assertTrue($edge->getNode2()->isEqual(new Node('2')));
|
||||
self::assertEquals(0.0, $edge->getWeight());
|
||||
self::assertEquals(1.0, $edge->getWeight());
|
||||
self::assertFalse($edge->isDirected());
|
||||
}
|
||||
|
||||
|
|
@ -57,8 +57,8 @@ class EdgeTest extends \PHPUnit\Framework\TestCase
|
|||
*/
|
||||
public function testWeightInputOutput() : void
|
||||
{
|
||||
$edge = new Edge(new Node('7'), new Node('8'), 1.0, true);
|
||||
self::assertEquals(1.0, $edge->getWeight());
|
||||
$edge = new Edge(new Node('7'), new Node('8'), 2.0, true);
|
||||
self::assertEquals(2.0, $edge->getWeight());
|
||||
|
||||
$edge = new Edge(new Node('7'), new Node('8'), 1.0);
|
||||
$edge->setWeight(3.0);
|
||||
|
|
|
|||
|
|
@ -58,11 +58,8 @@ class GraphTest extends \PHPUnit\Framework\TestCase
|
|||
self::assertEquals([], $this->graph->getBridges());
|
||||
self::assertEquals([], $this->graph->getFloydWarshallShortestPath());
|
||||
self::assertEquals([], $this->graph->getDijkstraShortestPath());
|
||||
self::assertEquals([], $this->graph->depthFirstTraversal());
|
||||
self::assertEquals([], $this->graph->breadthFirstTraversal());
|
||||
self::assertEquals([], $this->graph->longestPath());
|
||||
self::assertEquals([], $this->graph->longestPathBetweenNodes('invalid1', 'invalid2'));
|
||||
self::assertEquals([], $this->graph->getUnconnected());
|
||||
|
||||
self::assertEquals(0, $this->graph->getCost());
|
||||
self::assertEquals($this->graph, $this->graph->getKruskalMinimalSpanningTree());
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
|
|||
self::assertEquals(\basename(__DIR__ . '/FileUtilsTest.php'), FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_BASENAME));
|
||||
self::assertEquals('php', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_EXTENSION));
|
||||
self::assertEquals('FileUtilsTest', FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME));
|
||||
|
||||
self::assertEquals(
|
||||
[
|
||||
'dirname' => __DIR__,
|
||||
|
|
@ -108,7 +109,7 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
|
|||
'extension' => 'php',
|
||||
'filename' => 'FileUtilsTest',
|
||||
],
|
||||
FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php', \PATHINFO_FILENAME)
|
||||
FileUtils::mb_pathinfo(__DIR__ . '/FileUtilsTest.php')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user