Skip to content

Product moderation

Gates vendor products behind an operator review before they can be sold. For how the workflow behaves — the states, who gets auto-approved, what makes a product purchasable — see Product authoring and moderation. This page is the wiring.

1. Add the interface and trait to your Product entity

This is part of the standard entity wiring, alongside VendorAwareInterface:

php
<?php
// src/Entity/Product.php

declare(strict_types=1);

namespace App\Entity;

use Odiseo\SyliusMarketplacePlugin\Entity\ProductModerationAwareInterface;
use Odiseo\SyliusMarketplacePlugin\Entity\ProductModerationTrait;
use Odiseo\SyliusVendorPlugin\Entity\VendorAwareInterface;
use Odiseo\SyliusVendorPlugin\Entity\VendorTrait;
use Sylius\Component\Core\Model\Product as BaseProduct;

class Product extends BaseProduct implements VendorAwareInterface, ProductModerationAwareInterface
{
    use VendorTrait;
    use ProductModerationTrait;
}

Without ProductModerationTrait products are never gated for approval.

2. Apply the schema

The moderation_state and moderation_reason columns are already part of the plugin's shipped migrations, so there is nothing to generate — just make sure they have been applied:

bash
php bin/console doctrine:migrations:migrate

If you already ran this during installation, you're done.

3. Shop catalogue filtering

If your ProductRepository combines Sylius core's with Odiseo\SyliusMarketplacePlugin\Repository\ProductRepositoryTrait (see entity wiring for the same "extend core + trait" pattern), the shop-facing queries already filter themselves — no extra work needed:

  • createShopListQueryBuilder
  • findOneByChannelAndSlug
  • findOneByChannelAndCode
  • findOneByChannelAndCodeWithAvailableAssociations
  • findLatestByChannel

Each of them excludes non-approved products, and products belonging to a non-approved, disabled or channel-less vendor.

These queries don't check onboarding

The SQL filter can't know whether a vendor finished the onboarding wizard, because a subplugin can register a step the core query has no way to evaluate. For a complete single-product check, use ProductIsPurchasableInterface — see Shop visibility.

by Odiseo