Statements

De Sydonie

Les statements

TODO

  • Documenter les quelques méthodes non documentées ci-dessous (equals, existsRequest, etc)
  • Enrichir les raccourcis du type CURRENT_USER

Introduction

Les statements permettent d'implémenter les relations entre entitées de Sydonies. On les utilise par exemple pour représenter :

  • l'appartenance d'un document à un utilisateur
  • l'appartenance d'un utilisateur un groupe
  • les droits d'un groupe sur un document
  • une relation entre entités ou documents

Un statement est un triplet qui fait intervenir :

  • un sujet
  • une relation
  • un object

Prenons l'exemple suivant : Toto@sydonie.com possède l'article #x32b. Ici, Toto@sydonie.com est le sujet, possède est la relation et l'article x32b est l'objet.

Notons qu'un statement dans un sens engendre directement l'existence d'un second statement pour lequel le sujet devient l'objet et réciproquement. La relation passe alors mise à la forme passive. Toujours avec le même exemple, on obtient le statement suivant : l'article #x32b est possédé par Toto@sydonie.com.

Premiers pas

L'exemple qui suit illustre la création d'un statement entre 2 SydonieDocument : Information et Category. L'Information peut appartenir à zéro ou plusieurs catégories, une Category peut avoir zéro à plusieurs Information.

Notre projet comporte donc 2 SydonieDocument :

.
├── Category
│   ├── actions.xml
│   ├── configs.xml
│   ├── fields.xml
│   ├── labels.xml
│   └── templates
│       ├── edit.tpl.php
│       └── view.tpl.php
└── Information
    ├── actions
    ├── actions.xml
    ├── configs.xml
    ├── fields.xml
    ├── Information.php
    ├── labels.xml
    └── templates
        ├── edit.tpl.php
        └── view.tpl.php

Déclarer le statement

Nous allons commencer par indiquer à Sydonie qu'une relation (Statement). Pour cela, nous allons éditer le fichier MonProjet/config/site/config_statements.php

$statementManager = Manager_Statements::getInstance();
$statementManager->addPredicate('HAS_MANY', 'ONE_OF');

On notera ici qu'aucune référence aux objets n'est nécessaire (et que le statement déclaré ici peut être réutilisé - dans une certaine mesure).

Les InputProcessor_Statement

La création d'une relation 'rééle' entre Information et Category interviens lors de la création du document Information, cette opération est automatique car sydonie assure la création et le traitement des formulaire automatiquement en utilisant les InputProcessor.

Dans le cardre des Statements, la classe InputProcessor_Statement encapsule les mécanismes de récupération des données et la production des formulaires, cependant, elle implique de définir le mode de récupérations des données selectionnables (nos Category).

Si votre relation est simple, vous pouvez vous appuyer sur la classe InputProcessor_StatementGeneric qui fournir tous les outils requis.


Dans votre projet, créez un fichier MonProjet/InputProcessor/StatementInformationCategory/StatementInformationCategory.php :

<?php
class InputProcessor_StatementInformationCategory extends InputProcessor_StatementGeneric {
    public function __construct() {
        // On précise seulement l'objet distant et le nom du statement.
        parent::__construct('SydonieDocument_Category', 'HAS_MANY');
    }
}

Il ne nous reste plus qu'a afficher le fragment de formulaire.

Le formulaire

Dans le template edit du SydonieDocument Information, ajoutez un appel à la méthode statique build

  <?php echo InputProcessor_StatementInformationCategory::build('categories', $obj) ?>

Si vous testez le formulaire, vous devrier obtenir une erreur de type Catchable fatal error: Object of class SydonieDocument_Category could not be converted to string. En effet, PHP ne parvient pas à transformer notre SydonieDocument_Category en chaîne de caractère.

Utiliser __toString()

Première solution, déclarer une méthode __toString() à notre SydonieDocument_Category :

 <?php
 // SydonieDocument/Category/Category.php
 class SydonieDocument_Category extends Abstract_SydonieDocument {
     public function __toString() {
         return $this->getTitle()->getText();
     }
 }

