Question

I have some logic to apply after getting entities from database( by findAll() ) and before serializing the result to json.

I want to add translation on some fields. I know that I can do it manually by iterating on each entity and apply my logic in controller. But I need a better way to do it.

Is there a suggestions to make this automatic ?

Was it helpful?

Solution

I've got similar problem and tried to resolve this using custom handler but with no success, so i created compiler pass and override JsonSerializationVisitor where string values are serialized with TranslatableJsonSerializationVisitor class:

namespace tkuska\DemoBundle\Serialization;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Context; 

class TranslatableJsonSerializationVisitor extends JsonSerializationVisitor
{
    /**
     * @var \Symfony\Component\Translation\Translator;
     */
    private $translator;

    public function visitString($data, array $type, Context $context)
    {
        if (in_array('translatable', $type['params'])) {
            return (string) $this->translator->trans($data);
        }
        return (string) $data;
    }

    public function setTranslator(\Symfony\Component\Translation\Translator $translator)
    {
        $this->translator = $translator;
    }
}

and compiler:

namespace tkuska\DemoBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class TranslatableJsonSerializationCompiler implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $jmsJsonSerializationVisitor = $container->getDefinition('jms_serializer.json_serialization_visitor');
        $jmsJsonSerializationVisitor->setClass('tkuska\DemoBundle\Serialization\TranslatableJsonSerializationVisitor');

        $jmsJsonSerializationVisitor->addMethodCall('setTranslator', array(new Reference('translator')));
    }
}

in entity i set annotation for type 'string' with param 'translatable'

/**
 * @VirtualProperty
 * @Groups({"details"})
 * @Type("string<'translatable'>")
 * @return order status
 */
public function getOrderStatus(){
    return 'status.ordered';
}

Of course 'status.ordered' is my translation key.

OTHER TIPS

Thank you @zizoujab. Very useful post. I made a small improvement to it to call parent method and to unset my parameters, so that i can use this way of altering data on more complex types like array, that already have parameters and used

/**
 * @VirtualProperty
 * @Groups({"details"})
 * @Type("string<translatable>")
 * @return order status
 */

instead of

/**
 * @VirtualProperty
 * @Groups({"details"})
 * @Type("string<'translatable'>")
 * @return order status
 */

to convert the string 'translatable' into a parameter name instead of parameter value thus you can pass more complex parameters like this, and allow me to unset this parameters before calling the parent method.

/**
 * @VirtualProperty
 * @Groups({"details"})
 * @Type("string<translatable<set of parameters>>")
 * @return order status
 */

Code:

<?php

namespace Mktp\DefaultBundle\Service\JmsSerializer;

use JMS\Serializer\Context;
use JMS\Serializer\JsonSerializationVisitor;
use Symfony\Component\Translation\Translator;

class TranslatableJsonSerializationVisitor extends JsonSerializationVisitor
{
    /**
     * @var Translator;
     */
    private $translator;

    /**
     * @param string $data
     * @param array $type
     * @param Context $context
     * @return string
     */
    public function visitString($data, array $type, Context $context)
    {
        $translatable = $this->getParameters('translatable', $type['params']);
        if (count($translatable)) {
            $data = (string)$this->translator->trans($data);
        }
        return parent::visitString($data, $type, $context);
    }

    /**
     * @param array $data
     * @param array $type
     * @param Context $context
     * @return array|\ArrayObject|mixed
     */
    public function visitArray($data, array $type, Context $context)
    {
        $translatable = $this->getParameters('translatable', $type['params']);

        if (count($translatable)) {
            foreach ($data as $key => $value) {
                if (is_string($value)) {
                    $data[$key] = (string)$this->translator->trans($value);
                }
            }

        }
        return parent::visitArray($data, $type, $context);
    }

    /**
     * @param Translator $translator
     */
    public function setTranslator(Translator $translator)
    {
        $this->translator = $translator;
    }

    /**
     * @param string $type
     * @param array $parameters
     * @param bool $unsetParameters
     * @return array
     */
    protected function getParameters($type, &$parameters, $unsetParameters = true)
    {
        $result = array();
        foreach ($parameters as $key => $parameter) {
            if ($parameter['name'] == $type) {
                $result[] = $parameter;
                if ($unsetParameters) {
                    unset($parameters[$key]);
                }
            }
        }
        $parameters = array_values($parameters);
        return $result;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top