1

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(),
    ];
}
2

Try this approach:

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

            ],
        ];
    },
];```
| improve this answer | |
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.

| improve this answer | |
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:

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

'indices' => (new \modules\sitemodule\services\SiteModuleService)->getIndicies()
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.