Skip to content
Snippets Groups Projects
Commit 3d942fa6 authored by pfleu's avatar pfleu
Browse files

API : Entry : Ajotu Endpoint editBlock pour modifier uniquement un bloc de...

API : Entry : Ajotu Endpoint editBlock pour modifier uniquement un bloc de texte d'une entrée, identifiée par l'id de bloc et la clé de la valeur dans le bloc
parent 98213725
No related branches found
No related tags found
No related merge requests found
# Structure des données Json des entrées
- Les données récupérées depuis le dictionnaire sont stockées dans un tableau d'objet json, dans la propriété `attributes` de l'entité Entry
- La structure des données est décrite dans `Structure_minimale.json` et accessible via l'endpoint `/api/schema/entry`
## Accès aux données
- Chaque bloc de données possède un identifiant unique `id`, qu'on nomme `balexId` dans le code.
- On conserve cette donnée notamment pour pouvoir éventuellement réinjecter un jour les modifications dans le dictionnaire
- Cette donnée n'est pas utile dans l'application, on utilise plutôt le chemin calculé comme suit :
- Lorsqu'on parse le json dans Balex pour l'afficher, on crée des index de la forme:
`[Items][0][Sense][Definitions][0][Subdefinitions][0]`
- Ces index permettent de naviguer dans la structure des données pour modifier les blocs, en utilisant la classe `PropertyAccess`
- Ils sont passés en paramètre des routes de modification des données, pour indiquer quel bloc de données est modifié
## Modification des données depuis l'API
- Depuis l'API, on a pas affiché le contenu de l'entrée, on a pas calculé le chemin d'accès, on a juste `balexId`
- On parcourt donc le tableau de l'entrée, on identifie le bloc de données correspondant à `balexId`, et on calcule son chemin d'accès
- Cela permet d'utiliser les mêmes méthodes de modification des données que dans l'application
- Mais à la différence des formulaires de Balex, on ne peut pas modifier plusieurs données du bloc en même temps, et on spécifie la clé de la donnée à modifier, qui est rajoutée au chemin calculé
- Par exemple, pour modifier la valeur d'une sous-définition dont l'id balex donne un chemin calculé [Items][0][Sense][Definitions][0][Subdefinitions][0]
on ajoute la clé `Def`, pour obtenir [Items][0][Sense][Definitions][0][Subdefinitions][0][Def]
\ No newline at end of file
......@@ -16,6 +16,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
......@@ -298,6 +300,9 @@ class ApiEntryController extends AppBaseController
}
/**
* Remplacement complet du json de l'entrée par un autre json
* Attention, la validation de la structure du json n'est pas exhaustive, une sous-définition invalide ne sera pas détectée
*
* @Route("/edit/{id}", name="api_edit_entry", methods={"POST"})
*
* @OA\Response(
......@@ -343,6 +348,7 @@ class ApiEntryController extends AppBaseController
}
$attributes = json_decode(json_encode($data['attributes']));
$validator = new Validator();
// // Attention, validation pas exhaustive, une sous-définition invalide ne sera pas détectée
$validator->validate($attributes, (object) ['$ref' => __DIR__ . "/../JsonSchema/entrySchema.json"]);
if (!$validator->isValid()) {
$message = "JSON does not validate. Violations:\n";
......@@ -363,6 +369,100 @@ class ApiEntryController extends AppBaseController
return new JsonResponse($updatedEntryData, 200, [], true);
}
/**
* Remplacement d'une valeur de l'entrée, identifiée par l'id de bloc et la clé de la valeur dans le bloc
*
* @Route("/{id}/edit-block/{blockId}/{blockKey}", name="api_edit_block_entry", methods={"PUT"})
*
* @OA\Response(
* response=200,
* description="Success",
* @OA\JsonContent(
* ref=@Model(type=Entry::class, groups={"entry:read"})
* )
* )
* @OA\Response(response=401, description="error", @OA\JsonContent(type="string"))
* @OA\Response(response=403, description="error", @OA\JsonContent(type="string"))
* @OA\Response(response=500, description="error", @OA\JsonContent(type="string"))
*
* @OA\Parameter(
* name="id",
* in="path",
* description="id of the entry to edit",
* @OA\Schema(type="string")
* )
* @OA\Parameter(
* name="blockId",
* in="path",
* description="id of the block entry to edit",
* @OA\Schema(type="string")
* )
* @OA\Parameter(
* name="blockKey",
* in="path",
* description="key to edit inside the block, ie. « Def »",
* @OA\Schema(type="string")
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"block_content"},
* @OA\Property(property="block_content", type="string", example="nouveau contenu du bloc"),
* @OA\Property(property="comment", type="string", example="commentaire optionnel ajouté à la suite des commentaires existants"),
* )
* )
* @OA\Tag(name="Entries")
* @Security(name="OAuth2")
* @IsGranted("ENTRY_EDIT", subject="entry")
*/
public function editBlockEntry(Request $request, SerializerInterface $serializer, Entry $entry = null, $blockId, $blockKey)
{
if ($entry === null) {
return $this->createJsonResponse(401, ['error' => sprintf("Pas d'entrée trouvée pour cette Id")]);
}
$data = json_decode($request->getContent(), true);
if (!$data) {
return $this->createJsonResponse(401, ['error' => sprintf("Json non valide")]);
}
if ($missingFields = $this->getMissingFields($data, ['block_content'])) {
return $this->createJsonResponse(401, ['error' => sprintf("Veuillez fournir une valeur pour: %s", implode(', ', $missingFields))]);
}
$blockContent = json_decode(json_encode($data['block_content']));
$attributes = $entry->getAttributes();
$propertyAccessor = PropertyAccess::createPropertyAccessor();
// On calcule le chemin de l'attribut à modifier (chemin du bloc + clé du bloc)
$blockPath = WiktionaryManager::findBlockPathByBalexId($attributes, $blockId);
$keyPath = $blockPath . '[' . $blockKey . ']';
// On vérifie qu'on trouve le bloc à modifier et la clé à modifier
try{
$block = $propertyAccessor->getValue($attributes, $blockPath);
} catch (\Exception $e) {
return $this->createJsonResponse(401, ['error' => sprintf("Pas de valeur trouvée pour ce blockId")]);
}
if (!isset($block[$blockKey])) {
return $this->createJsonResponse(401, ['error' => sprintf("Pas de valeur trouvée pour ce blockKey")]);
}
// On vérifie qu'il s'agit d'un champ texte et pas d'un tableau
if (is_array($block[$blockKey])) {
return $this->createJsonResponse(401, ['error' => sprintf("Le bloc à modifier n'est pas un champ texte")]);
}
// On met à jour les attributs de l'entrée avec le bloc modifié
$propertyAccessor->setValue($attributes, $keyPath, $blockContent);
$entry->setAttributes($attributes);
if ($data['comment'] ?? null && $data['comment'] != $entry->getComments()) {
$entry->setCommentsBy($entry->getComments() . "\n" . $data['comment'], $this->getUser());
}
$this->doctrine->getManager()->flush();
$updatedEntryData = $serializer->serialize($entry, 'json', ['groups' => ["entry:read", "headword:read", "lexicon:read"]]);
return new JsonResponse($updatedEntryData, 200, [], true);
}
/**
* @Route("/edit-comment/{id}", name="api_edit_comment_entry", methods={"PUT"})
*
......
......@@ -166,6 +166,8 @@ class WiktionaryManager
}
/**
* Transforme les données du wiktionnaire en un tableau de données structurées
*
* @param $morphologicalLabels // On retourne tous les 'POS' trouvés pour le mot
* @param $wiktionaryData
* @return array
......@@ -282,4 +284,29 @@ class WiktionaryManager
return $label;
}
// On parcourt le tableau de contenu de l'entrée, on identifie le bloc de données correspondant à `balexId`, et on calcule son chemin d'accès
// Par exemple [Items][0][Sense][Definitions][0][Subdefinitions][0]
public static function findBlockPathByBalexId($attributes, $balexId, $path = '')
{
// Check if the current array itself has an "id" key
if (isset($attributes['id']) && $attributes['id'] === $balexId) {
return $path;
}
foreach ($attributes as $key => $value) {
$currentPath = $path . '[' . $key . ']';
// If the value is an array, search inside it
if (is_array($value)) {
$result = self::findBlockPathByBalexId($value, $balexId, $currentPath);
if ($result !== null) {
return $result;
}
}
}
return null;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment