146 lines
4.2 KiB
PHP
146 lines
4.2 KiB
PHP
|
<?php
|
||
|
namespace nur\mapper\base\capacitor;
|
||
|
|
||
|
use ArrayAccess;
|
||
|
use nur\A;
|
||
|
use nur\data\types\Metadata;
|
||
|
|
||
|
/**
|
||
|
* Class Capacitor: classe outil qui permet d'accumuler des données pour les
|
||
|
* fournir en une seule fois au moment voulu
|
||
|
*/
|
||
|
class Capacitor implements ICapacitor, ArrayAccess {
|
||
|
use TCapacitor;
|
||
|
|
||
|
/** @var Metadata */
|
||
|
private static $key_md;
|
||
|
|
||
|
private static function key_md(): Metadata {
|
||
|
if (self::$key_md === null) {
|
||
|
self::$key_md = new Metadata(self::KEY_SCHEMA);
|
||
|
}
|
||
|
return self::$key_md;
|
||
|
}
|
||
|
|
||
|
/** @var Metadata */
|
||
|
private static $sort_md;
|
||
|
|
||
|
private static function sort_md(): Metadata {
|
||
|
if (self::$sort_md === null) {
|
||
|
self::$sort_md = new Metadata(self::SORT_SCHEMA);
|
||
|
}
|
||
|
return self::$sort_md;
|
||
|
}
|
||
|
|
||
|
/** @var array */
|
||
|
protected $kinfos;
|
||
|
|
||
|
/**
|
||
|
* spécifier les clés à traquer lors du chargement d'une donnée.
|
||
|
*
|
||
|
* si une clé est marquée comme primaire, alors les doublons éventuels sont
|
||
|
* supprimés au fur et à mesure du chargement.
|
||
|
*/
|
||
|
function setKeys(array $keys, ?string $channel=null): void {
|
||
|
self::key_md()->eachEnsureSchema($keys);
|
||
|
$namedkeys = [];
|
||
|
$pkeys = null;
|
||
|
foreach ($keys as $key) {
|
||
|
$namedkeys[$key["name"]] = $key;
|
||
|
if ($key["primary"]) $pkeys[] = $key;
|
||
|
}
|
||
|
$this->kinfos[$channel] = ["keys" => $namedkeys, "pkeys" => $pkeys];
|
||
|
}
|
||
|
|
||
|
private static function get_kvalues(array $keys, $item): array {
|
||
|
$item = A::with($item);
|
||
|
$kvalues = [];
|
||
|
foreach ($keys as $kname => $key) {
|
||
|
$kvalues[$kname] = A::get($item, $key["name"]);
|
||
|
}
|
||
|
return $kvalues;
|
||
|
}
|
||
|
|
||
|
private static function compute_itemkey(array $pkvalues): string {
|
||
|
return implode("-", $pkvalues);
|
||
|
}
|
||
|
|
||
|
private static function get_itemkey(?array $kinfos, $item): ?string {
|
||
|
if ($kinfos === null) return null;
|
||
|
$pkeys = $kinfos["pkeys"];
|
||
|
if ($pkeys === null) return null;
|
||
|
$pkvalues = self::get_kvalues($pkeys, $item);
|
||
|
return self::compute_itemkey($pkvalues);
|
||
|
}
|
||
|
|
||
|
/** @var array */
|
||
|
protected $data;
|
||
|
|
||
|
/**
|
||
|
* trier les données selon les clés spécifiées. NB: cette implémentation
|
||
|
* autorise qu'on utilise des clés qui n'ont pas été déclarées avec
|
||
|
* {@link setKeys()}
|
||
|
*
|
||
|
* @see ICapacitor::sort()
|
||
|
*/
|
||
|
function sort(?array $keys=null, ?string $channel=null): void {
|
||
|
$kinfos = A::get($this->kinfos, $channel);
|
||
|
$asort = $kinfos !== null && $kinfos["pkeys"] !== null;
|
||
|
$defaultKeys = $kinfos !== null? $kinfos["keys"]: null;
|
||
|
if ($keys !== null) {
|
||
|
self::sort_md()->eachEnsureSchema($keys);
|
||
|
} else {
|
||
|
$keys = $defaultKeys;
|
||
|
if ($keys === null) return;
|
||
|
}
|
||
|
foreach ($keys as $kname => &$key) {
|
||
|
if ($key["reverse"] === null && $defaultKeys !== null && array_key_exists($kname, $defaultKeys)) {
|
||
|
$key["reverse"] = $defaultKeys[$kname]["reverse"];
|
||
|
}
|
||
|
if ($key["reverse"] === null) $key["reverse"] = false;
|
||
|
}; unset($key);
|
||
|
$sortfunc = function ($a, $b) use ($keys) {
|
||
|
$akvs = self::get_kvalues($keys, $a);
|
||
|
$bkvs = self::get_kvalues($keys, $b);
|
||
|
foreach ($keys as $kname => $key) {
|
||
|
$akv = $akvs[$kname];
|
||
|
$bkv = $bkvs[$kname];
|
||
|
if ($akv == $bkv) continue;
|
||
|
if ($akv < $bkv) {
|
||
|
return $key["reverse"] ? 1 : -1;
|
||
|
} else {
|
||
|
return $key["reverse"] ? -1 : 1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
};
|
||
|
if ($asort) uasort($this->data[$channel], $sortfunc);
|
||
|
else usort($this->data[$channel], $sortfunc);
|
||
|
}
|
||
|
|
||
|
function charge($item, ?string $channel=null, $pkvalues=null): void {
|
||
|
$kinfos = A::get($this->kinfos, $channel);
|
||
|
$itemkey = self::get_itemkey($kinfos, $item);
|
||
|
if ($itemkey === null && $item === null) {
|
||
|
if ($pkvalues === null) return;
|
||
|
$itemkey = self::compute_itemkey($pkvalues);
|
||
|
}
|
||
|
A::set($this->data[$channel], $itemkey, $item);
|
||
|
}
|
||
|
|
||
|
function getItem($pkvalues, ?string $channel=null, $default=null) {
|
||
|
if (is_array($pkvalues)) {
|
||
|
$itemkey = self::compute_itemkey($pkvalues);
|
||
|
} else {
|
||
|
$itemkey = $pkvalues;
|
||
|
}
|
||
|
return A::_pget($this->data, [$channel, $itemkey], $default);
|
||
|
}
|
||
|
|
||
|
function discharge(?string $channel=null, bool $remove=true): iterable {
|
||
|
$items = A::get($this->data, $channel, []);
|
||
|
if ($remove) A::del($this->data, $channel);
|
||
|
return $items;
|
||
|
}
|
||
|
}
|