Commit 8e1af5cc authored by Laurent Lecluse's avatar Laurent Lecluse
Browse files

Nouveau merge avec gestion complète des opérations liées aux historiques (undelete & soft-delete)

parent 4f4d4894
Loading
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -103,9 +103,9 @@ class DataManager
            $config['key'] ?? $idCol,
            $config['options'] ?? []
        );
        if ($result['insert'] + $result['update'] + $result['delete'] > 0) {
        if ($result['insert'] + $result['update'] + $result['delete'] + ($result['undelete'] ?? 0) + ($result['soft-delete'] ?? 0) > 0) {
            $msg = str_pad($table, 31, ' ');
            $msg .= 'Insert: ' . $result['insert'] . ', Update: ' . $result['update'] . ', Delete: ' . $result['delete'];
            $msg .= 'Insert: ' . ($result['insert'] + ($result['undelete'] ?? 0)) . ', Update: ' . $result['update'] . ', Delete: ' . ($result['delete'] + ($result['soft-delete'] ?? 0));
            $this->getBdd()->logMsg($msg);
        }
    }
+145 −70
Original line number Diff line number Diff line
@@ -125,7 +125,7 @@ class Table
            'case'          => -1,
            'types'         => $this->makeTypesOptions(),
            'key'           => null,
            'orderBy'       => '',
            'order-by'      => '',
        ];
        $options        = array_merge($defaultOptions, $options);

