Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest]
php-versions: ['8.1', '8.2']
php-versions: ['8.2', '8.3']
name: Test with PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ jobs:
fail-fast: false
matrix:
php-version:
- '8.1'
- '8.2'
- '8.3'

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.2'
extensions: intl
coverage: none

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
},
"require-dev": {
"blitz-php/coding-standard": "^1.6",
"blitz-php/database": "^1.0.0-rc",
"blitz-php/framework": "^1.0.0-rc",
"blitz-php/database": "^1.3.5-rc",
"blitz-php/framework": "^1.0.13-rc",
"kahlan/kahlan": "^6.1.0",
"phpstan/phpstan": "^2.1.42"
},
Expand Down
6 changes: 6 additions & 0 deletions src/Handlers/ArrayHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
return isset($this->general[$file]) && Arr::has($this->general[$file], $property);
}

return isset($this->contexts[$context][$file]) && Arr::has($this->contexts[$context][$file], $property);

Check failure on line 105 in src/Handlers/ArrayHandler.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Result of && is always false.

Check failure on line 105 in src/Handlers/ArrayHandler.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Offset string on list<mixed>|null in isset() does not exist.
}

/**
Expand All @@ -118,7 +118,7 @@

$value = $context === null
? Arr::get($this->general[$file], $property)
: Arr::get($this->contexts[$context][$file], $property);

Check failure on line 121 in src/Handlers/ArrayHandler.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Offset string does not exist on list<mixed>.

if (Arr::dimensions($value) === 2) {
return array_map(fn ($v) => $this->parseValue(...$v), $value);
Expand All @@ -144,7 +144,7 @@
$type,
]);
} else {
if (empty($this->contexts[$context][$file])) {

Check failure on line 147 in src/Handlers/ArrayHandler.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Offset string on list<mixed>|null in empty() does not exist.
$this->contexts[$context][$file] = [];
}
Arr::set($this->contexts[$context][$file], $property, [
Expand All @@ -160,8 +160,14 @@
protected function forgetStored(string $file, string $property, ?string $context): void
{
if ($context === null) {
if (! isset($this->general[$file])) {
return;
}
Arr::forget($this->general[$file], $property);
} else {
if (! isset($this->contexts[$context][$file])) {

Check failure on line 168 in src/Handlers/ArrayHandler.php

View workflow job for this annotation

GitHub Actions / PHP Static Analysis

Offset string on list<mixed>|null in isset() does not exist.
return;
}
Arr::forget($this->contexts[$context][$file], $property);
}
}
Expand Down
187 changes: 126 additions & 61 deletions src/Handlers/DatabaseHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use BlitzPHP\Database\Builder\BaseBuilder;
use BlitzPHP\Database\Connection\BaseConnection;
use BlitzPHP\Database\Exceptions\DatabaseException;
use BlitzPHP\Utilities\DateTime\Date;
use RuntimeException;

/**
Expand Down Expand Up @@ -89,6 +88,7 @@ public function get(string $file, string $property, ?string $context = null): mi
public function set(string $file, string $property, mixed $value = null, ?string $context = null): void
{
if ($this->deferWrites) {
$this->hydrate($context);
$this->markPending($file, $property, $value, $context);
} else {
$this->persist($file, $property, $value, $context);
Expand Down Expand Up @@ -136,21 +136,21 @@ public function setMany(array $settings, ?string $context = null): void
*/
private function persist(string $file, string $property, mixed $value, ?string $context): void
{
$time = Date::now()->format('Y-m-d H:i:s');
$time = date('Y-m-d H:i:s');
$type = gettype($value);
$prepared = $this->prepareValue($value);

// S'il a été stocké, nous devons le mettre à jour
if ($this->has($file, $property, $context)) {
$result = $this->builder()->where('file', $file)->where('key', $property);
$query = $this->builder()->where('file', $file)->where('key', $property);

if ($context === null) {
$result = $result->whereNull('context');
$query->whereNull('context');
} else {
$result = $result->where('context', $context);
$query->where('context', $context);
}

$result = $result->update([
$result = $query->update([
'value' => $prepared,
'type' => $type,
'context' => $context,
Expand Down Expand Up @@ -325,73 +325,135 @@ public function persistPendingProperties(): void
}

try {
$this->db->beginTransaction();
$this->persistRows($upserts, $deletes);

// Gérer les mises à jour avec insertion : récupérer les enregistrements existants correspondant à nos données en attente
if ($upserts !== []) {
// Construire une requête pour récupérer uniquement les enregistrements dont nous avons besoin
$builder = $this->buildOrWhereConditions($upserts, 'file', 'key', 'context');
$this->pendingProperties = [];
} catch (DatabaseException|RuntimeException $e) {
logger()->error('Échec de la persistance des propriétés en attente : ' . $e->getMessage());

$existing = $builder->clone()->result('array');
$this->pendingProperties = [];
}
}

// Créez une carte des enregistrements existants pour faciliter la recherche
$existingMap = [];
/**
* Prépare les lignes de la base de données en vue de leur persistance.
*
* @param list<array{file: string, property: string, value: mixed}> $settings
*
* @return list<array{file: string, key: string, value: mixed, type: string, context: string|null, created_at: string, updated_at: string}>
*/
private function prepareUpsertRows(array $settings, ?string $context): array
{
$time = date('Y-m-d H:i:s');
$rows = [];

foreach ($existing as $row) {
$key = $this->buildCompositeKey($row['file'], $row['key'], $row['context']);
$existingMap[$key] = $row['id'];
}
foreach ($settings as $setting) {
$rows[] = [
'file' => $setting['file'],
'key' => $setting['property'],
'value' => $this->prepareValue($setting['value']),
'type' => gettype($setting['value']),
'context' => $context,
'created_at' => $time,
'updated_at' => $time,
];
}

// Distinguer les insertions des mises à jour
$inserts = [];
$updates = [];

foreach ($upserts as $row) {
$key = $this->buildCompositeKey($row['file'], $row['key'], $row['context']);

if (isset($existingMap[$key])) {
// L'enregistrement existe - se préparer à la mise à jour
$updates[] = [
'id' => $existingMap[$key],
'value' => $row['value'],
'type' => $row['type'],
'updated_at' => $row['updated_at'],
];
} else {
// Nouvel enregistrement - préparation à l'insertion
$inserts[] = $row;
}
}
return $rows;
}

// Insérer de nouveaux enregistrements par lots
if ($inserts !== []) {
$builder->bulkInsert($inserts);
}
/**
* Prépare les lignes de la base de données en vue de leur suppression définitive.
*
* @param list<array{file: string, property: string}> $settings
*
* @return list<array{file: string, key: string, context: string|null}>
*/
private function prepareDeleteRows(array $settings, ?string $context): array
{
$rows = [];

// Mise à jour groupée des enregistrements existants
if ($updates !== []) {
$builder->bulkUpdate($updates, 'id');
}
}
foreach ($settings as $setting) {
$rows[] = [
'file' => $setting['file'],
'key' => $setting['property'],
'context' => $context,
];
}

return $rows;
}

/**
* Persiste les lignes préparées dans la base de données.
*
* @param list<array{file: string, key: string, value: mixed, type: string, context: string|null, created_at: string, updated_at: string}> $upserts
* @param list<array{file: string, key: string, context: string|null}> $deletes
*/
private function persistRows(array $upserts, array $deletes): void
{
if ($upserts === [] && $deletes === []) {
return;
}

$this->db->beginTransaction();

// Supprimer en bloc toutes les opérations de suppression
if ($deletes !== []) {
$builder = $this->buildOrWhereConditions($deletes, 'file', 'key', 'context');
// Gérer les mises à jour avec insertion : récupérer les enregistrements existants correspondant à nos données en attente
if ($upserts !== []) {
// Construire une requête pour récupérer uniquement les enregistrements dont nous avons besoin
$builder = $this->buildOrWhereConditions($upserts, 'file', 'key', 'context');

$builder->delete();
$existing = $builder->clone()->result('array');

// Créez une carte des enregistrements existants pour faciliter la recherche
$existingMap = [];

foreach ($existing as $row) {
$key = $this->buildCompositeKey($row['file'], $row['key'], $row['context']);
$existingMap[$key] = $row['id'];
}

$this->db->commit();
// Distinguer les insertions des mises à jour
$inserts = [];
$updates = [];

foreach ($upserts as $row) {
$key = $this->buildCompositeKey($row['file'], $row['key'], $row['context']);

if (isset($existingMap[$key])) {
// L'enregistrement existe - se préparer à la mise à jour
$updates[] = [
'id' => $existingMap[$key],
'value' => $row['value'],
'type' => $row['type'],
'updated_at' => $row['updated_at'],
];
} else {
// Nouvel enregistrement - préparation à l'insertion
$inserts[] = $row;
}
}

if ($this->db->transStatus() === false) {
logger()->error("Impossible d'enregistrer les propriétés en attente dans la base de données.");
// Insérer de nouveaux enregistrements par lots
if ($inserts !== []) {
$builder->bulkInsert($inserts);
}

$this->pendingProperties = [];
} catch (DatabaseException $e) {
logger()->error('Échec de la persistance des propriétés en attente : ' . $e->getMessage());
// Mise à jour groupée des enregistrements existants
if ($updates !== []) {
$builder->bulkUpdate($updates, 'id');
}
}

$this->pendingProperties = [];
// Supprimer en bloc toutes les opérations de suppression
if ($deletes !== []) {
$this->buildOrWhereConditions($deletes, 'file', 'key', 'context')->delete();
}

$this->db->commit();

if ($this->db->transStatus() === false) {
logger()->error("Impossible d'enregistrer les propriétés en attente dans la base de données.");
}
}

Expand All @@ -412,9 +474,12 @@ private function buildOrWhereConditions(array $rows, string $fileKey, string $ke

foreach ($rows as $row) {
$builder->orWhere(function ($q) use ($row, $fileKey, $keyKey, $contextKey) {
$q->where($fileKey, $row[$fileKey])
->where($keyKey, $row[$keyKey])
->where($contextKey, $row[$contextKey]);
$q->where([$fileKey => $row[$fileKey], $keyKey => $row[$keyKey]]);
if (null === $row[$contextKey]) {
$q->whereNull($contextKey);
} else {
$q->where($contextKey, $row[$contextKey]);
}
});
}

Expand Down
Loading
Loading