Skip to content

Commit e32fa21

Browse files
samokissSamuel Gomis
andauthored
feat(API-1931): Add guided tutorial "How to get families and attribut… (#696)
* feat(API-1931): Add guided tutorial "How to get families and attributes" to documentation + refactoring * feat(API-1931): fix Co-authored-by: Samuel Gomis <[email protected]>
1 parent 4f023ef commit e32fa21

File tree

7 files changed

+236
-20
lines changed

7 files changed

+236
-20
lines changed

content/getting-started/synchronize-pim-products-6x/step-2.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Synchronize catalog structure: families and attributes
22

3+
:::warning
4+
This guide is being deprecated and will soon be unpublished. Please go to the newest guide [here](/tutorials/how-to-get-families-and-attributes.html).
5+
:::
6+
37
## What do we synchronize?
48
![relationship schema](../../img/getting-started/synchronize-pim-products/step-2-objects-relationship-schema.svg)
59

content/swagger/akeneo-web-api.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<a href="/tutorials/homepage.html" class="back-button">
2+
<button>
3+
<img src="/img/icons/icon--arrow-back.svg" style="margin-right: 10px;">
4+
All guided tutorials
5+
</button>
6+
</a>
7+
8+
# How to get families, family variants, and attributes
9+
10+
<table class="tag-container">
11+
<tr>
12+
<td>Use case:</td>
13+
<td>
14+
<div class="tag-not-selectable">
15+
<div class="tag-color tag-color-light-blue"></div>
16+
<div class="tag-label">App Workflow</div>
17+
</div>
18+
</td>
19+
</tr>
20+
<tr>
21+
<td>PIM Features:</td>
22+
<td class="td-features">
23+
<div class="tag-not-selectable">
24+
<div class="tag-color tag-color-orange"></div>
25+
<div class="tag-label">Attributes</div>
26+
</div>
27+
<div class="tag-not-selectable">
28+
<div class="tag-color tag-color-pink"></div>
29+
<div class="tag-label">Families</div>
30+
</div>
31+
</td>
32+
</tr>
33+
</table>
34+
35+
<div class="endpoint-container">
36+
<div class="endpoint-text">REST API endpoint(s):</div>
37+
<a href="/api-reference.html#get_families" class="endpoint-link" target="_blank" rel="noopener noreferrer">family</a>
38+
<a href="/api-reference.html#Familyvariants" class="endpoint-link" target="_blank" rel="noopener noreferrer">family variants</a>
39+
<a href="/api-reference.html#Attribute" class="endpoint-link" target="_blank" rel="noopener noreferrer">attributes</a>
40+
</div>
41+
42+
<div class="block-requirements">
43+
<div class="block-requirements-headline">
44+
If you're starting to build your App, make sure you previously followed:
45+
</div>
46+
<div class="block-requirements-row">
47+
<img src="../../img/illustrations/illus--Attributegroup.svg" width="110px">
48+
<div class="block-requirements-steps">
49+
<ul>
50+
<li>Step 1. <a href="how-to-get-your-app-token.html" target="_blank" rel="noopener noreferrer">Get your App token tutorial</a></li>
51+
<li>Step 2. <a href="how-to-retrieve-pim-structure.html" target="_blank" rel="noopener noreferrer">How to retrieve PIM structure</a></li>
52+
</ul>
53+
</div>
54+
</div>
55+
</div>
56+
57+
## Context
58+
59+
Families and attributes are the basis of an Akeneo catalog structure: get them before retrieving the products from the PIM.
60+
61+
::: info
62+
If you plan to get product variants and their corresponding models, we advise you to retrieve now the associated family variants.
63+
:::
64+
65+
![relationship schema](../../img/getting-started/synchronize-pim-products/step-2-objects-relationship-schema.svg)
66+
67+
::: tips
68+
Get the big picture <a href="/getting-started/synchronize-pim-products-6x/step-0.html" target="_blank" rel="noopener noreferrer">here</a>.
69+
:::
70+
71+
## Fetch the catalog structure: families and attributes
72+
73+
### Workflow
74+
75+
![relationship schema](../../img/getting-started/synchronize-pim-products/step-2-steps-schema.svg)
76+
77+
### 0 - Initialization
78+
79+
```php [activate:PHP]
80+
81+
$pimUrl = 'https://url-of-your-pim.com';
82+
$appToken = 'your_app_token'; // Token provided during oAuth steps
83+
84+
// If you haven't done it yet, please follow the Guzzle official documentation for installing the client
85+
// https://docs.guzzlephp.org/en/stable/overview.html#installation
86+
87+
// Set your client for querying Akeneo API as follows
88+
$client = new \GuzzleHttp\Client([
89+
'base_uri' => $pimUrl,
90+
'headers' => ['Authorization' => 'Bearer ' . $appToken],
91+
]);
92+
```
93+
94+
### 1 - Collect families and attribute codes
95+
96+
Get families and attribute codes by requesting the PIM API
97+
98+
```php [activate:PHP]
99+
const API_URL = '/api/rest/v1/families?search={"has_products":[{"operator":"=","value":true}]}';
100+
101+
// Make an authenticated call to the API
102+
$response = $client->get(API_URL);
103+
104+
$data = json_decode($response->getBody()->getContents(), true);
105+
106+
// Collect families and list of unique attribute codes from paginated API
107+
$families = $data['_embedded']['items'];
108+
$attributeCodes = array_merge(...array_column($data['_embedded']['items'], 'attributes'));
109+
while (array_key_exists('next', $data['_links'])) {
110+
$response = $client->get($data['_links']['next']['href']);
111+
$data = json_decode($response->getBody()->getContents(), true);
112+
$families = array_merge($families, $data['_embedded']['items']);
113+
$attributeCodes = array_merge($attributeCodes, ...array_column($data['_embedded']['items'], 'attributes'));
114+
}
115+
116+
$attributeCodes = array_unique($attributeCodes);
117+
118+
// Save families and attribute codes into stores
119+
saveFamilies($families);
120+
saveAttributesCodes($attributeCodes);
121+
```
122+
123+
Store family codes in a <b>family_code_list</b> and attribute codes in a separate list (<b>attribute_code_list</b>). We will deal with <b>attribute_code_list</b> later in this tutorial.
124+
125+
::: tips
126+
Warning! with the API call GET api/rest/v1/families, you will collect <b>all the families into the database</b>! Please ask yourself this question before continuing: <i>Do I really need all of them?</i>
127+
At this step, it’s the perfect occasion to save time later, during products synchronization. We strongly advise you to <b>filter your families</b> as much as you can before building family_code_list and attribute_code_list.<br>
128+
👉 One way to do this is <a href="/documentation/filter.html#by-family-codes" target="_blank" rel="noopener noreferrer">the family codes filter</a>
129+
:::
130+
131+
### 2 - Collect family variants
132+
::: info
133+
This step is mandatory if you want to synchronize product variants later. If not, jump to the third step.
134+
:::
135+
136+
Get family variants by requesting the PIM API for each families
137+
138+
```php [activate:PHP]
139+
const MAX_ITEMS = 100;
140+
const API_URL = '/api/rest/v1/families/%s/variants?limit=' . MAX_ITEMS;
141+
142+
// Get family codes from storage
143+
$codes = getFamilyCodes();
144+
145+
// Collect family variants from paginated API
146+
$variants = [];
147+
foreach ($codes as $code) {
148+
$response = $client->get(sprintf(API_URL, $code));
149+
$data = json_decode($response->getBody()->getContents(), true);
150+
$variants = array_merge($variants, $data['_embedded']['items']);
151+
}
152+
153+
// Save variants into storage
154+
saveVariants($variants);
155+
```
156+
157+
### 3 - Collect attributes
158+
159+
Remember your <b>attribute_code_list</b>? It’s (already) time to use it to retrieve attribute information
160+
161+
```php [activate:PHP]
162+
const MAX_ITEMS = 100;
163+
const API_URL = '/api/rest/v1/attributes?search={"code":[{"operator":"IN","value":%s}]}&limit=' . MAX_ITEMS;
164+
165+
// Get attributes codes from storage
166+
$attributeCodes = getAttributesCodes();
167+
168+
// Collect attributes from paginated API
169+
$rawAttributes = [];
170+
foreach (array_chunk($attributeCodes, MAX_ITEMS) as $chunk) {
171+
$response = $client->get(sprintf(API_URL, json_encode($chunk)));
172+
$data = json_decode($response->getBody()->getContents(), true);
173+
$rawAttributes = array_merge($rawAttributes, $data['_embedded']['items']);
174+
}
175+
176+
// Only keep fields needed
177+
$attributes = [];
178+
foreach ($rawAttributes as $rawAttribute) {
179+
$attributes[$rawAttribute['code']] = [
180+
'code' => $rawAttribute['code'],
181+
'type' => $rawAttribute['type'],
182+
// Add additional fields if needed
183+
];
184+
}
185+
186+
// save attributes into storage
187+
saveAttributes($attributes);
188+
```
189+
190+
::: warning
191+
attribute_code_list may be significant, very big! If you get an <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.15" target="_blank" rel="noopener noreferrer">HTTP 414 error</a>
192+
, you probably hit these boundaries. A workaround is to split your attribute_code_list into different parts and call them independently.
193+
:::

