Skip to content
2 changes: 1 addition & 1 deletion src/Driver/Cassandra/Cassandra.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public function query(Query $query): QueryResult

$rawQueryResult = $this->rawQuery($queryString);

$result = new QueryResult((bool)$rawQueryResult);
$result = new QueryResult();
$result->setQueryString($queryString);
foreach ($rawQueryResult as $resultRow) {
/** @var class-string<ModelInterface> $modelClass */
Expand Down
20 changes: 20 additions & 0 deletions src/Driver/Features/SearchCountableInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Aternos\Model\Driver\Features;

use Aternos\Model\Search\Search;

/**
* Class SearchCountableInterface
* @package Aternos\Model\Driver\Features
*/
interface SearchCountableInterface
{
/**
* Execute a count for a search query
*
* @param Search $search
* @return int
*/
public function searchCount(Search $search): int;
}
4 changes: 2 additions & 2 deletions src/Driver/Mysqli/Mysqli.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ protected function connect(): void
* @throws MysqlConnectionFailedException if connecting to the mysql database fails
* @throws MysqlException if a mysql error occurs while executing the query
*/
protected function rawQuery(string $query): mysqli_result|bool
protected function rawQuery(string $query): mysqli_result|true
{
$this->connect();
$result = mysqli_query($this->connection, $query);
Expand Down Expand Up @@ -239,7 +239,7 @@ public function query(Query $query): QueryResult

$rawQueryResult = $this->rawQuery($queryString);

$result = new QueryResult((bool)$rawQueryResult);
$result = new QueryResult();
$result->setQueryString($queryString);
if ($query instanceof UpdateQuery || $query instanceof DeleteQuery) {
$result->setAffectedRows(mysqli_affected_rows($this->connection));
Expand Down
29 changes: 27 additions & 2 deletions src/Driver/OpenSearch/OpenSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Aternos\Model\Driver\Driver;
use Aternos\Model\Driver\Features\CRUDAbleInterface;
use Aternos\Model\Driver\Features\SearchableInterface;
use Aternos\Model\Driver\Features\SearchCountableInterface;
use Aternos\Model\Driver\OpenSearch\Authentication\OpenSearchAuthenticationInterface;
use Aternos\Model\Driver\OpenSearch\Exception\HttpErrorResponseException;
use Aternos\Model\Driver\OpenSearch\Exception\HttpTransportException;
Expand All @@ -28,7 +29,7 @@
*
* @package Aternos\Model\Driver\Search
*/
class OpenSearch extends Driver implements CRUDAbleInterface, SearchableInterface
class OpenSearch extends Driver implements CRUDAbleInterface, SearchableInterface, SearchCountableInterface
{
public const ID = "opensearch";
protected string $id = self::ID;
Expand Down Expand Up @@ -219,7 +220,7 @@ public function search(Search $search): SearchResult
throw new SerializeException("Received invalid search response from OpenSearch");
}

$result = new SearchResult(true);
$result = new SearchResult();
if (isset($response->took) && is_int($response->took)) {
$result->setSearchTime($response->took);
}
Expand All @@ -242,6 +243,30 @@ public function search(Search $search): SearchResult
return $result;
}

/**
* @param Search $search
* @return int
* @throws HttpErrorResponseException If the response status code is not 2xx
* @throws HttpTransportException If an error happens while the http client processes the request
* @throws SerializeException If an error happens during (de-)serialization
*/
public function searchCount(Search $search): int
{
/** @var class-string<ModelInterface> $modelClassName */
$modelClassName = $search->getModelClassName();

$response = $this->request(
"GET",
$this->buildUrl($modelClassName::getName(), "_count"),
$search->getSearchQuery()
);
if (!isset($response->count) || !is_int($response->count)) {
throw new SerializeException("Received invalid count response from OpenSearch");
}

return $response->count;
}

/**
* @param stdClass $response
* @param class-string<ModelInterface> $modelClass $modelClass
Expand Down
2 changes: 1 addition & 1 deletion src/Driver/Test/TestTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function query(Query $query): QueryResult
$entries = $this->groupAndAggregateEntries($clonedEntries, $query->getGroup(), $query->getFields());
}

$queryResult = new QueryResult(true);
$queryResult = new QueryResult();
foreach ($entries as $entry) {
if ($query instanceof SelectQuery) {
/** @var class-string<ModelInterface> $modelClass */
Expand Down
145 changes: 105 additions & 40 deletions src/GenericModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,32 @@
use Aternos\Model\Driver\DriverRegistry;
use Aternos\Model\Driver\DriverRegistryInterface;
use Aternos\Model\Driver\Test\TestDriver;
use Aternos\Model\Driver\Features\{CacheableInterface,
DeletableInterface,
DeleteQueryableInterface,
GettableInterface,
QueryableInterface,
SavableInterface,
SearchableInterface,
SelectQueryableInterface,
UpdateQueryableInterface
};
use Aternos\Model\Driver\Features\CacheableInterface;
use Aternos\Model\Driver\Features\DeletableInterface;
use Aternos\Model\Driver\Features\DeleteQueryableInterface;
use Aternos\Model\Driver\Features\GettableInterface;
use Aternos\Model\Driver\Features\QueryableInterface;
use Aternos\Model\Driver\Features\SavableInterface;
use Aternos\Model\Driver\Features\SearchableInterface;
use Aternos\Model\Driver\Features\SearchCountableInterface;
use Aternos\Model\Driver\Features\SelectQueryableInterface;
use Aternos\Model\Driver\Features\UpdateQueryableInterface;
use Aternos\Model\Driver\Mysqli\Mysqli;
use Aternos\Model\Driver\Redis\Redis;
use Aternos\Model\Query\{CountField,
DeleteQuery,
GroupField,
Limit,
Query,
QueryResult,
QueryResultCollection,
SelectQuery,
UpdateQuery,
WhereCondition,
WhereGroup
};
use Aternos\Model\Query\CountField;
use Aternos\Model\Query\DeleteQuery;
use Aternos\Model\Query\GroupField;
use Aternos\Model\Query\Limit;
use Aternos\Model\Query\Query;
use Aternos\Model\Query\QueryResult;
use Aternos\Model\Query\QueryResultCollection;
use Aternos\Model\Query\SelectQuery;
use Aternos\Model\Query\UpdateQuery;
use Aternos\Model\Query\WhereCondition;
use Aternos\Model\Query\WhereGroup;
use Aternos\Model\Search\Search;
use Aternos\Model\Search\SearchResult;
use BadMethodCallException;
use Exception;

/**
* Class GenericModel
Expand Down Expand Up @@ -241,6 +239,22 @@ protected static function getSearchableDrivers(): array
return $drivers;
}

/**
* Get all search-countable drivers from static::$drivers
*
* @return class-string<SearchCountableInterface>[]
*/
protected static function getSearchCountableDrivers(): array
{
$drivers = [];
foreach (static::$drivers as $driver) {
if (static::getDriverRegistry()->isDriverInstanceOf($driver, SearchCountableInterface::class)) {
$drivers[] = $driver;
}
}
return $drivers;
}

/**
* @param array $rawData
* @return bool
Expand Down Expand Up @@ -463,26 +477,35 @@ public static function query(Query $query): QueryResult

$result = false;
$results = [];
$lastException = null;
foreach ($drivers as $queryableDriver) {
/** @var QueryableInterface $driver */
$driver = static::getDriverRegistry()->getDriver($queryableDriver);
$result = $driver->query($query);
try {
$result = $driver->query($query);

if ($result->wasSuccessful() && $query instanceof SelectQuery) {
break;
}
if ($query instanceof SelectQuery) {
$lastException = null;
break;
}

if (!$query instanceof SelectQuery) {
$results[] = $result;
} catch (ModelException $e) {
$lastException = $e;
}
}

if ($lastException !== null) {
/** @var ModelException|null $lastException */
throw $lastException;
}

if ($result === false) {
throw new BadMethodCallException("You can't query the model if no queryable driver is available.");
}

if (static::$registry) {
if ($query instanceof SelectQuery && $result->wasSuccessful() && $query->shouldSaveResultsToRegistry()) {
if ($query instanceof SelectQuery && $query->shouldSaveResultsToRegistry()) {
foreach ($result as $model) {
if ($model->getId() === null) {
continue;
Expand All @@ -495,7 +518,7 @@ public static function query(Query $query): QueryResult
if ($query instanceof SelectQuery || count($results) === 1) {
return $result;
} else {
return new QueryResultCollection(true, $results);
return new QueryResultCollection($results);
}
}

Expand All @@ -513,6 +536,7 @@ public static function query(Query $query): QueryResult
* @param array|GroupField[]|string[]|null $group
* @param bool $saveResultsToRegistry Whether results of this query should be saved in the model registry.
* @return QueryResult<static>|static[]
* @throws ModelException
* @noinspection PhpDocSignatureInspection
*/
public static function select(null|WhereCondition|array|WhereGroup $where = null,
Expand All @@ -527,14 +551,12 @@ public static function select(null|WhereCondition|array|WhereGroup $where = null

/**
* @param WhereCondition|array|WhereGroup|null $where
* @return int|null
* @return int
* @throws ModelException
*/
public static function count(null|WhereCondition|array|WhereGroup $where = null): ?int
public static function count(null|WhereCondition|array|WhereGroup $where = null): int
{
$result = static::select(where: $where, fields: [new CountField()]);
if (!$result->wasSuccessful()) {
return null;
}
if (count($result) === 0) {
return 0;
}
Expand Down Expand Up @@ -666,28 +688,36 @@ public function set(array $data): QueryResult
*
* @param Search $search
* @return SearchResult<static>
* @throws ModelException
*/
public static function search(Search $search): SearchResult
{
$search->setModelClassName(static::class);

$result = false;
$lastException = null;
foreach (static::getSearchableDrivers() as $searchableDriver) {
/** @var SearchableInterface $driver */
$driver = static::getDriverRegistry()->getDriver($searchableDriver);
$result = $driver->search($search);

if ($result->wasSuccessful()) {
break;
try {
$result = $driver->search($search);
$lastException = null;
} catch (ModelException $e) {
$lastException = $e;
}
}

if ($lastException !== null) {
/** @var ModelException|null $lastException */
throw $lastException;
}

if ($result === false) {
throw new BadMethodCallException("You can't search the model if no searchable driver is available.");
}

if (static::$registry) {
if ($result->wasSuccessful() && count($result) > 0) {
if (count($result) > 0) {
foreach ($result as $model) {
if ($model->getId() === null) {
continue;
Expand All @@ -699,4 +729,39 @@ public static function search(Search $search): SearchResult

return $result;
}

/**
* Count how many instances of the model match this search
*
* @param Search $search
* @return int
*/
public static function searchCount(Search $search): int
{
$search->setModelClassName(static::class);

$result = false;
$lastException = null;
foreach (static::getSearchCountableDrivers() as $searchableDriver) {
/** @var SearchCountableInterface $driver */
$driver = static::getDriverRegistry()->getDriver($searchableDriver);
try {
$result = $driver->searchCount($search);
$lastException = null;
} catch (ModelException $e) {
$lastException = $e;
}
}

if ($lastException !== null) {
/** @var ModelException|null $lastException */
throw $lastException;
}

if ($result === false) {
throw new BadMethodCallException("You can't search the model if no search-countable driver is available.");
}

return $result;
}
}
14 changes: 12 additions & 2 deletions src/ModelCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,19 @@
*/
class ModelCollection implements Iterator, Countable, ArrayAccess
{
protected array $models = [];
protected array $models;
protected int $iterator = 0;

/**
* ModelCollection constructor.
*
* @param TModel[] $models
*/
public function __construct(array $models = [])
{
$this->models = $models;
}

/**
* Add a model
*
Expand Down Expand Up @@ -131,4 +141,4 @@ public function offsetUnset(mixed $offset): void
unset($this->models[$offset]);
}

}
}
Loading
Loading