Redéfinir getForeignLabel( $obj )

Deuxième solution, plus élégante, redéfinir dans la classe InputProcessor_StatementInformationCategory la méthode getForeignLabel( $obj) ; dans notre exemple, le premier argument reçu par la méthode est une instance de la SydonieDocument_Category à afficher :

<?php
class InputProcessor_StatementInformationCategory extends InputProcessor_StatementGeneric {
    public function __construct() {
        parent::__construct('SydonieDocument_Category', 'HAS_MANY');
    }
    
    public function getForeignLabel($foreignObject) {
        return $foreignObject->getTitle()->getText() . " Cool :)";
    }
}

Gestion des statements dans Sydonie

A partir de n'importe quel endroit dans Sydonie, il est possible de vérifier l'existence, créer ou supprimer des statements. Pour simplifier la gestion des statements, on peut utiliser le singleton Sydonie_Managers_Statements".

$statementsManager = getManager('Statements');

Ce singletons possède principalement 5 méthodes :

  • add($subject, $predicate, $object)
  • exists($subject, $predicate, $object)
  • exists3($subject, $predicate1, $intermediate, $predicate2 $object)
  • get($subject, $predicate, $object)
  • remove($)

Nous donnerons une explication détaillée de ces méthodes en même temps que nous étudierons la forme des arguments qu'elles acceptent.

Description des méthodes

"add"

Cette méthode ajoute un statement ainsi que sa forme passive dans le système.

Pour ajouter un statement "OWNS" entre "toto@sydonie.net" et l'article #x32b, on peut utiliser le code suivant :

$subject = .. // l'instance de Sydonie_SocialEntities_Account ayant pour login "toto@sydonie.net"
$predicate = 'OWNS'
$object = .. // l'instance de Sydonie_SydonieDocumentTypes_Article ayant pour identifiant "x32b"
$statementsManager->add($subject, $predicate, $object);

"exists"

Cette méthode permet de tester l'existence d'un statement.

Pour vérifier que "toto@sydonie.net" possède l'article #x32b, on peut utiliser le code suivant :

$subject = .. // l'instance de Sydonie_SocialEntities_Account ayant pour login "toto@sydonie.net"
$predicate = 'OWNS'
$object = .. // l'instance de Sydonie_SydonieDocumentTypes_Article ayant pour identifiant "x32b"
$ok = $statementsManager->exists($subject, $predicate, $object);

Ou alors :

$subject = .. // l'instance de Sydonie_SydonieDocumentTypes_Article ayant pour identifiant "x32b"
$predicate = 'IS_OWNED_BY'
$object = .. // l'instance de Sydonie_SocialEntities_Account ayant pour login "toto@sydonie.net"
$ok = $statementsManager->exists($subject, $predicate, $object);

Notons que l'on peut remplacer n'importe quel argument par la valeur "null", pour avoir une requête plus générale.

Pour vérifier que le "toto@sydonie.net" possède quelquechose, on peut utiliser le code suivant :

$subject = .. // l'instance de Sydonie_SocialEntities_Account ayant pour login "toto@sydonie.net"
$predicate = 'OWNS'
$ok = $statementsManager->exists($subject, $predicate, null);

