nur-sery/nur_src/b/values/Breaker.php

150 lines
4.0 KiB
PHP
Raw Normal View History

2023-12-03 22:10:18 +04:00
<?php
namespace nur\b\values;
use nur\A;
/**
* Class Breaker: aide pour construire des listes multi-niveaux.
*
* Dans une liste, certaines valeurs sont des clés. L'idée est de pouvoir
* visuellement représenter les valeurs associées à ces clés. La méthode
* should_break_on() indique quand la clé change, ce qui indique qu'il faut
* passer à la section suivante.
*
* Par exemple, avec la liste suivante:
* clé valeur
* A 1.1
* A 1.2
* A 1.3
* B 2.1
* B 2.2
* C 3.1
* should_break_on($row["clé"]) retourne true pour les valeurs 1.1, 2.1 et 3.1
* et donc on peut facilement construire cette liste pour affichage:
* clé valeur
* A 1.1
* 1.2
* 1.3
* B 2.1
* 2.2
* C 3.1
*/
class Breaker {
function __construct() {
$this->reset();
}
private $values;
private $changedKeys;
private function computeShouldBreak(array $values): bool {
$should_break = false;
foreach ($values as $key => $value) {
if (!$should_break) {
if ($value === A::get($this->values, $key)) {
$this->changedKeys[$key] = false;
continue;
} else {
$should_break = true;
}
}
$this->changedKeys[$key] = $should_break;
}
$this->values = $values;
return $should_break;
}
function reset() {
$this->values = [];
$this->changedKeys = [];
}
/**
* Retourner true si $values est différent par rapport au précédent appel
*
* $value peut contenir des instances de Breaker à condition que leur
* méthodes {@link shouldBreakOn()} aient déjà été appelées.
*/
function shouldBreakOn(...$values): bool {
# résoudre les éventuelles instances de Breaker
$actual_values = [];
foreach ($values as $value) {
if ($value instanceof Breaker) {
$actual_values = array_merge($actual_values, $value->values);
} else {
$actual_values[] = $value;
}
}
return $this->computeShouldBreak($actual_values);
}
/**
* Retourner true si les valeurs de $array correspondant aux clés $keys sont
* différentes par rapport au précédent appel
*
* $keys peut contenir des instances de Breaker à condition que leur méthodes
* {@link shouldBreakOnKeys()} aient déjà été appelées.
*/
function shouldBreakOnKeys($array, ...$keys): bool {
# résoudre les éventuelles instances de Breaker
$values = [];
foreach ($keys as $key) {
if ($key instanceof Breaker) {
$values[] = $key;
} else {
$values[$key] = A::get($array, $key);
}
}
$actual_values = [];
foreach ($values as $key => $value) {
if ($value instanceof Breaker) {
$actual_values = array_merge($actual_values, $value->values);
} else {
$actual_values[$key] = $value;
}
}
return $this->computeShouldBreak($actual_values);
}
/**
* Tester si la clé spécifiée a été changée.
*
* Il faut d'abord avoir appelé l'une des méthodes {@link shouldBreakOn()}
* ou {@link shouldBreakOnKeys()}
*/
function isChanged($key=null): bool {
if ($key !== null) return $this->changedKeys[$key];
foreach ($this->changedKeys as $changed) {
if ($changed) return true;
}
return false;
}
/**
* Appeler {@link shouldBreakOn()}(...$values) puis retourner un tableau avec
* les valeurs non modifiées à false.
*/
function getChanged(...$values): array {
$this->shouldBreakOn(...$values);
$values = [];
foreach ($this->values as $key => $value) {
$values[$key] = $this->changedKeys[$key]? $value: false;
}
return $values;
}
/**
* Appeler {@link shouldBreakOnKeys()}($array, ...$keys) puis retourner un
* tableau avec les valeurs non modifiées à false.
*/
function getChangedKeys($array, ...$keys): array {
$this->shouldBreakOnKeys($array, ...$keys);
$values = [];
foreach ($this->values as $key => $value) {
$values[$key] = $this->changedKeys[$key]? $value: false;
}
return $values;
}
}