Aller au contenu

Salesforce Files expliqué : ContentDocument, ContentVersion et ContentDocumentLink

Un fichier Salesforce n'est pas un objet mais trois. Comprendre lequel fait quoi évite la plupart des erreurs de manipulation, en requête comme en migration.

Un administrateur qui ouvre pour la première fois le modèle des Fichiers Salesforce est surpris : là où il attendait un objet, il en trouve trois. Cette séparation n’est pas une complication gratuite. Elle explique la plupart des comportements qu’on trouve étranges au début, et elle conditionne toute manipulation de fichiers en masse.

Les trois objets

ContentDocument — le document

ContentDocument représente le document en tant qu’entité, indépendamment de son contenu à un instant donné. C’est lui qui porte l’identité du fichier dans la durée : son titre, son extension, sa taille, son propriétaire.

Il ne contient pas le fichier lui-même. Il pointe vers sa version courante par le champ LatestPublishedVersionId.

Un point à retenir : on ne crée pas un ContentDocument directement. Il apparaît quand on crée une première version.

ContentVersion — le contenu, à un moment donné

ContentVersion porte le contenu binaire du fichier, dans le champ VersionData, ainsi que la date, l’auteur et le numéro de version.

Un même ContentDocument peut compter dix ContentVersion. Une seule a IsLatest = true.

C’est par cet objet qu’on crée un fichier :

  • créer une ContentVersion sans ContentDocumentId engendre un nouveau document ;
  • créer une ContentVersion avec un ContentDocumentId ajoute une version au document existant.

Cette subtilité est la source d’erreur la plus fréquente des imports en masse : on obtient huit cents documents distincts là où on attendait huit cents versions, ou l’inverse.

ContentDocumentLink relie un document à un enregistrement. C’est un objet de jonction, avec ContentDocumentId d’un côté et LinkedEntityId de l’autre.

Sa conséquence est considérable : un même document peut être rattaché à autant d’enregistrements que nécessaire, sans être dupliqué. Un contrat lié au compte et à l’opportunité occupe une seule fois l’espace de stockage.

Deux champs méritent attention :

  • ShareType décide de ce que le destinataire peut faire : V pour lecture seule, C pour collaborateur, I pour un accès déduit de celui de l’enregistrement parent.
  • Visibility décide qui voit le lien : AllUsers, InternalUsers, ou SharedUsers.

Trois pièges classiques

Une requête SOQL sur ContentDocumentLink doit filtrer sur ContentDocumentId ou sur LinkedEntityId. Sans filtre, la plateforme refuse.

SELECT ContentDocumentId FROM ContentDocumentLink
WHERE LinkedEntityId = '001...'

La limitation surprend, mais elle a sa logique : la table est immense, et une requête sans filtre reviendrait à demander tous les rattachements de l’org.

Conséquence pratique : pour lister les documents d’un ensemble d’enregistrements, il faut d’abord la liste des identifiants, puis interroger par lots.

Supprimer un lien ne supprime pas le document

Retirer un ContentDocumentLink détache le fichier de l’enregistrement, sans plus. Le document continue d’exister, il n’est simplement plus visible depuis cette fiche.

C’est ce qui produit les documents orphelins : ils occupent du stockage, ils n’apparaissent nulle part, et personne ne les cherche puisque personne ne sait qu’ils sont là.

À l’inverse, supprimer le ContentDocument supprime toutes ses versions et tous ses rattachements d’un coup.

La suppression passe par la corbeille

Un ContentDocument supprimé part à la corbeille et continue de compter dans le quota de stockage jusqu’à sa purge. Après une opération de nettoyage, la page d’utilisation du stockage ne bouge donc pas immédiatement — ce qui fait conclure à tort que le nettoyage a échoué.

Le partage public : ContentDistribution

Un quatrième objet mérite d’être connu. ContentDistribution engendre un lien public vers une version de fichier, consultable sans licence Salesforce.

Ses champs utiles :

  • ExpiryDate, la date au-delà de laquelle le lien cesse de fonctionner ;
  • PreferencesPasswordRequired, qui protège le lien par un mot de passe ;
  • PreferencesAllowOriginalDownload, qui autorise ou non le téléchargement ;
  • ViewCount, le nombre de consultations ;
  • DistributionPublicUrl, le lien lui-même.

C’est l’outil natif du partage externe. Sa limite : quiconque possède l’adresse accède au document, et le suivi se réduit à un compteur.

Et les Attachments dans tout ça ?

L’objet Attachment est le mécanisme antérieur. Son modèle tient en un objet : un ParentId, un Body. Ni version, ni rattachement multiple, ni partage public.

La plateforme n’y investit plus depuis des années, et toutes les fonctions récentes sont du côté des Fichiers. Pour un projet documentaire engagé aujourd’hui, la conversion est un préalable, pas une option.

Ce qu’il faut retenir

ContentDocument est le document, ContentVersion son contenu à un instant donné, ContentDocumentLink son rattachement à un enregistrement. Cette séparation permet le versionnement et le rattachement multiple sans duplication, qui sont les deux forces du modèle.

Les pièges tiennent en trois lignes : la requête sur les liens exige un filtre, retirer un lien ne supprime pas le document, et la corbeille retient le stockage.

Toute solution documentaire native sérieuse s’appuie sur ces objets sans les contourner. C’est le cas de gedone : il ajoute la connaissance du client, du tag et de la date de validité, sans jamais déplacer ni copier le fichier lui-même.

Une démonstration sur vos propres dossiers

Trente minutes de visioconférence suffisent à établir si le produit répond à votre besoin. La présentation s'appuie sur un modèle de données proche du vôtre, et le compte rendu qui suit précise autant ce que gedone couvre que ce qu'il laisse de côté.

Demander une démonstration

Réponse sous deux jours ouvrés