Skip to content

Commit

Permalink
feat(db): added aggregate and order by functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
hejrobin committed Mar 1, 2024
1 parent f3dac09 commit 0e8eb7f
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 50 deletions.
19 changes: 19 additions & 0 deletions vendor/Haku/Database/Attributes/Aggregate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);

namespace Haku\Database\Attributes;

/* @note Deny direct file access */
if (defined('HAKU_ROOT_PATH') === false) exit;

use \Attribute;

#[Attribute(Attribute::TARGET_PROPERTY)]
class Aggregate
{

public function __construct(
public string $aggregate,
) {}

}
50 changes: 44 additions & 6 deletions vendor/Haku/Database/Mixins/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
Timestamp,
Omitted,
Included,
Validates
Validates,
Aggregate
};

use Haku\Schema\{
Expand All @@ -44,6 +45,7 @@ trait Entity
protected readonly array $timestampFields;
protected readonly array $omittedFields;
protected readonly array $includedFields;
protected readonly array $aggregatedFields;

protected bool $isValid = false;
protected bool $hasChanges = false;
Expand Down Expand Up @@ -85,6 +87,7 @@ protected function prepareEntityProperties(): void
$omittedFields = [];
$includedFields = [];
$timestampFields = [];
$aggregatedFields = [];

$properties = $ref->getProperties();

Expand All @@ -94,6 +97,7 @@ protected function prepareEntityProperties(): void
Omitted::class,
Included::class,
Validates::class,
Aggregate::class,
];

foreach ($properties as $property)
Expand All @@ -108,6 +112,7 @@ protected function prepareEntityProperties(): void
'timestampFields',
'omittedFields',
'includedFields',
'aggregatedFields',
];

$propertyIsIgnored =
Expand Down Expand Up @@ -194,6 +199,9 @@ protected function prepareEntityProperties(): void
case Omitted::class:
$includedFields[] = $property->getName();
break;
case Aggregate::class:
$aggregatedFields[$property->getName()] = $attr->aggregate;
break;
}
}
}
Expand All @@ -203,6 +211,7 @@ protected function prepareEntityProperties(): void
$this->timestampFields = $timestampFields;
$this->omittedFields = $omittedFields;
$this->includedFields = $includedFields;
$this->aggregatedFields = $aggregatedFields;
}

/**
Expand All @@ -227,9 +236,12 @@ public function assign(array $record, bool $notifyChange = true)

// Use defined setters first, fallback to attempt to set.
// @NOTE Read-only properties will trigger errors
if (method_exists($this, $setter)) {
if (method_exists($this, $setter))
{
$this->$setter($value);
} else {
}
else
{
$this->$field = $value;
}
}
Expand Down Expand Up @@ -259,7 +271,7 @@ public function getPrimaryKey(): ?int

public function isSoftDeleteable(): bool
{
return method_exists($this, 'hasDeletedAt');
return property_exists($this, 'deletedAt');
}

/**
Expand Down Expand Up @@ -343,13 +355,24 @@ public function validate(): array
);
}

/**
* Returns array of aggregated select fields.
*/
public function getAggregateFields(): array
{
return $this->aggregatedFields;
}

