# Modèle de page et données

Les blocs de l'app couvrent la plupart des cas. Mais une marque veut parfois montrer ses points de vente ailleurs ou autrement : les horaires dans le pied de page de la page d'un point de vente, les points de vente d'une ville dans un article de blog, une section sur mesure du thème de l'agence. Tout cela est possible en Liquid, parce que l'app publie chaque point de vente qui a une page dans des métaobjets de la boutique, que le thème lit comme il lit les produits.

Cette page explique comment ces données sont rangées, montre comment les lire, puis détaille chaque champ.

:::caution[L'app n'est pas encore sortie]
Les types et les champs décrits ici peuvent encore changer.
:::

## Comment les données sont rangées

Le marchand gère ses points de vente dans l'app. À chaque enregistrement d'un point de vente qui a une page, l'app le publie dans la boutique, en une seconde environ, dans une entrée de métaobjet **Store page** (`$app:store_page`) : sa page, avec son adresse sur la boutique, son title et sa meta description, et **toutes les données** du point de vente : nom, adresse, horaires, photos, type…

Un point de vente sans page, seulement sur le store locator (un revendeur, par exemple), n'a pas d'entrée : les blocs de l'app (store locator, liste des points de vente) le lisent dans des données à eux, que Liquid ne lit pas en dehors d'eux.

Les types de points de vente (« Flagship », « Revendeur »…) sont des entrées **Store type** (`$app:store_type`), et les services (« Click & Collect », « Réparations »…) des entrées **Store service** (`$app:store_service`) : chaque page les référence, et leurs noms sont traduits.

Une entrée a le même handle que le point de vente, qui est aussi la fin de l'adresse de sa page : `paris-le-marais`. Un point de vente en brouillon est publié comme caché : Liquid ne le voit pas. Un point de vente sans page, ou supprimé, n'a pas d'entrée.

Le marchand voit ces entrées dans **Contenu > Métaobjets**, en lecture seule : on les modifie toujours depuis l'app.

## Trouver l'identifiant de l'app

Dans les blocs de l'app, les types s'écrivent `$app:store_page`. Dans une section ou un snippet du thème, il faut leur nom complet, avec l'identifiant de l'app sur la boutique : `app--<id>--store_page`. Pour le trouver :

1. Dans Shopify, ouvrez **Boutique en ligne > Thèmes**, puis, dans le menu **…** du thème, **Modifier le code**.
2. Ouvrez le dossier `templates/metaobject`.
3. Le template des pages des points de vente s'appelle `app--<id>--store_page.json` : le nombre est l'identifiant de l'app.

Le template n'existe qu'une fois créé dans l'éditeur de thème (voir [Configurer l'app](/fr/docs/getting-started/set-up/)).

## Ajouter vos sections à la page d'un point de vente

Toutes les pages des points de vente partagent le template **Store page**. Le bloc **Page point de vente** compose la page, mais vous pouvez placer d'autres sections du thème autour de lui : un bandeau, un texte de la marque, des produits mis en avant. Leurs textes se relient aux données du point de vente sans code :

1. Dans l'éditeur de thème, ouvrez le template **Store page**.
2. Ajoutez une section du thème, par exemple un texte riche.
3. Dans un réglage de texte, cliquez sur la source dynamique (la petite prise), puis choisissez un champ du point de vente : **Name**, **City**, **Description**…

Chaque page affiche alors les données de son point de vente.

## Lire les données en Liquid

Sur le template **Store page**, `metaobject` est l'entrée **Store page** affichée, avec toutes les données du point de vente. Chaque champ se lit par sa clé, suivie de `.value`.

### Afficher les services en pastilles

```liquid
<ul class="services">
  {%- for service in metaobject.store_services.value -%}
    <li>{{ service.name.value }}</li>
  {%- endfor -%}
</ul>
```

### Afficher les horaires d'un jour

