<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>

CC BY-SA 4.0
2019-05-19T13:13:53.957Z
2019-05-19T13:13:53.957Z
Fred Carlsen
<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>

CC BY-SA 4.0
2019-05-17T19:17:33.837Z
2019-05-17T19:17:33.837Z
Lindsey D
<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>

CC BY-SA 4.0
2020-09-30T11:39:03.783Z
2020-09-30T11:39:03.783Z
nitech
<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>

<craft3><plugins>
How do I iterate over a categories field in an entry
# 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:
'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:
 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.
$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 Fred Carsen's approach, but it didn't quite output what I expected. Here's my approach:
// 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:
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:
'indices' => (new \modules\sitemodule\services\SiteModuleService)->getIndicies()


---

