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;
 | |
|   }
 | |
| }
 |