8

I'm using AngularJS one of my module's settings page, so I have a javascript object in client side as a model.

This is the model I assembled with Angular.

var model = {
  title: 'This is a title',
  url: '/random-custom-url',
  description: 'little descrp',
  blocks: [
    {id: 1, name: 'block1', description: 'blockdecrtiption1', terms: [{"tid":"1","name":"term1","vid":"tags"}]},
    {id: null, name: 'block2', description: 'blockdecrtiption2', terms: []},
    {id: 3, name: 'block3', description: 'blockdecrtiption3', terms: []}
  ]
};

I'd like to "catch" it in a controller's function (as a PHP associative array), so then I can process it and save to database. (I have some tables in the database, created with hook_schema()).

In the controller's method, how can I get the data posted with AJAX and convert it to an associative array?

Thanks a lot!

0

4 Answers 4

10

Do not create your own table; that's not the Drupal 8 way. Instead, create your own entity type. Introduction to Entity API in Drupal 8 can be a starting point to understand better the entity API.

The storage controller will generate the SQL schema for you and do all the storage work. The REST module will handle what you need.

For a generic case to receive and reply with JSON, first make sure there isn't a Drupal API for it. Likely there is. REST gives you entities, the Views module gives you lists of entities.

If there isn't an API you can use, you need to write a custom controller with the request as an argument by just adding Request $request to the method parameters. (See Parameters in routes for more.)

To respond with JSON, use any Response object, for example JsonResponse. See TimezoneController::getTimeZone() and the relevant routing entry in core/modules/system/system.routing.yml.

class MyController {

  public function myJson(Request $request) {
    $params = array();
    $content = $request->getContent();
    if (!empty($content)) {
      $params = json_decode($content, TRUE);
    }
    return new JsonResponse($params);
  }
}
0
13

In case you want to do it more the D8 way here is a rough outline what you could do, which allows you to be flexible without the requirement to do everything in your own. A proper rest integration requires you to work with all the different HTTP status codes, header fields etc. Rest takes partly care of that

The rest module provides a generic abstraction for REST, not tight to entities in the first place. Entities are just one example.

First you should register your rest plugin, like every other plugin:

namespace Drupal\pinguin\Plugin\rest\resource;

use Drupal\rest\Plugin\ResourceBase;

/**
 * Provides a resource for database watchdog log entries.
 *
 * @RestResource(
 *   id = "pinguin",
 *   label = @Translation("Example rest plugin for pinguins"),
 *   uri_paths = {
 *     "canonical" = "/pinguin/{id}"
 *   },
 *   serialization_class = "Drupal\pinguin\PingInterface"
 * )
 */
class Pinguin extends ResourceBase {
}

If you do an updating request like PUSH or UPDATE, it uses the serializer component to convert your data into some php array/ object. It uses two steps in order to do that. First the data incoming is decoded, for example from JSON into a PHP array. The next step is the denormalization. This step converts the denormalized data (PHP array) into a domain object, for example the Ping (a single pinguin).

The first step is to specify the resulting class in the annotation (see above). To register a new denormalization you add the following entry into your .services.yml:

services:
  pinguin.denormalizer.pinguin:
    class: Drupal\pinguin\normalizer\PinguinDenormalizer
    tags:
      - { name: normalizer }

This normalizer specifies which interface it can convert to:

<?php

namespace Drupal\pinguin\normalizer;

use \Drupal\serialization\Normalizer\NormalizerBase;

class PinguinDenormalizer extends NormalizerBase {

   // IMPORTANT
   /**
    * The interface or class that this Normalizer supports.
    *
    * @var array
    */
    protected $supportedInterfaceOrClass = array('Drupal\pinguin\PingInterface');

    /**
    * {@inheritdoc}
    */
    public function denormalize($data, $class, $format = NULL, array $context  = array()) {     
      // Validate the data.
      if (empty($data['location']) || $data['location'] == 'northpole') {
        throw new \UnexpectedValueException("this can't be real pinguins");
      }

      // create the domain object.
      return new Ping($data['name'], $data['location'], $data['family']);
    }
 }

Once this serialization is done, it calls a method named after the HTTP method on the plugin class, so for example "post". There you can do whatever you want.

<?php
class Pinguin extends ResourceBase {
  public function post(PingInterface $ping) {
    // deal with the data, save it for example.
    $ping_storage->save($ping):
  }
}

The advantages of these abstractions are that you don't have to deal with the incoming format but on the other hand it needs a bunch of code to get started.

1
  • I'm surprised how this is one of the very few examples howto consume data from an API with Drupal using the serializer service and a denormalizer. Even better, how about using a Typed Data definition to validate your date?
    – askibinski
    Aug 3, 2021 at 14:55
5

In the controller's method, how can I get the data posted with AJAX and convert it to an associative array?

use Drupal\Component\Serialization\Json;

class MyController {
  public function myPostAction(Request $request) {
    $params = Json::decode($request->getContent());
    //...
  }
}
0

Got this working for Drupal 9:

// Need to get POST params
// https://drupal.stackexchange.com/questions/207044/how-to-get-post-and-get-parameters
  $json_string = $request->request->get('json_string');
  $decoded = \Drupal\Component\Serialization\Json::decode($json_string);
  foreach ($decoded as $product) {
    print ($product['endTimeVal']);
    // etc.
  }

and the AJAX:

jQuery.post("/neway/addtocart", {json_string: JSON.stringify(cartItems)})
  .done(function () {
    console.log("success - reload (and reset) the page");
    // window.location.reload();
  })
  .fail(function () {
    console.log("error");
  })
  .always(function () {
    console.log("finished");
  });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.