Answers.Body: <p>Try this approach:</p>
  
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  return [
      'transformer' =&gt; function(craft\base\Element $entry) {
          $level1Categories = [];
          $level2Categories = [];
          $level3Categories = [];
  
          foreach ($entry-&gt;equipmentCategory-&gt;level(1)-&gt;all() as $category) {
              /** @var $category \craft\elements\Category */
              $level1Categories[] = $category-&gt;title;
              $children           = $category-&gt;getChildren()-&gt;level(2)-&gt;all();
  
              foreach ($children as $child) {
                  /** @var $child \craft\elements\Category */
                  $crumb      = [
                      $category-&gt;title,
                      $child-&gt;title,
                  ];
  
                  $level2Categories[] = implode(' &gt; ', $crumb);
  
                  foreach ($child-&gt;getChildren()-&gt;level(3)-&gt;all() as $subChild) {
                      /** @var $subChild \craft\elements\Category */
                      $crumb      = [
                          $category-&gt;title,
                          $child-&gt;title,
                          $subChild-&gt;title
                      ];
  
                      $level3Categories[] = implode(' &gt; ', $crumb);
                  }
              }
          }
  
          return [
              'entryTitle'   =&gt; $entry-&gt;title,
              'id'           =&gt; $entry-&gt;id,
              'url'          =&gt; $entry-&gt;url,
              'title'        =&gt; $entry-&gt;equipmentFriendlyName,
              'manufacturer' =&gt; $entry-&gt;equipmentManufacturer,
              'modelNumber'  =&gt; $entry-&gt;equipmentModelNumber,
              'description'  =&gt; $entry-&gt;equipmentDescription,
              'thumbnail'    =&gt; !empty($entry-&gt;equipmentImages-&gt;one()) ? (string)('https://acsweb.com' . $entry-&gt;equipmentImages-&gt;one()-&gt;getUrl()) : '',
              'categories'   =&gt; [
                  'lvl0' =&gt; $level1Categories,
                  'lvl1' =&gt; $level2Categories,
                  'lvl2' =&gt; $level3Categories,
  
              ],
          ];
      },
  ];```
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-05-19T13:13:53.957Z
Answers.LastActivityDate: 2019-05-19T13:13:53.957Z
Answers.UserDisplayName: Fred Carlsen
Answers.Body: <p>You need to loop through <code>equipmentCategory</code> and compile the array that you want.</p>
  
  <pre class="lang-php prettyprint-override"><code>$categories = [];
  
  foreach ($entry-&gt;equipmentCategory-&gt;all() as $category) {
      $categories[] = /* However you want to configure the category */;
  }
  
  return [
      ...
      'categories' =&gt; $categories,
  ];
  </code></pre>
  
  <p>You may even want to offload this logic to a separate <code>private</code> method, to keep things a little cleaner.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-05-17T19:17:33.837Z
Answers.LastActivityDate: 2019-05-17T19:17:33.837Z
Answers.UserDisplayName: Lindsey D
Answers.Body: <p>I tried Fred Carsen's approach, but it didn't quite output what I expected. Here's my approach:</p>
  <pre class="lang-php prettyprint-override"><code>// get categories
  $level1Categories = [];
  $level2Categories = [];
  $level3Categories = [];
  
  foreach ($product-&gt;productFamily-&gt;level(1)-&gt;all() as $category) {
      $level1Categories[] = $this-&gt;traverseCategory($category);
  }
  foreach ($product-&gt;productFamily-&gt;level(2)-&gt;all() as $category) {
      $level2Categories[] = $this-&gt;traverseCategory($category);
  }
  foreach ($product-&gt;productFamily-&gt;level(3)-&gt;all() as $category) {
      $level3Categories[] = $this-&gt;traverseCategory($category);
  }    
  
  // ...
  
  return [
      // ...
      'productFamilies'   =&gt; [
          'lvl0' =&gt; $level1Categories,
          'lvl1' =&gt; $level2Categories,
          'lvl2' =&gt; $level3Categories
      ]            
  ];
  
  // ...
  
  private function traverseCategory($category){
      $parent = $category-&gt;parent;
      if ($parent != null){
          return $this-&gt;traverseCategory($parent) . ' &gt; ' . $category-&gt;title;
      }
      else {
          return $category-&gt;title;
      }
  }
  </code></pre>
  <p>As I had to poke a lot around to find info on this, I've included the entire SiteModuleService that creates the Algolia Indicies and scales a product image. Perhaps it is helpful for someone else:</p>
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  /**
   * site-module module for Craft CMS 3.x
   */
  
  namespace modules\sitemodule\services;
  
  use modules\sitemodule\Sitemodule;
  
  use aelvan\imager\Imager;
  use aelvan\imager\services\ImagerService;
  use craft\helpers\UrlHelper;
  
  use Craft;
  use craft\base\Component;
  use craft\commerce\Plugin;
  
  class SitemoduleService extends Component
  {
  
      public function getIndicies(){
          $indicies = [];
          $productTypes = Plugin::getInstance()-&gt;getProductTypes()-&gt;getAllProductTypes();
  
          // iterate product types and create one index for each type
          foreach ($productTypes as $productType){
  
              $indicies[] = 
              \rias\scout\ScoutIndex::create(getenv('ENVIRONMENT').'_products_'.$productType-&gt;handle)
                  -&gt;elementType(\craft\commerce\elements\Product::class)
                  -&gt;criteria(function (\craft\commerce\elements\db\ProductQuery $query) use ($site, $productType) {
  
                      $query-&gt;siteId = $site-&gt;id;
                      $query-&gt;typeId = $productType-&gt;id;
  
                      return $query;
                  })
                  -&gt;transformer(function (\craft\commerce\elements\Product $product) {
                      return $this-&gt;transformProductData($product);
                  });
          }
  
          // create one indice that contains all products
          $indicies[] = 
          \rias\scout\ScoutIndex::create(getenv('ENVIRONMENT').'_products')
              -&gt;elementType(\craft\commerce\elements\Product::class)
              -&gt;criteria(function (\craft\commerce\elements\db\ProductQuery $query) use ($site) {
  
                  $query-&gt;siteId = $site-&gt;id;
  
                  return $query;
              })
              -&gt;transformer(function (\craft\commerce\elements\Product $product) {
                  return $this-&gt;transformProductData($product);
              });        
  
          return $indicies;
      }
  
      private function traverseCategory($category){
          $parent = $category-&gt;parent;
          if ($parent != null){
              return $this-&gt;traverseCategory($parent) . ' &gt; ' . $category-&gt;title;
          }
          else {
              return $category-&gt;title;
          }
      }
  
      /**
       * Transformer used by Scout to feed Algolia with Product data
       */
      public function transformProductData(\craft\commerce\elements\Product $product){
  
          $supplier = (string)$product-&gt;supplier[0]-&gt;title;
  
          // get categories
          $level1Categories = [];
          $level2Categories = [];
          $level3Categories = [];
  
          foreach ($product-&gt;productFamily-&gt;level(1)-&gt;all() as $category) {
              $level1Categories[] = $this-&gt;traverseCategory($category);
          }
          foreach ($product-&gt;productFamily-&gt;level(2)-&gt;all() as $category) {
              $level2Categories[] = $this-&gt;traverseCategory($category);
          }
          foreach ($product-&gt;productFamily-&gt;level(3)-&gt;all() as $category) {
              $level3Categories[] = $this-&gt;traverseCategory($category);
          }        
  
          $ret = [
              'title' =&gt; $product-&gt;title,
              'url' =&gt; $product-&gt;url,
              'productDescription' =&gt; $product-&gt;productDescription,
              'image' =&gt; $this-&gt;getProductPrimaryImage($product), 
              'supplier' =&gt; $supplier,
              'productFamilies'   =&gt; [
                  'lvl0' =&gt; $level1Categories,
                  'lvl1' =&gt; $level2Categories,
                  'lvl2' =&gt; $level3Categories
              ]            
          ];
  
          // return data to algolia                
          return $ret; 
      }
  
      public function getProductPrimaryImage(\craft\commerce\elements\Product $product){
  
          // generate product image
          $srcImage = $product-&gt;productImage-&gt;one();
  
          // found any?
          $imageUrl = &quot;&quot;;
  
          // get base URL of site 5
          $baseUrl = rtrim(UrlHelper::siteUrl(&quot;&quot;,&quot;&quot;,&quot;&quot;, 5),'/');
  
          if ($srcImage){
  
              // get focalpoint
              $focalPoint = $srcImage-&gt;getFocalPoint();
              if (!$focalPoint) {
                  $focalPoint = &quot;50% 50%&quot;;
              }
  
              // transform with imager
              $image = Imager::getInstance()-&gt;imager-&gt;transformImage($srcImage, [
                  'width' =&gt; 400,
                  'format' =&gt; 'jpg',
                  'ratio' =&gt; 1/1,
                  'allowUpscale' =&gt; false,
                  'jpegQuality' =&gt; 90,
                  'position' =&gt; $focalPoint,
                  'interlace' =&gt; true
              ], null, null);
              $imageUrl = $baseUrl.$image-&gt;url;                            
          }
          else {
              $imageUrl = &quot;https://via.placeholder.com/400x400.png?text=Placeholder&quot;;
          }  
          
          return $imageUrl;
  
      }
  }
  </code></pre>
  <p>It gets called by <code>scout.php</code> thus:</p>
  <pre class="lang-php prettyprint-override"><code>'indices' =&gt; (new \modules\sitemodule\services\SiteModuleService)-&gt;getIndicies()
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-09-30T11:39:03.783Z
Answers.LastActivityDate: 2020-09-30T11:39:03.783Z
Answers.UserDisplayName: nitech
Body: <p>I'm using the Scout plugin to sync my entry data to an Algolia search index. In the transformer for the scout.php config file, I need to iterate over all of the categories in a category field on each entry.</p>
  
  <p>How would I do this to output the category at each level like so:</p>
  
  <pre><code>"categories": {
      "lvl0": ["Rigging", "Film Lighting"],
      "lvl1": ["Rigging &gt; Rigging Support", "Film Lighting &gt; Stands"],
      "lvl2": ["Rigging &gt; Rigging Support &gt; Stands"]
  }
  </code></pre>
  
  <p>Here is where i'm at with the transformer:</p>
  
  <pre class="lang-php prettyprint-override"><code>'transformer' =&gt; static function (craft\base\Element $entry) {
      return [
          'entryTitle' =&gt; $entry-&gt;title,
          'id' =&gt; $entry-&gt;id,
          'url' =&gt; $entry-&gt;url,
          'title' =&gt; $entry-&gt;equipmentFriendlyName,
          'manufacturer' =&gt; $entry-&gt;equipmentManufacturer,
          'modelNumber' =&gt; $entry-&gt;equipmentModelNumber,
          'description' =&gt; $entry-&gt;equipmentDescription,
          'categories' =&gt; $entry-&gt;equipmentCategory-&gt;all(),
      ];
  }
  </code></pre>
  
Tags: <craft3><plugins>
Title: How do I iterate over a categories field in an entry
ThreadText: # How do I iterate over a categories field in an entry
  
  (Asked by: username_0 on 2019-05-17)
  
  I'm using the Scout plugin to sync my entry data to an Algolia search index. In the transformer for the scout.php config file, I need to iterate over all of the categories in a category field on each entry.
  
  How would I do this to output the category at each level like so:
  
  ```
  "categories": {
      "lvl0": ["Rigging", "Film Lighting"],
      "lvl1": ["Rigging > Rigging Support", "Film Lighting > Stands"],
      "lvl2": ["Rigging > Rigging Support > Stands"]
  }
  
  ```
  
  Here is where i'm at with the transformer:
  
  ```php
  'transformer' => static function (craft\base\Element $entry) {
      return [
          'entryTitle' => $entry->title,
          'id' => $entry->id,
          'url' => $entry->url,
          'title' => $entry->equipmentFriendlyName,
          'manufacturer' => $entry->equipmentManufacturer,
          'modelNumber' => $entry->equipmentModelNumber,
          'description' => $entry->equipmentDescription,
          'categories' => $entry->equipmentCategory->all(),
      ];
  }
  
  ```
  
  **Tags:** craft3, plugins
  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_1 on 2019-05-19. Score: 2
  
  Try this approach:
  
  ```php
  <?php
  return [
      'transformer' => function(craft\base\Element $entry) {
          $level1Categories = [];
          $level2Categories = [];
          $level3Categories = [];
  
          foreach ($entry->equipmentCategory->level(1)->all() as $category) {
              /** @var $category \craft\elements\Category */
              $level1Categories[] = $category->title;
              $children           = $category->getChildren()->level(2)->all();
  
              foreach ($children as $child) {
                  /** @var $child \craft\elements\Category */
                  $crumb      = [
                      $category->title,
                      $child->title,
                  ];
  
                  $level2Categories[] = implode(' > ', $crumb);
  
                  foreach ($child->getChildren()->level(3)->all() as $subChild) {
                      /** @var $subChild \craft\elements\Category */
                      $crumb      = [
                          $category->title,
                          $child->title,
                          $subChild->title
                      ];
  
                      $level3Categories[] = implode(' > ', $crumb);
                  }
              }
          }
  
          return [
              'entryTitle'   => $entry->title,
              'id'           => $entry->id,
              'url'          => $entry->url,
              'title'        => $entry->equipmentFriendlyName,
              'manufacturer' => $entry->equipmentManufacturer,
              'modelNumber'  => $entry->equipmentModelNumber,
              'description'  => $entry->equipmentDescription,
              'thumbnail'    => !empty($entry->equipmentImages->one()) ? (string)('https://acsweb.com' . $entry->equipmentImages->one()->getUrl()) : '',
              'categories'   => [
                  'lvl0' => $level1Categories,
                  'lvl1' => $level2Categories,
                  'lvl2' => $level3Categories,
  
              ],
          ];
      },
  ];```
  
  ```
  
  
  ---
  
  ### Answer by username_2 on 2019-05-17. Score: 1
  
  You need to loop through `equipmentCategory` and compile the array that you want.
  
  ```php
  $categories = [];
  
  foreach ($entry->equipmentCategory->all() as $category) {
      $categories[] = /* However you want to configure the category */;
  }
  
  return [
      ...
      'categories' => $categories,
  ];
  
  ```
  
  You may even want to offload this logic to a separate `private` method, to keep things a little cleaner.
  
  
  ---
  
  ### Answer by username_3 on 2020-09-30. Score: 1
  
  I tried username_1 Carsen's approach, but it didn't quite output what I expected. Here's my approach:
  
  ```php
  // get categories
  $level1Categories = [];
  $level2Categories = [];
  $level3Categories = [];
  
  foreach ($product->productFamily->level(1)->all() as $category) {
      $level1Categories[] = $this->traverseCategory($category);
  }
  foreach ($product->productFamily->level(2)->all() as $category) {
      $level2Categories[] = $this->traverseCategory($category);
  }
  foreach ($product->productFamily->level(3)->all() as $category) {
      $level3Categories[] = $this->traverseCategory($category);
  }    
  
  // ...
  
  return [
      // ...
      'productFamilies'   => [
          'lvl0' => $level1Categories,
          'lvl1' => $level2Categories,
          'lvl2' => $level3Categories
      ]            
  ];
  
  // ...
  
  private function traverseCategory($category){
      $parent = $category->parent;
      if ($parent != null){
          return $this->traverseCategory($parent) . ' > ' . $category->title;
      }
      else {
          return $category->title;
      }
  }
  
  ```
  
  As I had to poke a lot around to find info on this, I've included the entire SiteModuleService that creates the Algolia Indicies and scales a product image. Perhaps it is helpful for someone else:
  
  ```php
  <?php
  /**
   * site-module module for Craft CMS 3.x
   */
  
  namespace modules\sitemodule\services;
  
  use modules\sitemodule\Sitemodule;
  
  use aelvan\imager\Imager;
  use aelvan\imager\services\ImagerService;
  use craft\helpers\UrlHelper;
  
  use Craft;
  use craft\base\Component;
  use craft\commerce\Plugin;
  
  class SitemoduleService extends Component
  {
  
      public function getIndicies(){
          $indicies = [];
          $productTypes = Plugin::getInstance()->getProductTypes()->getAllProductTypes();
  
          // iterate product types and create one index for each type
          foreach ($productTypes as $productType){
  
              $indicies[] = 
              \rias\scout\ScoutIndex::create(getenv('ENVIRONMENT').'_products_'.$productType->handle)
                  ->elementType(\craft\commerce\elements\Product::class)
                  ->criteria(function (\craft\commerce\elements\db\ProductQuery $query) use ($site, $productType) {
  
                      $query->siteId = $site->id;
                      $query->typeId = $productType->id;
  
                      return $query;
                  })
                  ->transformer(function (\craft\commerce\elements\Product $product) {
                      return $this->transformProductData($product);
                  });
          }
  
          // create one indice that contains all products
          $indicies[] = 
          \rias\scout\ScoutIndex::create(getenv('ENVIRONMENT').'_products')
              ->elementType(\craft\commerce\elements\Product::class)
              ->criteria(function (\craft\commerce\elements\db\ProductQuery $query) use ($site) {
  
                  $query->siteId = $site->id;
  
                  return $query;
              })
              ->transformer(function (\craft\commerce\elements\Product $product) {
                  return $this->transformProductData($product);
              });        
  
          return $indicies;
      }
  
      private function traverseCategory($category){
          $parent = $category->parent;
          if ($parent != null){
              return $this->traverseCategory($parent) . ' > ' . $category->title;
          }
          else {
              return $category->title;
          }
      }
  
      /**
       * Transformer used by Scout to feed Algolia with Product data
       */
      public function transformProductData(\craft\commerce\elements\Product $product){
  
          $supplier = (string)$product->supplier[0]->title;
  
          // get categories
          $level1Categories = [];
          $level2Categories = [];
          $level3Categories = [];
  
          foreach ($product->productFamily->level(1)->all() as $category) {
              $level1Categories[] = $this->traverseCategory($category);
          }
          foreach ($product->productFamily->level(2)->all() as $category) {
              $level2Categories[] = $this->traverseCategory($category);
          }
          foreach ($product->productFamily->level(3)->all() as $category) {
              $level3Categories[] = $this->traverseCategory($category);
          }        
  
          $ret = [
              'title' => $product->title,
              'url' => $product->url,
              'productDescription' => $product->productDescription,
              'image' => $this->getProductPrimaryImage($product), 
              'supplier' => $supplier,
              'productFamilies'   => [
                  'lvl0' => $level1Categories,
                  'lvl1' => $level2Categories,
                  'lvl2' => $level3Categories
              ]            
          ];
  
          // return data to algolia                
          return $ret; 
      }
  
      public function getProductPrimaryImage(\craft\commerce\elements\Product $product){
  
          // generate product image
          $srcImage = $product->productImage->one();
  
          // found any?
          $imageUrl = "";
  
          // get base URL of site 5
          $baseUrl = rtrim(UrlHelper::siteUrl("","","", 5),'/');
  
          if ($srcImage){
  
              // get focalpoint
              $focalPoint = $srcImage->getFocalPoint();
              if (!$focalPoint) {
                  $focalPoint = "50% 50%";
              }
  
              // transform with imager
              $image = Imager::getInstance()->imager->transformImage($srcImage, [
                  'width' => 400,
                  'format' => 'jpg',
                  'ratio' => 1/1,
                  'allowUpscale' => false,
                  'jpegQuality' => 90,
                  'position' => $focalPoint,
                  'interlace' => true
              ], null, null);
              $imageUrl = $baseUrl.$image->url;                            
          }
          else {
              $imageUrl = "https://via.placeholder.com/400x400.png?text=Placeholder";
          }  
          
          return $imageUrl;
  
      }
  }
  
  ```
  
  It gets called by `scout.php` thus:
  
  ```php
  'indices' => (new \modules\sitemodule\services\SiteModuleService)->getIndicies()
  
  ```
  
  
  ---
  
  