Catalog Management

Products

Products

How do I remove the manufacturer from the product slug/url?

The manufacturer name is automatically prepended to the product slug (its URL). If you wish for this to not happen you can set the static Aero\Catalog\Models\Product::$slugContainsManufacturer boolean to false:

\Aero\Catalog\Models\Product::$slugContainsManufacturer = false;

 

Listing Rules

Listing Rules

What is the listing rules context builder?

The Listing Rules Context Builder is used to build the rule-specific context that listing rules need when being displayed in the Admin UI.

For example, the \Aero\Admin\Pipelines\Pipes\AttachTaggedWithRuleContext pipe attaches the hydrated tags data for the \Aero\Catalog\Rules\Types\TaggedWithRule rule:

class AttachTaggedWithRuleContext
{
    public function handle(\Aero\Admin\Pipelines\ListingRulesContextBuilder $pipeline, \Closure $next)
    {
        $tagIds = $pipeline->rules
            ->where('type', \Aero\Catalog\Rules\Types\TaggedWithRule::KEY)
            ->flatMap(function (ListingRule $rule) {
                return collect($rule->options['filters'] ?? [])->flatten()->toArray();
            })
            ->unique();

        $pipeline->data[\Aero\Catalog\Rules\Types\TaggedWithRule::KEY] = [
            'tags' => $tagIds->isNotEmpty()
                ? \Aero\Catalog\Models\Tag::query()
                    ->select('id', 'tag_group_id', 'name')
                    ->with('group')
                    ->whereIn('id', $tagIds)->get()
                    ->mapWithKeys(function (\Aero\Catalog\Models\Tag $tag) {
                        $id = $tag->getKey();

                        return [
                            $id => [
                                'value' => (string) $id,
                                'name' => $tag->getAttribute('name'),
                                'group_id' => (string) $tag->getRelation('group')->getKey(),
                                'group' => $tag->getRelation('group')->getAttribute('name'),
                            ],
                        ];
                    })
                    ->toArray()
                : [],
        ];

        return $next($pipeline);
    }
}

Parameters

Parameter Type Description
$pipeline \Aero\Admin\Pipelines\ListingRulesContextBuilder The listing rules context builder.
$next \Closure The next step in the builder.

Attributes

Attribute
Type
Description
$rules
\Illuminate\Support\Collection All the rules corresponding to the model (that is being viewed in the Admin).
$data
array
The array of data the rules need (for display purposes only).

Listing Rules

What is the listing rules data formatter pipeline?

The Listing Rules Data Formatter is used to format the listing rules data into an array for the Admin UI page.

For example, the \Aero\Admin\Pipelines\Pipes\AttachListingRuleAttributes attaches the attributes to the array:

class AttachListingRuleAttributes
{
    public function handle(\Aero\Admin\Pipelines\ListingRulesDataFormatter $pipeline, Closure $next)
    {
        $pipeline->data['id'] = $pipeline->rule->getKey();

        $pipeline->data['type'] = $pipeline->rule->getAttribute('type');

        $pipeline->data['rule_logic'] = $pipeline->rule->getAttribute('rule_logic') ?? 'must';

        $pipeline->data['logic'] = $pipeline->rule->getAttribute('logic') ?? 'and';

        $pipeline->data['group'] = $pipeline->rule->getAttribute('group') ?? 0;

        $pipeline->data['options'] = $pipeline->rule->getAttribute('options') ?? [];

        return $next($pipeline);
    }
}

The formatter also assigns the context built by the ListingRulesContextBuilder as view data to the rule.


Parameters

Parameter Type Description
$pipeline \Aero\Admin\Pipelines\ListingRulesDataFormatter The listing rules data formatter.
$next \Closure The next step in the formatter.

Attributes

Attribute
Type
Description
$rule
\Aero\Catalog\Models\ListingRule The listing rule being formatted.
$data
array
The array of formatted data for the Admin UI.
Listing Rules

How can I add a custom listing rule?

To add a custom listing rule you need to create a class that extends Aero\Catalog\Rules\ListingRule\AttributedWithRule.

For example we will add a rule that checks if the listing has specific attributes.

First, we need to index the attribute ids so that they can be queried easily:

\Aero\Search\Elastic\Documents\ListingDocument::add('search-result', function (\Aero\Search\Elastic\Documents\ListingDocument $document) {
    return [
        'attribute_ids' => $document->getModel()->attributes->map(function (\Aero\Catalog\Models\Attribute $attribute) {
            return (string) $attribute->getKey();
        }),
    ];
});

Now, we can make the rule:

class AttributedWithRule extends \Aero\Catalog\Rules\ListingRule
{
    public const KEY = 'attributes';

    public string $name = 'have one of these attributes';

    public string $view = 'acme::attributes';

    protected array $options = [
        'attributes' => [],
    ];

    public function viewData(array $context): array
    {
        return [
            'attributes' => collect($this->options['attributes'] ?? [])
                ->flatten()
                ->map(function ($id) use ($context) {
                    return $context['attributes'][$id] ?? null;
                })
                ->toArray(),
        ];
    }

    public function apply(\Aero\Search\Contracts\SearchBuilder $builder): ?\Elastica\Query\BoolQuery
    {
        $attributeIds = $this->options['attributes'] ?? [];

        if (! is_array($attributeIds)) {
            return null;
        }

        return (new BoolQuery())->addMust(
            new \Elastica\Query\Terms('search-result.attribute_ids', $attributeIds),
        );
    }

    public function optionRules(): array
    {
        return [
            'attributes' => 'required|array',
            'attributes.*' => 'required|distinct|int|exists:attributes,id',
        ];
    }
}

For the UI to work we must build the context for our rule:

\Aero\Admin\Pipelines\ListingRulesContextBuilder::extend(function (\Aero\Admin\Pipelines\ListingRulesContextBuilder $pipeline, \Closure $next) {
    $attributeIds = $pipeline->rules
        ->where('type', \Aerocargo\Acme\AttributedWithRule::KEY)
        ->flatMap(function (\Aero\Catalog\Models\ListingRule $rule) {
            return collect($rule->options['attributes'] ?? [])->flatten()->toArray();
        })
        ->unique();

    $pipeline->data[\Aerocargo\Acme\AttributedWithRule::KEY] = [
        'attributes' => $attributeIds->isNotEmpty()
            ? Attribute::query()
                ->select('id', 'name')
                ->whereIn('id', $attributeIds)->get()
                ->mapWithKeys(function (\Aero\Catalog\Models\Attribute $attribute) {
                    $id = $attribute->getKey();

                    return [
                        $id => [
                            'value' => (string) $id,
                            'name' => $attribute->getAttribute('name'),
                        ]
                    ];
                })
                ->toArray()
            : [],
    ];

    return $next($pipeline);
});

And add the view:

<template v-for="attribute in builder.rule.view_data.attributes ?? []">
    <input type="hidden" :name="{{ $name }} + '[attributes][]'" :value="attribute.value">
</template>

<label :for="{{ $id }} + '-attributes-option'">Attributes</label>
<searchable-select :id="{{ $id }} + '-attributes-option'" class="mt-2 w-full"
                   :input-name="'rules[' + builder.group_index + '][' + builder.rule_index + '][view_data][attributes]'"
                   url="{{ route('admin.catalog.attributes.search') }}" track-by="value" label="name" :multiple="true"
                   v-model="builder.rule.view_data.attributes"></searchable-select>