content/tutorials/guides/how-to-retrieve-pim-structure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ The channel resource holds the basic PIM structure data.
5757
![relationship schema](../../img/getting-started/synchronize-pim-products/step-1-objects-relationship-schema.svg)
5858

5959
:::tips
60-
Get the big picture <a href="https://api.akeneo.com/getting-started/synchronize-pim-products-6x/step-0.html" target="_blank" rel="noopener noreferrer">here</a>.
60+
Get the big picture <a href="/getting-started/synchronize-pim-products-6x/step-0.html" target="_blank" rel="noopener noreferrer">here</a>.
6161
:::
6262

6363
## Fetch the PIM structure

src/partials/guided-tutorials-homepage.handlebars

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
<div class="tile-list">
2727
{{#each tutorials}}
2828
<a href="{{link}}" class="tile-title">
29-
<div class="tile mr-30">
29+
<div class="tile mrb-30">
3030
{{title}}
3131
<div class="tile-tag">
3232
{{#use_cases}}
33-
<div class="tag-selectable">
33+
<div class="tag-not-selectable">
3434
<div class="tag-color tag-color-{{color}}"></div>
3535
<div class="tag-label">{{use_case}}</div>
3636
</div>
3737
{{/use_cases}}
3838
{{#features}}
39-
<div class="tag-selectable">
39+
<div class="tag-not-selectable">
4040
<div class="tag-color tag-color-{{color}}"></div>
4141
<div class="tag-label">{{feature}}</div>
4242
</div>

styles/guided-tutorial.less

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
flex-direction: row;
146146
justify-content: left;
147147
align-items: center;
148+
148149
padding: 0px 8px 0px 0px;
149150
gap: 8px;
150151

@@ -252,6 +253,10 @@
252253
background-color: #71AEAE;
253254
text-decoration: none;
254255
}
256+
.endpoint-link:focus {
257+
color: white;
258+
text-decoration: none;
259+
}
255260

256261
.tutorial-title {
257262
font-weight: 700;
@@ -263,6 +268,17 @@
263268
margin-top: 40px;
264269
}
265270

271+
.td-features {
272+
display: flex;
273+
flex-direction: row;
274+
justify-content: left;
275+
align-items: center;
276+
width: 350px;
277+
.tag-not-selectable {
278+
margin-right: 12px;
279+
}
280+
}
281+
266282
/********************* TILE ************************/
267283
.tile {
268284
position: relative;
@@ -282,10 +298,10 @@
282298

283299
border: 1px solid #8C9AAF;
284300
border-radius: 5px;
285-
&:hover {
301+
&:hover , &:focus{
286302
color: #414D97;
287-
text-decoration: none;
288303
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.25);
304+
cursor: pointer;
289305
}
290306
}
291307

@@ -305,6 +321,11 @@
305321
flex: none;
306322
order: 0;
307323
flex-grow: 0;
324+
&:hover , &:focus{
325+
text-decoration: none;
326+
color: #414D97;
327+
cursor: auto;
328+
}
308329
}
309330

310331
.tile-tag {
@@ -314,7 +335,7 @@
314335
position: absolute;
315336
bottom: 8px;
316337
right: 0;
317-
.tag-selectable {
338+
.tag-selectable, .tag-not-selectable {
318339
margin-right: 8px;
319340
}
320341
}
@@ -326,13 +347,11 @@
326347
flex-wrap: wrap;
327348
margin-top: 21px;
328349
width: 770px;
329-
.tile {
330-
margin-bottom: 30px;
331-
}
332350
}
333351

334-
.mr-30 {
352+
.mrb-30 {
335353
margin-right: 30px;
354+
margin-bottom: 30px;
336355
}
337356

338357
/********************* USE CASE & FEATURE ********************/

tasks/build-doc.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -803,11 +803,11 @@ gulp.task('build-tutorials-homepage', ['clean-dist','less'], function () {
803803
const pages = {
804804
"how-to-get-your-app-token.md": "How to get your App token",
805805
"how-to-retrieve-pim-structure.md": "How to retrieve PIM structure",
806+
"how-to-get-families-and-attributes.md": "How to get families, family variants, and attributes",
806807
// "how-to-get-categories.md": "How to get categories",
807808
// "how-to-parse-product-values.md": "How to parse product values",
808809
// "how-to-collect-products.md": "How to collect products",
809810
// "how-to-collect-product-variations.md": "How to collect product variations",
810-
// "how-to-get-families-and-attributes.md": "How to get families and attributes",
811811
// "how-to-publish-your-app.md": "How to publish your App",
812812
};
813813

@@ -837,6 +837,12 @@ gulp.task('build-tutorials-homepage', ['clean-dist','less'], function () {
837837
'features': [],
838838
'use_cases': useCases
839839
},
840+
{
841+
'title': 'How to get families, family variants, and attributes',
842+
'link': '/tutorials/how-to-get-families-and-attributes.html',
843+
'features': [ features[4], features[3]],
844+
'use_cases': useCases
845+
},
840846
// {
841847
// 'title': 'How to get categories',
842848
// 'link': '/apps/how-to-get-categories.html',
@@ -862,12 +868,6 @@ gulp.task('build-tutorials-homepage', ['clean-dist','less'], function () {
862868
// 'use_cases': useCases
863869
// },
864870
// {
865-
// 'title': 'How to get families and attributes',
866-
// 'link': '/apps/how-to-get-families-and-attributes.html',
867-
// 'features': [ features[4], features[3]],
868-
// 'use_cases': useCases
869-
// },
870-
// {
871871
// 'title': 'How to publish your App',
872872
// 'link': '/apps/how-to-publish-your-app.html',
873873
// 'features': [],
@@ -907,11 +907,11 @@ gulp.task('build-tutorials', ['clean-dist','less'], function () {
907907
const pages = {
908908
"how-to-get-your-app-token.md": "How to get your App token",
909909
"how-to-retrieve-pim-structure.md": "How to retrieve PIM structure",
910+
"how-to-get-families-and-attributes.md": "How to get families, family variants, and attributes",
910911
// "how-to-get-categories.md": "How to get categories",
911912
// "how-to-parse-product-values.md": "How to parse product values",
912913
// "how-to-collect-products.md": "How to collect products",
913914
// "how-to-collect-product-variations.md": "How to collect product variations",
914-
// "how-to-get-families-and-attributes.md": "How to get families and attributes",
915915
// "how-to-publish-your-app.md": "How to publish your App",
916916
};
917917

0 commit comments

Comments
 (0)