`opening_hours` est un JSON avec une clé par jour ouvert (voir [son format](#les-horaires)). Les horaires du lundi :

```liquid
{%- assign hours = metaobject.opening_hours.value -%}
{%- for slot in hours.monday -%}
  {{ slot.opens }}–{{ slot.closes }}
{%- else -%}
  Fermé
{%- endfor -%}
```

« Fermé » est à traduire avec les fichiers de langue de votre thème.

### Lister les points de vente d'une ville

Partout ailleurs dans le thème, lisez les entrées par le nom complet du type. Par exemple, les liens vers les pages des points de vente de Lyon :

```liquid
{%- for store in metaobjects['app--<id>--store_page'].values -%}
  {%- if store.city.value == 'Lyon' -%}
    <a href="{{ store.system.url }}">{{ store.name.value }}</a>
  {%- endif -%}
{%- endfor -%}
```

Une entrée précise se lit par son handle : `metaobjects['app--<id>--store_page']['paris-le-marais']`. C'est ainsi que se lisent les points de vente à proximité, dont `nearby_stores` donne les handles.

:::note[Bon à savoir]
- Parcourez toujours les entrées avec `for` : `values.first` est vide.
- Une page de résultats compte 250 entrées au plus : au-delà, paginez.
- Shopify ne filtre pas les métaobjets par champ : comme dans l'exemple, le filtre se fait dans la boucle.
- Seuls les points de vente avec une page y sont : une liste en Liquid ne peut pas montrer tous les points de vente du store locator. Le store locator saute aussi les points de vente dont `store_locator` est faux, qui n'ont qu'une page : faites de même si votre liste doit montrer ceux qu'il montre.
- Le handle d'une entrée, donc l'adresse de la page, ne se traduit pas.
:::

## Les champs d'une page de point de vente (Store page)

| Champ | Type | Contenu |
| --- | --- | --- |
| `name` | texte | Le nom. Traduisible. |
| `address1`, `address2` | texte | Les lignes d'adresse. |
| `city`, `zip` | texte | La ville, le code postal. |
| `province_code` | texte | Le code de province Shopify (`NY`, `QC`, `JP-13`), vide pour un pays sans provinces. |
| `country_code` | texte | Le code pays ISO à deux lettres (`FR`). |
| `address` | texte | L'adresse entière sur une ligne. |
| `latitude`, `longitude` | décimal | Les coordonnées, vides tant que l'adresse n'est pas placée. |
| `timezone` | texte | Le fuseau horaire IANA du point de vente (`Europe/Paris`), tiré de ses coordonnées. |
| `phone`, `email` | texte | Le contact. |
| `access` | texte multiligne | Comment venir, une ligne par arrêt ou parking. Traduisible. |
| `opening_hours` | JSON | Les horaires de la semaine (format ci-dessous). |
| `special_hours` | JSON | Les horaires exceptionnels en cours ou à venir (format ci-dessous). |
| `description` | texte riche | Le texte de présentation, à afficher avec `metafield_tag`. Traduisible. |
| `photos` | liste de fichiers | Les photos ; la première est la principale. |
| `store_services` | liste de références vers **Store service** | Les services ; le nom de chacun est dans `name`, traduisible. |
| `tags` | liste de textes | Les tags. |
| `store_type` | référence vers **Store type** | Le type, vide sans type. |
| `store_locator` | booléen | Vrai si le point de vente est aussi sur le store locator ; faux s'il n'a qu'une page. |
| `nearby_stores` | liste de textes | Les handles des 3 points de vente avec une page les plus proches, du plus proche au plus loin. |
| `seo_title` | texte | Le title de la page : celui saisi dans l'app, sinon le nom. Traduisible. |
| `seo_description` | texte | La meta description : celle saisie dans l'app, sinon le texte de présentation en texte brut, coupé à 160 caractères. Traduisible. |
| `structured_data` | texte multiligne | Écrit par l'app : les horaires, la position et l'adresse des données structurées de la page, que le bloc **Page point de vente** insère. À ne pas traduire ni modifier. |

L'adresse de la page : `system.url`.

### Les horaires

`opening_hours` a une clé par jour ouvert, en anglais (`monday` à `sunday`), avec ses créneaux en heures locales du point de vente. Un jour fermé n'a pas de clé ; un jour à horaires coupés a plusieurs créneaux.

```json
{
  "monday": [{ "opens": "10:00", "closes": "13:00" }, { "opens": "14:00", "closes": "19:00" }],
  "saturday": [{ "opens": "10:00", "closes": "19:00" }]
}
```

`special_hours` liste les jours et les périodes qui sortent de la semaine habituelle (jours fériés, vacances, nocturnes), dans l'ordre des dates. `from` et `to` sont des dates du calendrier du point de vente, les mêmes pour un seul jour ; `label` est facultatif. Une période sans créneau est fermée. Ces jours-là, elle remplace les horaires de la semaine. Les périodes passées disparaissent d'elles-mêmes.

```json
[
  { "from": "2026-12-24", "to": "2026-12-24", "label": "Réveillon", "slots": [{ "opens": "10:00", "closes": "16:00" }] },
  { "from": "2026-12-25", "to": "2026-12-25", "label": "Noël", "slots": [] }
]
```

## Les champs d'un type (Store type)

| Champ | Type | Contenu |
| --- | --- | --- |
| `name` | texte | Le nom du type (« Flagship »). Traduisible. |
| `color` | couleur | La couleur de ses marqueurs, vide sans couleur. |
| `marker` | adresse | L'adresse du SVG de son marqueur personnalisé, vide sans marqueur. |

## Les champs d'une page pays (Country page)

Une entrée par pays qui a assez de points de vente, créée par l'app quand les pages pays sont activées ; les textes sont modifiables dans **Contenu > Metaobjects > Country page**. Les points de vente ne sont pas dans l'entrée : le bloc **Page pays ou ville**, sur ce template, les lit dans les données du store locator.

| Champ | Type | Contenu |
| --- | --- | --- |
| `country_code` | texte | Le code du pays (ISO 3166-1, `FR`), écrit par l'app. |
| `seo_title` | texte | Le title de la page : le nom du pays par défaut. Traduisible. |
| `seo_description` | texte | La meta description. Traduisible. |
| `introduction` | texte riche | Le texte affiché sous le nom du pays. Traduisible. |

## Les champs d'une page ville (City page)

Une entrée par ville qui a assez de points de vente, créée par l'app quand les pages villes sont activées ; les textes sont modifiables dans **Contenu > Metaobjects > City page**. La ville est son nom tel qu'écrit sur les points de vente.

| Champ | Type | Contenu |
| --- | --- | --- |
| `country_code` | texte | Le code du pays (ISO 3166-1, `FR`), écrit par l'app. |
| `city` | texte | La ville, telle qu'écrite sur ses points de vente, écrite par l'app. |
| `seo_title` | texte | Le title de la page : le nom de la ville par défaut. Traduisible. |
| `seo_description` | texte | La meta description. Traduisible. |
| `introduction` | texte riche | Le texte affiché sous le nom de la ville. Traduisible. |

## Le format d'import

Le fichier que le marchand importe dans l'app (CSV ou Excel) a ses colonnes et ses formats décrits dans [la référence du format d'import](https://app.getlavia.app/docs/import-format.md), servie par l'app et lisible par les assistants IA. Pour reprendre les points de vente d'un autre outil ou d'un fichier de la marque, partez de cette référence.

## Pour aller plus loin

- [Blocs de thème](/fr/docs/developers/theme-blocks/) : ce que les blocs font déjà avec ces données.
- [SEO](/fr/docs/developers/seo/) : le title, la meta description et les données structurées des pages.