/**
* Returns array of exposeable property names.
*/
protected function getExposeablePropertyNames(): array
{
$exposeableFields = array_unique(
array_keys($this->validationRules, $this->includedFields)
array_keys([
...$this->validationRules,
...$this->includedFields,
])
);

return [
Expand All @@ -366,6 +389,7 @@ protected function getRecord(
bool $filterPrivate = false,
bool $filterOmitted = true,
bool $filterTimestamps = false,
bool $filterAggregates = true
): array
{
$ref = new ReflectionClass(static::class);
Expand All @@ -375,6 +399,14 @@ protected function getRecord(
$properties = $ref->getProperties();
$validProperties = $this->getExposeablePropertyNames();

if ($filterAggregates === false)
{
$validProperties = [
...$validProperties,
...array_keys($this->getAggregateFields())
];
}

foreach ($properties as $property)
{
if (in_array($property->getName(), $validProperties))
Expand Down Expand Up @@ -416,10 +448,16 @@ protected function getRecordFields(
bool $filterPrivate = false,
bool $filterOmitted = true,
bool $filterTimestamps = false,
bool $filterAggregates = true
): array
{
return array_keys(
$this->getRecord($filterPrivate, $filterOmitted, $filterTimestamps),
$this->getRecord(
filterPrivate:$filterPrivate,
filterOmitted: $filterOmitted,
filterTimestamps: $filterTimestamps,
filterAggregates: $filterAggregates
),
);
}

Expand Down
52 changes: 30 additions & 22 deletions vendor/Haku/Database/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public static function from(array $record = []): static
}

public static function findAll(
array $whereClauses = [],
array $where = [],
array $orderBy = [],
int $limit = Model::DefaultFetchLimit,
int $offset = 0,
bool $includeDeleted = false,
Expand All @@ -61,22 +62,24 @@ public static function findAll(
$result = [];

if ($self->isSoftDeleteable() && !$includeDeleted) {
$whereClauses[] = Where::null('deletedAt');
$where[] = Where::null('deletedAt');
}

[$query, $parameters] = Find::all(
$self->tableName,
$self->getRecordFields(),
$whereClauses,
$limit,
$offset
tableName: $self->tableName,
tableFields: $self->getRecordFields(),
aggregateFields: $self->getAggregateFields(),
where: $where,
orderBy: $orderBy,
limit: $limit,
offset: $offset
);

return haku('db')->fetchAll($query, $parameters);
}

public static function findOne(
array $whereClauses = [],
array $where = [],
bool $includeDeleted = false,
): ?static
{
Expand All @@ -86,14 +89,14 @@ public static function findOne(

if ($self->isSoftDeleteable() && !$includeDeleted)
{
$whereClauses[] = Where::null('deletedAt');
$where[] = Where::null('deletedAt');
}

[$query, $parameters] = Find::one(
$self->tableName,
$self->getRecordFields(),
$whereClauses,
1,
tableName: $self->tableName,
tableFields: $self->getRecordFields(),
aggregateFields: $self->getAggregateFields(),
where: $where,
);

$result = haku('db')->fetch($query, $parameters);
Expand All @@ -116,7 +119,8 @@ public static function find(int | string $primaryKey, bool $includeDeleted = fal
}

public static function paginate(
array $whereClauses = [],
array $where = [],
array $orderBy = [],
int $page = 1,
int $limit = Model::DefaultFetchLimit,
bool $includeDeleted = false,
Expand All @@ -127,10 +131,10 @@ public static function paginate(
$result = [];

if ($self->isSoftDeleteable() && !$includeDeleted) {
$whereClauses[] = Where::null('deletedAt');
$where[] = Where::null('deletedAt');
}

[$countQuery, $countParams] = Find::count($self->tableName, $self->primaryKeyName, $whereClauses);
[$countQuery, $countParams] = Find::count($self->tableName, $self->primaryKeyName, $where);

$numRecords = haku('db')->fetchColumn($countQuery, $countParams);
$pageCount = ceil($numRecords / $limit);
Expand All @@ -156,14 +160,17 @@ public static function paginate(
}

[$query, $parameters] = Find::all(
$self->tableName,
$self->getRecordFields(),
$whereClauses,
$limit,
$offset,
tableName: $self->tableName,
tableFields: $self->getRecordFields(),
aggregateFields: $self->getAggregateFields(),
where: $where,
orderBy: $orderBy,
limit: $limit,
offset: $offset,
);

$records = haku('db')->fetchAll($query, $parameters);
$records = array_map(fn($record) => static::from($record)->json(), $records);

return [
'pagination' => [
Expand Down Expand Up @@ -227,7 +234,7 @@ public function jsonSerialize(): mixed
return null;
}

return $this->getRecord(filterPrivate: true);
return $this->getRecord(filterPrivate: true, filterAggregates: false);
}

/**
Expand Down Expand Up @@ -276,6 +283,7 @@ public function save(bool $ignoreValidationStatus = false): ?static
}

$record = $this->getRecord(filterTimestamps: true);

$record = $this->marshalRecord($record);

$updatedModel = null;
Expand Down
Loading

0 comments on commit 0e8eb7f

Please sign in to comment.