@@ -146,8 +146,8 @@ class Table
        $params = [];
        $sql    .= $this->makeWhere($where, $options, $params);

        if ($options['orderBy']) {
            $sql .= ' ORDER BY ' . $options['orderBy'];
        if ($options['order-by']) {
            $sql .= ' ORDER BY ' . $options['order-by'];
        }
        $select = $this->getBdd()->select($sql, $params, $options);

@@ -418,46 +418,104 @@ class Table



    private function mergeDiff(array &$o, array &$n, array &$options): array
    {
        $diff = [];
        foreach ($o as $c => $ov) {
            if ($options['has-historique']) {
                if ($c == $options['histo-creation-column']
                    || $c == $options['histo-createur-id-column']
                    || $c == $options['histo-modification-column']
                    || $c == $options['histo-modificateur-id-column']
                    || $c == $options['histo-destruction-column']
                    || $c == $options['histo-destructeur-id-column']
                ) {
                    continue; // on ignore les colonnes relatives aux historiques
                }
            }

            $newc = $n[$c] ?? null;
            $oldc = $o[$c] ?? null;
            if ($newc instanceof \DateTime) $newc = $newc->format('Y-m-d H:i:s');
            if ($oldc instanceof \DateTime) $oldc = $oldc->format('Y-m-d H:i:s');
            if ($newc != $oldc && array_key_exists($c, $n) && $c != $options['id-column']) {
                $ok = empty($options['update-cols']); // OK par défaut si une liste n'a pas été établie manuellement

                if (in_array($c, $options['update-cols'])) $ok = true;
                if (in_array($c, $options['update-ignore-cols'])) $ok = false;
                if (in_array($c, $options['update-only-null']) && $oldc !== null) $ok = false;

                if ($ok) {
                    if ((is_float($newc) || is_double($newc)) && (is_float($oldc) || is_double($oldc))) {
                        // nécessaire à cause des pb de précision des float/doubles en PHP :
                        // dans ce cas, on considère quand même que les 2 chiffres sont identiques => diff négligeable
                        $ok = (abs($newc - $oldc) > 0.0000000001);
                    }
                }
                if ($ok) {
                    $diff[$c] = $n[$c];
                }
            }
        }

        return $diff;
    }



    public function merge(array $data, $key, array $options = []): array
    {
        $result = ['insert' => 0, 'update' => 0, 'delete' => 0, 'soft-delete' => 0];
        $ddl = $this->getDdl();
        $bdd = $this->getBdd();

        /* Initialisation */
        /* Initialisation des options */
        $hasHistorique  = $this->hasHistorique();
        $defaultOptions = [
            'custom-select'      => null,
            'where'              => null,
            'key'                => $key,
            'delete'             => true,
            'soft-delete'        => false,
            'soft-delete'        => true,
            'undelete'           => true,
            'insert'             => true,
            'update'             => true,
            'return-insert-data' => false,
            'update-cols'        => [],
            'update-ignore-cols' => [],
            'update-only-null'   => [],
            'id-column'          => $bdd->getOption(Bdd::OPTION_ID_COLUMN),
            'has-historique'     => $hasHistorique,
            'histo-user-id'      => null,
            'histo-date'         => new \DateTime(),
        ];
        if ($hasHistorique) {
            $defaultOptions['histo-user-id']                = $bdd->getHistoUserId();
            $defaultOptions['histo-creation-column']        = $bdd->getOption(Bdd::OPTION_HISTO_CREATION_COLUMN);
            $defaultOptions['histo-createur-id-column']     = $bdd->getOption(Bdd::OPTION_HISTO_CREATEUR_ID_COLUMN);
            $defaultOptions['histo-modification-column']    = $bdd->getOption(Bdd::OPTION_HISTO_MODIFICATION_COLUMN);
            $defaultOptions['histo-modificateur-id-column'] = $bdd->getOption(Bdd::OPTION_HISTO_MODIFICATEUR_ID_COLUMN);
            $defaultOptions['histo-destruction-column']     = $bdd->getOption(Bdd::OPTION_HISTO_DESTRUCTION_COLUMN);
            $defaultOptions['histo-destructeur-id-column']  = $bdd->getOption(Bdd::OPTION_HISTO_DESTRUCTEUR_ID_COLUMN);

            $result = ['insert' => 0, 'update' => 0, 'delete' => 0, 'soft-delete' => 0, 'undelete' => 0];
        } else {
            $result = ['insert' => 0, 'update' => 0, 'delete' => 0];
        }
        $options = array_merge($defaultOptions, $options);

        if ($options['return-insert-data']) {
            $result['insert-data'] = [];
        }

        $ddl = $this->getDdl();
        $bdd = $this->getBdd();

        $histoUserId   = $bdd->getHistoUserId();
        $hasHistorique = $this->hasHistorique();

        $idCol = $this->getBdd()->getOption(Bdd::OPTION_ID_COLUMN);

        if (empty($options['where']) && $hasHistorique) {
            $histoDestructionCol = $this->getBdd()->getOption(Bdd::OPTION_HISTO_DESTRUCTION_COLUMN);
            $options['where']    = $histoDestructionCol . ' IS NULL';
        if ($hasHistorique && (null === $options['histo-user-id'])) {
            throw new \Exception('L\'id de l\'utilisateur pour la gestion des historiques doit être fourni en configuration ou bien en option');
        }


        /* Mise en forme des nouvelles données */
        $new = [];
        foreach ($data as $d) {
        foreach ($data as $i => $d) {
            foreach ($d as $c => $v) {
                if (isset($ddl['columns'][$c])) {
                    if (isset($options['columns'][$c]['transformer'])) {
@@ -482,73 +540,90 @@ class Table
        $selOptions['fetch'] = Bdd::FETCH_EACH;
        $stmt                = $this->select($options['where'], $selOptions);
        while ($o = $stmt->next()) {
            // récupération de k et n
            // récupération de k et n & on détermine quelle action effectuer
            $k      = $this->makeKey($o, $key);
            $action = null; // rien à faire par défaut
            if (array_key_exists($k, $new)) {
                $n = $new[$k];
                unset($new[$k]);
            } else {
                $n = null;
                unset($new[$k]); // on retire de suite du tableau
                $diff = $this->mergeDiff($o, $n, $options);
                if (!empty($diff)) {
                    if ($options['update']) {
                        $action = 'update';
                    }

            if (empty($n) && $options['soft-delete'] && $hasHistorique && $histoUserId !== null) { // SOFT DELETE
                $histoDestructionCol   = $this->getBdd()->getOption(Bdd::OPTION_HISTO_DESTRUCTION_COLUMN);
                $histoDestructeurIdCol = $this->getBdd()->getOption(Bdd::OPTION_HISTO_DESTRUCTEUR_ID_COLUMN);

                //On ne delete pas mais on historise
                $n                         = $o;
                $n[$histoDestructionCol]   = new \DateTime();
                $n[$histoDestructeurIdCol] = $histoUserId;
                $this->update($n, $this->makeKeyArray($o, $k));
                $result['soft-delete']++;
            } elseif (empty($n) && !$options['soft-delete']) { // DELETE
                if ($options['delete']) {
                    $this->delete($this->makeKeyArray($o, $key));
                    $result['delete']++;
                } elseif ($hasHistorique) {
                    if ($options['undelete'] && array_key_exists($options['histo-destruction-column'], $o) && $o[$options['histo-destruction-column']]) {
                        $action = 'undelete';
                    }
            } elseif ($options['update']) { // UPDATE si différent!!
                $toUpdate = [];
                foreach ($o as $c => $ov) {
                    $newc = $n[$c] ?? null;
                    $oldc = $o[$c] ?? null;
                    if ($newc instanceof \DateTime) $newc = $newc->format('Y-m-d H:i:s');
                    if ($oldc instanceof \DateTime) $oldc = $oldc->format('Y-m-d H:i:s');
                    if ($newc != $oldc && array_key_exists($c, $n) && $c != $idCol) {
                        $ok = empty($options['update-cols']); // OK par défaut si une liste n'a pas été établie manuellement

                        if (in_array($c, $options['update-cols'])) $ok = true;
                        if (in_array($c, $options['update-ignore-cols'])) $ok = false;
                        if (in_array($c, $options['update-only-null']) && $oldc !== null) $ok = false;

                        if ($ok) {
                            if ((is_float($newc) || is_double($newc)) && (is_float($oldc) || is_double($oldc))) {
                                if (abs($newc - $oldc) > 0.0000000001) {
                                    // nécessaire à cause des pb de précision des float/doubles en PHP :
                                    // dans ce cas, on considère quand même que les 2 chiffres sont identiques
                                    $toUpdate[$c] = $n[$c];
                }
            } else {
                                $toUpdate[$c] = $n[$c];
                if ($options['has-historique']) {
                    if (null === $o[$options['histo-destruction-column']]) {
                        // opération de soft-delete
                        if ($options['soft-delete']) {
                            $action = 'soft-delete';
                        }
                    }
                } else {
                    // opération de delete
                    if ($options['delete']) {
                        $action = 'delete';
                    }
                }
            }
                if (!empty($toUpdate)) {

            // on effectue l'action
            if ($action) {
                switch ($action) {
                    case 'delete':
                        $this->delete($this->makeKeyArray($o, $key));

                        break;
                    case 'soft-delete':
                        $toUpdate = [
                            $options['histo-destruction-column']    => $options['histo-date'],
                            $options['histo-destructeur-id-column'] => $options['histo-user-id'],
                        ];
                        $this->update($toUpdate, $this->makeKeyArray($o, $key), ['ddl' => $ddl]);
                    $result['update']++;

                        break;
                    case 'undelete':
                        $toUpdate = [
                            $options['histo-destruction-column']    => null,
                            $options['histo-destructeur-id-column'] => null,
                        ];
                        $this->update($toUpdate, $this->makeKeyArray($o, $key), ['ddl' => $ddl]);

                        break;
                    case 'update':
                        $toUpdate = $diff;
                        if ($hasHistorique) {
                            $toUpdate[$options['histo-modification-column']]    = $options['histo-date'];
                            $toUpdate[$options['histo-modificateur-id-column']] = $options['histo-user-id'];
                        }
                        $this->update($toUpdate, $this->makeKeyArray($o, $key), ['ddl' => $ddl]);

                        break;
                }
                $result[$action]++;
            }
        }

        /* Pour finir, insertion de tous les nouveaux éléments */
        foreach ($new as $k => $n) {
            if ($options['insert']) {
                if ($hasHistorique) {
                    $n[$options['histo-creation-column']]        = $options['histo-date'];
                    $n[$options['histo-createur-id-column']]     = $options['histo-user-id'];
                    $n[$options['histo-modification-column']]    = $options['histo-date'];
                    $n[$options['histo-modificateur-id-column']] = $options['histo-user-id'];
                }
                $this->insert($n);
                $result['insert']++;
                if ($options['return-insert-data']) {
                    $insertedData = [];
                    if (isset($n[$idCol])) {
                        $insertedData[$idCol] = $n[$idCol];
                    if (isset($n[$options['id-column']])) {
                        $insertedData[$options['id-column']] = $n[$options['id-column']];
                    }
                    foreach ($key as $null => $kc) {
                        $insertedData[$kc] = $n[$kc];