|
| 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 | + |
| 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 | + |
| 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 | +::: |
0 commit comments