There is no license information available for the latest version (8.47.1) of this package.

A generic bundle for reviewing things (e.g. products or articles)

8.47.1 2025-12-15 13:54 UTC

README

Allows for reviewing arbitrary things. In order to review something, one has to create a review_configuration in the backend, consisting of a ReviewTargetHandler (handles what should be reviewed), a PublicationStrategy (how a review should be published) and some optional rendering information.

This bundle exposes 3 endpoints:

  • GET /api/reviews/{configurationId}/{reviewTargetId} lists existing reviews
    • Use Accept: application/json header to get the information as JSON
  • POST /api/reviews/{configurationId}/{reviewTargetId} creates a new review
  • GET /api/reviews/{configurationId}/publication-hook/{encryptedArguments}
    • Calls the handlePublicationHook method on the publication strategy of the given configuration. Useful in order to delay publication of a review (e.g. by sending a 'confirm' link to an admin)

In the URLs above, {configurationId} is the id of the review_configuration record created in the backed. {reviewTargetId} can be an arbitrary string that is a valid review target according to the configured ReviewTargetHandler.

A publication strategy can create hook urls by using PublicationStrategyHookService

Including Reviews

The bundle is built in a way to maximize flexibility when including reviews. For this reason, multiple ways of including reviews on multiple different levels of abstraction exist:

1. As a template module in a module spot

TODO (Does not exist yet)

2. As a template include

In the template for your review target, you can include ChameleonSystemReview/include.html.twig and pass it a configurationId and targetId in order to render the reviews there.

{% include 'ChameleonSystemReview/include.html.twig' with {
    'configurationId': '89fa5164-3e28-454e-a296-d43c606afed2',
    'targetId': product.id
} %}

Behind the scenes, this template does very little: It defines a single div and loads some javascript that will asynchronously fetch the reviews. As such, this should not be impacted by caching.

3. Custom rendering

If you have a more complex use case (e.g. integrating reviews into a larger AJAX response), then you can use ReviewService::renderAsHtml in order to get the rendered HTML. After inserting the rendered HTML into the DOM, be sure to fire the chameleon_system_review.inserted event on the container in order to initialize the form correctly.

<!-- Ensure this script is loaded somewhere -->
<script
    src="/assets/bundles/chameleonsystemreview/main.js"
    type="module"
></script>

<script>
    fetchMoreInvolvedData(html => {
        const container = document.querySelector('#container');
        container.innerHTML = html;
        container.dispatchEvent(new Event(chameleon_system_review.inserted', {
            bubbles: true
        }))
    })
</script>

This method can also be used in order to statically embed reviews into the template.

4. Manually via JSON API

If you want full custom rendering, as may be the case in a single page application (SPA), you can fetch reviews from the /api/reviews/{configurationId}/{reviewTargetId} using a GET request and submit reviews using a POST request.

  • When you specify the Accept: application/json header, the endpoint will respond with JSON
  • When you specify the Content-Type: application/json header, the POST endpoint will accept a JSON body

Make sure you specify credentials: 'same-origin' when submitting the data in order to ensure that the session cookie is transmitted with the request. It is required to validate that a user is logged in.

Extending

Adding review target handler

In order to add a target handler, implement the ReviewTargetHandlerInterface and tag the resulting service with chameleon_system_review.review_target_handler in the dependency injection container. Additionally, a label and help may be specified while tagging.

When implementing a target handler that is based on database contents, the AbstractDatabaseReviewTargetHandler may be a good base class to use.

Implementation:

class BandTargetHandler implements ReviewTargetHandlerInterface {
    public function isIdentifierValid(string $identifier): bool
    {
        return null !== $this->getReviewTarget($identifier);
    }

    public function getReviewTarget(string $identifier)
    {
        return $this->bandApi->get($identifier);
    }
}

Registering:

<service
    id="esono_customer.review.band_target_handler"
    class="Esono\Customer\Review\BandTargetHandler"
>
    <!--
        Translations for label & help can be defined in
        @YourBundle/Resources/translations/admin.{LANG}.xlf
    -->
    <tag
        name="chameleon_system_review.review_target_handler"
        label="esono_customer.review.band_target_handler.label"
        help="esono_customer.review.band_target_handler.help"
    />
</service>

Adding a publication strategy

In order to add a publication strategy, implement the PublicationStrategyInterface and tag the resulting service with chameleon_system_review.publication_strategy in the dependency injection container. Additionally, a label and help may be specified while tagging.

class NotifyModeratorViaTelegram implements PublicationStrategyInterface {
    public function handleNewReview(Configuration $configuration, ReviewDataModelInterface $review): void
    {
        $review->setPublic(true);

        $deleteUrl = $this->hookService->generatePublicationStrategyHookUrl($configuration, [
            'action' => 'delete',
            'review' => $review->getId(),
        ]);

        $this->telegramService->sendToModerator(sprintf(
            "New review: \n%s\n\n Delete: %s\nn",
            $review->getText(),
            $deleteUrl
        ));
    }

    public function handlePublicationHook(array $arguments): string
    {
        switch ($arguments['action']) {
            case 'delete':
                $this->dataAccess->deleteReview($arguments['review']);
                break;

             default:
                throw new PublicationHookNotAllowedException('Cannot handle hook');
        }
    }
}
<service
    id="esono_customer.review.notify_moderator_via_telegram"
    class="Esono\Customer\Review\NotifyModeratorViaTelegram"
>
    <!--
        Translations for label & help can be defined in
        @YourBundle/Resources/translations/admin.{LANG}.xlf
    -->
    <tag
        name="chameleon_system_review.publication_strategy"
        label="esono_customer.review.notify_moderator_via_telegram.label"
        help="esono_customer.review.notify_moderator_via_telegram.help"
    />
</service>

When implementing a publication strategy that accepts hooks, the PublicationStrategyHookService may be of interest in order to generate hook URLs.