Pour vérifier que "toto@sydonie.net" possède un article (n'importe lequel), on peut utiliser le code suivant :

$subject = .. // l'instance de Sydonie_SocialEntities_Account ayant pour login "toto@sydonie.net"
$predicate = 'OWNS'
$ok = $statementsManager->exists($subject, $predicate, array('Sydonie_SydonieDocumentTypes_Article'));

Ou, (si 'article' est un raccourci de la classe (Sydonie_SydonieDocumentTypes_Article')

$subject = .. // l'instance de Sydonie_SocialEntities_Account ayant pour login "toto@sydonie.net"
$predicate = 'OWNS'
$ok = $statementsManager->exists($subject, $predicate, array('article'));

Afin de simplifier le travail du codeur, certains alias ont été développés.

Par exemple, pour vérifier que l'utilisateur actuellement connecté possède l'article "x32b", on peut utiliser la syntaxe suivante :

$predicate = 'OWNS'
$object = .. // l'instance de Sydonie_SydonieDocumentTypes_Article ayant pour identifiant "x32b"
$ok = $statementsManager->exists(CURRENT_USER, $predicate, $object);

Quatre alias sont définis dans Sydonie : CURRENT_USER : pour faire référence à l'utilisateur en cours ACCOUNT_VISITOR : pour faire référence à l'account "virtuel" d'un utilisateur non-logué GROUP_VISITOR : pour faire référence au groupe d'un utilisateur non-logué GROUP_ADMIN : pour faire référence au groupe des administrateurs


exists3

Ce test vérifie l'existence d'une relation entre éléments d'un triplet.

Grâce à la fonction suivante : "exists3($subject, $predicate1, $intermediate, $predicate2 $object)" Le développeur peut tester qu'il existe à la fois un statement : ($subject, $predicate1, $intermediate) et un statement ($intermediate, $predicate2 $object)

Ce test est très utile pour la gestion des droits. On veut tester que l'utilisateur courant est dans un groupe qui à le droit de lire l'article "#x32b". Le code php correspondant à une telle domande est le suivant :

$object = .. //l'instance de Sydonie_SydonieDocumentTypes_Article ayant pour identifiant "x32b"
$ok = $statementsManager->exists3(CURRENT_USER, 'IS_MEMBER_OF_GROUP', array('group'), 'GROUP_HAS_DOCUMENT', $object);

setObject

Cette méthode permet de modifier l'objet d'un statement existant défini par son sujet $subject et son prédicat $predicate.

Note : attention, si il existe plusieurs statements ayant pour sujet $subjet et pour prédicat $predicate, seul l'objet du premier statement de la forme sera modifié

$article = // l'instance de Sydonie_SydonieDocumentTypes_Article ayant pour identifiant "x32b"
$user = CURRENT_USER
$statementsManager->setObject($article, 'IS_OWNED_BY', $user);

L'exemple ci-dessus permet de modifier l'utilisateur qui possède l'article $article par l'utilisateur courant.

get

Note : dans la plupart des cas, on préférera utiliser la méthode getObjects à get

Cette méthode permet de retourner l'ensemble des statements ayant une certaine forme. Les valeurs retournées sont de type Sydonie_Statement. Cette méthode reprend la syntaxe de "exists".

Exemple : pour afficher tous les groupes dont l'utilisateur courant est membre.

$statements = $statementsManager->get(CURRENT_USER, 'IS_MEMBER_OF_GROUP', array('group'));
$groupStorage = Sydonie_SydonieEntity_Factory::getStorage('Sydonie_SocialEntities_Group');
foreach($statements as $st) {
  $groupId = $st->getObjectId();
  $group = $groupStorage->retrieve($groupId);
  echo "{$group->getGroupName()} \n";
}

Exemple : pour afficher tous les documents qu'un visiteur peut voir.

$statements = $statementsManager->get(GROUP_VISITOR, 'GROUP_HAS_DOCUMENT', null);
foreach($statements as $st) {
  $docId = $st->getObjectId();
  $doc = Registry::getEntity($docId);
  echo "{$doc->getInstanceHeading()} \n";
}

getObjects

Cette méthode retourne, pour un sujet donné $subject et un predicat donné $predicate la liste des objets [$object] tels qu'il existe un statement ($subject, $predicate, $object)

$documents = $statementsManager->getObjects(GROUP_VISITOR, 'GROUP_HAS_DOCUMENT');
foreach($documents as $doc) {
  echo "{$doc->getInstanceHeading()} \n";
}

remove

Cette méthode permet de supprimer à la fois un statement et sa forme passive. Notons bien que cette méthode échoue lorsque plus d'un statement doit être supprimé. Il s'agit d'une sécurité mise en place lors du développement.

Exemple : supprimer l'accés d'un document en lecture pour un visiteur.

$document = .. // l'article #x32b
$statementsManager->remove(GROUP_VISITOR, 'GROUP_HAS_DOCUMENT', $document);

removeFromId

Idem que remove mais prend un identifiant de statement en paramètre.

$statementId = .. // un id retrouvé dun'e façon ou d'une autre...
$statementsManager->removeFromId($statementId);

Ajouter ses propres statements

Pour ajouter ses propres statements dans le site, on utilise le fichier de configuration .../config/site/config_statements.php

Ce fichier a la forme suivante :

<?php
$statementManager = Sydonie_Managers_Statements::getInstance();
 
$statementManager->addPredicate('IS_COPYEDITOR_OF', 'HAS_COPYEDITOR');
$statementManager->addPredicate('IS_CHIEF_EDITOR_OF', 'HAS_CHIEF_EDITOR');
$statementManager->addPredicate('IS_PUBLISHER_OF', 'IS_PUBLISHED_BY');
?>

Notons que l'on définit les 2 formes réciproques.

Notons aussi que (pour l'instant ?) l'on ne spécifie pas les types d'objets que ces statements pourront lier, ni des cardinalités. C'est alors au développeur de vérifier les contraintes d'intégrité des statements de son site.

Interdire la création de certains statements

Sydonie propose un système très simple pour interdire la création de statements ayant certaines formes. La déclaration des statements interdits est effectuée, par convention, dans le fichier de configuration .../config/site/config_statements.php

Ce fichier a la forme suivante :

<?php
$statementManager = Sydonie_Managers_Statements::getInstance();
 
$statementManager->addPredicate('IS_COPYEDITOR_OF', 'HAS_COPYEDITOR');
$statementManager->addPredicate('IS_CHIEF_EDITOR_OF', 'HAS_CHIEF_EDITOR');
...
 
$statementManager->addForbiddenStatement(ACCOUNT_VISITOR, 'OWNS', null);
 
?>

Dans cet exemple, nous avons interdit la création d'un statement de type "OWNS" entre un visiteur et n'importe quel objet du système.

La ligne suivante illustre l'interdiction de poser un statement de type "IS_FRIEND_OF" entre un visiteur et un autre account.

$statementManager->addForbiddenStatement(ACCOUNT_VISITOR, 'IS_FRIEND_OF', array('account'));

Note : Pour des raisons d'implémentation, la présence du prédicat est obligatoire!!!

Stockage des statements

Dans la base de données, les statements sont stockés dans les tables "appli_relation" et "appli_statements". Une vue "appli_view_statemlents" sur ces 2 tables permet de visualiser correctement les données relatives aux Statements :

Le schéma de la table est le suivant :

  • id
  • subjectClass
  • subjectId
  • predicate
  • objectType
  • objectValue
  • confirmationStatus

La colonne "predicate" est une chaîne de caractères (en anglais) qui représente la relation (les espaces entre les mots sont remplacés par des soulignés).

Notons que le sujet et l'objet sont découpés sur deux colonnes qui sont respectivement ("subjectClass", "subjectId") et ("objectType", "objectValue").

Pour reprendre l'exemple précédent, l'enregistrement en base de données contiendra :

  • id = XXX
  • subjectClass = "Sydonie_SocialEntities_Account"
  • subjectId = l'id de l'account de toto@sydonie.com
  • predicate = "OWNS"
  • objectType = "Sydonie_SydonieDocumentTypes_Article"
  • objectValue = "x32b"
  • confirmationStatus = "1"

On aura aussi l'enregistrement suivant :

  • id = YYY
  • subjectClass = "Sydonie_SydonieDocumentTypes_Article"
  • subjectId = "x32b"
  • predicate = "IS_OWNED_BY"
  • objectType = "Sydonie_SocialEntities_Account"
  • objectValue = l'id de l'account de toto@sydonie.com
  • confirmationStatus = "1"