0

I'm trying to build a dynamic text field with AJAX autocomplete.

I defined a method into the controller which is used for the AJAX call.

public function cityAction(Request $request)
{
    $repository = $this->getDoctrine()
        ->getRepository('UserCityBundle:District');

    $items = $repository->findAll();

// $format = $request->getRequestFormat();
// \Doctrine\Common\Util\Debug::dump($items);

    return $this->render('CommonAjaxBundle:Default:index.html.twig', array('data' => array(
        'success' => true,
        'root' => 'district',
        'count' => sizeof($items),
        'rows' => $items
    )));
}

Into the twig file:

{{ data | json_encode | raw }}

I took that from an example of how make an ajax call in Symfony2. It should print a json encode of my District entity repository but i got this result:

{"success":true,"root":"district","count":6,"rows":[{},{},{},{},{},{}]} 

Why it doesn't print the fields between the brackets ?

Roberto Rizzi
  • 1,525
  • 5
  • 26
  • 39
  • You've tried to see what's inside items? Most likely you will need to create a loop in the template to extract the data that you need from the object items. As advice.. why not use a template `.json.html` directly? You can see an example in my reply here: http://stackoverflow.com/a/17914189/2036211 – Lughino Sep 24 '13 at 16:30

2 Answers2

3

You can serialize your entities by the mean of the serializer component (see http://symfony.com/doc/current/components/serializer.html).

To serialize your entity, you can either use the GetSetMethodNormalizer class described in the above link or you can create a normalizer and declare it as a new service tagged as serializer.normalizer in your bundle's service definition file (Resource/config/service.yml if you use the yaml configuration).

parameters:
    common_ajax_bundle.district.normalizer.class: Full\Path\To\Your\normalizer\Class

common_ajax_bundle.district.normalizer:
    class: '%common_ajax_bundle.district.normalizer.class%'
    tags:
        - { name: serializer.normalizer }

The normalizer class can extend the Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer class. Put all the fields to ignore in the constructor via the method $this->setIgnoredAttributes(array(/* the fields to ignore */)) or you can create it from scratch, it just have to implement the Symfony\Component\Serializer\Normalizer\NormalizerInterface interface.

Then in you controller:

public function cityAction(Request $request)
{
    $repository = $this->getDoctrine()
        ->getRepository('UserCityBundle:District');

    $items = $repository->findAll();

    $serializer = $this->container->get('serializer');

    // serialize all of your entities
    $serializedCities = array();
    foreach ($items as $city) {
        $serializedCities[] = $serializer->normalize($city, 'json');
    }

    return new JsonResponse(array(
        'success' => true,
        'root' => 'district',
        'count' => sizeof($items),
        'rows' => $serializedCities
    ));
}
bjuice
  • 291
  • 1
  • 8
  • @RobertoRizzi I have edited my answer to be a bit more specific. – bjuice Sep 25 '13 at 08:49
  • I sow it. Thank you :) I got this error `InvalidArgumentException: There is no extension able to load the configuration for "common_ajax_bundle.district.normalizer" (in /var/www/examples/test4/www/src/Common/AjaxBundle/DependencyInjection/../Resources/config/services.yml). Looked for namespace "common_ajax_bundle.district.normalizer", found none` What it means? I'm so sorry but it's my first json ajax call in symfony2 and i haven't known very well how it works. Moreover i'm a newbie with it. – Roberto Rizzi Sep 25 '13 at 09:00
  • @RobertoRizzi You have to create a normalizer class. %common_ajax_bundle.district.normalizer% is a parameter you have to define in your service configuration file. btw, I have updated my answer. – bjuice Sep 25 '13 at 11:55
1

The data from an entity are private, so it's not accessible from json_encode. You must use a serialiser. Lot of people recommend to use JMSSerializerBundle. You could also use the following code:

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;

$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new 
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');

In my case, I build a twig function in order to return a limited number of data (also to be able to rename the key attribute):

namespace Company\EgBundle\Services;

class TwigExtension extends \Twig_Extension
{
    public function getFilters()
    {
        return array(
            new \Twig_SimpleFilter('encode_entity', array($this, 'encodeEntity')),
        );
    }

    public function encodeEntity($entity, $filter = null)
    {
        $data = [];
        $methods = get_class_methods($entity); 
        if ($filter) {
            $methods = array_intersect($methods, array_keys($filter));
        }
        foreach ($methods as $method) {
            if (strpos($method, 'get') === 0) {
                $value = $entity;
                while(is_object($value)) {
                    $value = $value->$method();
                }
                $key = $filter ? $filter[$method] : substr($method, 3);
                $data[$key] = $value;
            }
        }
        return json_encode($data);
    }
}

And to use this function in the template do:

{{ row|encode_entity({'getProductName': 'name', 'getIdIsoCry': 'currency'}) }}
Alexandre
  • 3,088
  • 3
  • 34
  • 53