#How to get families, family variants, and attributes
Use case: |
App Workflow
|
PIM Features: |
Attributes
Families
|
#Context
Families and attributes are the basis of an Akeneo catalog structure: get them before retrieving the products from the PIM.
If you plan to get product variants and their corresponding models, we advise you to retrieve now the associated family variants.
Get the big picture here.
#Fetch the catalog structure: families and attributes
#Workflow
#0 - Initialization
function buildApiClient(): GuzzleHttp\Client
{
$pimUrl = 'https://url-of-your-pim.com';
$appToken = 'your_app_token'; // Token provided during oAuth steps
// If you haven't done it yet,
// please follow the Guzzle official documentation to install the client
// https://docs.guzzlephp.org/en/stable/overview.html#installation
// Set your client for querying Akeneo API as follows
$client = new \GuzzleHttp\Client([
'base_uri' => $pimUrl,
'headers' => ['Authorization' => 'Bearer ' . $appToken],
]);
}
// Install the node-fetch library by following the official documentation:
// https://www.npmjs.com/package/node-fetch
import fetch from 'node-fetch';
const pimUrl = 'https://url-of-your-pim.com';
const accessToken = 'your_app_token'; // Token provided during oAuth steps
// Set your client for querying Akeneo API as follows
async function get(url, accessToken) {
return await fetch(url, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
}
#1 - Collect families and attribute codes
Get families and attribute codes by requesting the PIM API
$client = buildApiClient();
const MAX_ITEMS = 100;
$nextUrl = '/api/rest/v1/families?search={"has_products":[{"operator":"=","value":true}]}&limit=' . MAX_ITEMS;
$families = $attributeCodes = [];
do {
// Collect families and list of unique attribute codes from API
$response = $client->get($nextUrl);
$data = json_decode($response->getBody()->getContents(), true);
$families[] = $data['_embedded']['items'];
$attributeCodes = array_merge(
$attributeCodes,
...array_column($data['_embedded']['items'], 'attributes'));
$nextUrl = $data['_links']['next']['href'] ?? null;
} while (
$nextUrl
);
$families = array_merge(...$families);
$attributeCodes = array_unique($attributeCodes);
// Save families and attribute codes into storage
storeFamilies($families);
storeAttributesCodes($attributeCodes);
const maxItems = 100;
let nextUrl = `${pimUrl}/api/rest/v1/families?search={"has_products":[{"operator":"=","value":true}]}&limit=${maxItems}`;
const families = [];
do {
const response = await get(nextUrl, accessToken);
const data = await response.json();
const newFamilies = data['_embedded']['items']
families.push(...newFamilies);
nextUrl = data._links?.next?.href;
} while (nextUrl)
// Collect attributes from all families
const attributeCodes = families.reduce(
(acc, family) => [...acc, ...family.attributes],
[]
);
const uniqueAttributes = [...new Set(attributeCodes)];
// Save families and attribute codes into storage
storeFamilies(families);
storeAttributeCodes(uniqueAttributes);
Store family codes in a family_code_list and attribute codes in a separate list (attribute_code_list). We will deal with attribute_code_list later in this tutorial.
Warning! with the API call GET api/rest/v1/families, you will collect all the families into the database! Please ask yourself this question before continuing: Do I really need all of them?
At this step, it’s the perfect occasion to save time later, during products synchronization. We strongly advise you to filter your families as much as you can before building family_code_list and attribute_code_list.
👉 One way to do this is the family codes filter
#2 - Collect family variants
This step is mandatory if you want to synchronize product variants later. If not, jump to the third step.
Get family variants by requesting the PIM API for each families
$client = buildApiClient();
$maxProductsPerPage = 100;
$apiUrl = '/api/rest/v1/families/%s/variants?limit=%s';
// Get family codes from storage
$codes = getFamilyCodes();
// Collect family variants from API
$familyVariants = [];
foreach ($codes as $code) {
$nextUrl = sprintf($apiUrl, $code, $maxProductsPerPage);
do {
// Collect family variants from API
$response = $client->get($nextUrl);
$data = json_decode($response->getBody()->getContents(), true);
$familyVariants[] = $data['_embedded']['items'];
$nextUrl = $data['_links']['next']['href'] ?? null;
} while (
$nextUrl
);
}
$familyVariants = array_merge(...$familyVariants);
// add index to $familyVariants
$indexedFamilyVariants = [];
foreach ($familyVariants as $familyVariant) {
$indexedFamilyVariants[$familyVariant['code']] = $familyVariant;
}
// Save family variants into storage
storeFamilyVariants($indexedFamilyVariants);
const maxProductsPerPage = 100;
// Get family codes from storage
const familyCodes = await getFamilyCodes();
let familyVariants = [];
for (const code of familyCodes) {
let nextUrl = `${pimUrl}/api/rest/v1/families/${code}/variants?limit=${maxProductsPerPage}`;
do {
// Collect family variants from API
const response = await get(nextUrl, accessToken);
const data = await response.json();
const newVariants = data['_embedded']['items'];
familyVariants.push(...newVariants);
nextUrl = data._links?.next?.href;
} while (nextUrl)
}
// add index to familyVariants
let indexedFamilyVariants = {};
for (const familyVariant of familyVariants) {
indexedFamilyVariants[familyVariant['code']] = familyVariant;
}
// Save family variants into storage
storeFamilyVariants(indexedFamilyVariants);
#3 - Collect attributes
Remember your attribute_code_list? It’s (already) time to use it to retrieve attribute information
$client = buildApiClient();
const MAX_ITEMS = 100;
const API_URL = '/api/rest/v1/attributes?search={"code":[{"operator":"IN","value":%s}]}&limit=%s';
// Get attributes codes from storage
$attributeCodes = getAttributesCodes();
// Collect attributes from API
$rawAttributes = [];
foreach (array_chunk($attributeCodes, MAX_ITEMS) as $chunk) {
$response = $client->get(sprintf(API_URL, json_encode($chunk), MAX_ITEMS));
$data = json_decode($response->getBody()->getContents(), true);
$rawAttributes[] = $data['_embedded']['items'];
}
$rawAttributes = array_merge(...$rawAttributes);
// Only keep fields needed
$attributes = [];
foreach ($rawAttributes as $rawAttribute) {
$attributes[$rawAttribute['code']] = [
'code' => $rawAttribute['code'],
'type' => $rawAttribute['type'],
// Add additional fields if needed
];
}
// save attributes into storage
storeAttributes($attributes);
const maxItems = 100;
// Get attributes codes from storage
const attributeCodes = await getAttributeCodes();
const maxAttributesPerQuery = 10;
// split attributeCodes in chucks of $maxAttributesPerQuery elements
let chunks = [];
while (attributeCodes.length > 0) {
chunks.push(attributeCodes.splice(0, maxAttributesPerQuery));
}
let rawAttributes = [];
for (const item of chunks) {
let nextUrl = `${pimUrl}/api/rest/v1/attributes?search={"code":[{"operator":"IN","value":${JSON.stringify(item)}}]}&limit=` + maxItems;
do {
const response = await get(nextUrl, accessToken);
let data = await response.json();
let newRawAttributes = data['_embedded']['items']
rawAttributes.push(...newRawAttributes);
nextUrl = data._links?.next?.href;
} while (nextUrl);
}
// Only keep fields needed
let attributes = {};
for (const rawAttribute of rawAttributes) {
attributes[rawAttribute['code']] = {
'code': rawAttribute['code'],
'type': rawAttribute['type'],
// Add additional fields if needed
};
}
//save attributes into storage
storeAttributes(attributes);
Retrieved attribute list follows this structure:
// Output
[
'attribute_code' => [
'code' => 'attribute_code',
'type' => 'pim_catalog_text',
],
]
// Output
{
"attribute_code":
{
"code": "attribute_code",
"type": "pim_catalog_text"
}
}
attribute_code_list may be significant, very big! If you get an HTTP 414 error , you probably hit these boundaries. A workaround is to split your attribute_code_list into different parts and call them independently.