pieces of the api puzzle: leveraging typed data...
TRANSCRIPT
![Page 1: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/1.jpg)
Pieces of the API Puzzle: Leveraging Typed Data, Serializer, and Guzzle in
Drupal 8 Matthew Radcliffe
mradcliffe @matt-kineme
![Page 2: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/2.jpg)
Summary
• Guzzle
• Typed Data
• Serializer
![Page 3: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/3.jpg)
Guzzle
“Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services.”
![Page 4: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/4.jpg)
Curl
• Procedural
• Not extendable
• Simple error handling
![Page 5: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/5.jpg)
// Make sure our URL is safe. $url = 'http://api.example.com?key=' . urlencode('value'); !// Init curl to get a curl handle. $ch = curl_init($url); !// Set some boiler plate options. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); !// Execute curl. $result = curl_exec($ch); !// Check for errors. if (curl_errorno($ch)) { $error = curl_error($ch); throw new Exception('Error: ' . htmlentities($error)); } !// Do something with the result.
![Page 6: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/6.jpg)
if ( (count($arguments) == 0) || ( is_string($arguments[0]) ) || ( is_numeric($arguments[0]) ) || ( $arguments[0] === false ) ) { $where = false; //it's a GET request if ( !in_array($name, $valid_get_methods) ) { return false; } $filterid = ( count($arguments) > 0 ) ? strip_tags(strval($arguments[0])) : false; if(isset($arguments[1])) $modified_after = ( count($arguments) > 1 ) ? str_replace( 'X','T', date( 'Y-m-dXH:i:s', strtotime($arguments[1])) ) : false; if(isset($arguments[2])) $where = ( count($arguments) > 2 ) ? $arguments[2] : false; if ( is_array($where) && (count($where) > 0) ) { $temp_where = ''; foreach ( $where as $wf => $wv ) { if ( is_bool($wv) ) { $wv = ( $wv ) ? "%3d%3dtrue" : "%3d%3dfalse"; } else if ( is_array($wv) ) { if ( is_bool($wv[1]) ) { $wv = ($wv[1]) ? rawurlencode($wv[0]) . "true" : rawurlencode($wv[0]) . "false" ; } else { $wv = rawurlencode($wv[0]) . "%22{$wv[1]}%22" ; } } else { $wv = "%3d%3d%22$wv%22"; } $temp_where .= "%26%26$wf$wv"; } $where = strip_tags(substr($temp_where, 6)); } else { $where = strip_tags(strval($where)); }
![Page 7: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/7.jpg)
drupal_http_request()
• Procedural
• Provides an options argument
• Not extendable
• Simple error handling
![Page 8: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/8.jpg)
$options = array( 'headers' => array( 'Accept' => 'application/xml', ), 'method' => 'GET', 'data' => 'key=' . urlencode('value'), ); !$result = drupal_http_request('http://example.com', $options); !// Handle error if ($result['code'] < 200 || $result['code'] > 300) { throw new Exception('Error: ' . htmlentities($result['error'])); } !// Do something with $result['data'].
![Page 9: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/9.jpg)
Guzzle• Class-based, Namespace
• OAuth support, JSON/XML parsing.
• Extendable
• Throws catchable exceptions
• Guzzle 4+:
• Use different HTTP adapter than Curl.
• Asynchronous requestsGuzzle 3 Documentation. http://guzzle3.readthedocs.org/docs.html
![Page 10: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/10.jpg)
Guzzle 3$options = array( 'headers' => array( 'Accept' => 'application/xml', ), 'query' => array( 'key' => 'value', ), ); !$client = new Guzzle\Http\Client($options); !try { $response = $client->get('http://example.com'); // Do something with the response. $xml = $response->xml(); } catch (Guzzle\Http\Exception\BadResponseException $e) { // Handle 400 and 500 level errors }
![Page 11: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/11.jpg)
Guzzle 4$options = array( 'headers' => array( 'Accept' => 'application/xml', ), 'query' => array( 'key' => 'value', ), ); !$client = new GuzzleHttp\Client('http://example.com', $options); !try { $response = $client->get('something'); // Do something with the response. $xml = $response->xml(); } catch (GuzzleHttp\Exception\RequestException $e) { // Handle errors in making request. } catch (GuzzleHttp\Exception\ResponseException $e) { // Handle errors from the response. }
![Page 12: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/12.jpg)
Typed Data• Plugin
• Data type
• Data definition
• Entities are typed data.
• Needlessly complicated?
![Page 13: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/13.jpg)
<?php !$node = \Drupal\node\Entity\Node::load($nid); !$author_name = $node->uid->referencedentities()[0]->get('name')[0]->getValue();
![Page 14: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/14.jpg)
Plugin• Annotation-based
• A plugin instance is loaded via a plugin manager i.e. Block Manager or Entity Manager. Field types, widgets, and formatters.
• Drupal\module\Plugin\(Plugin type) namespace.
• Common operations that all of those things inherit.
![Page 15: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/15.jpg)
/** * * @FieldType( * id = "uri", * label = @Translation("URI"), * description = @Translation("An entity field containing a URI."), * no_ui = TRUE, * default_formatter = "uri_link", * ) */ class UriItem extends StringItem {}
plugin type
plugin id plugin label
![Page 16: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/16.jpg)
Data Type Plugin• @DataType
• Description
• (optional) Definition class
• (optional) List class, List definition class
• (optional) Primitive
• (optional) Constraints
![Page 17: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/17.jpg)
Primitive Data Type• string
• integer, float
• boolean
• binary
• uri
![Page 18: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/18.jpg)
namespace Drupal\Core\TypedData\Plugin\DataType; !use Drupal\Core\TypedData\PrimitiveBase; use Drupal\Core\TypedData\Type\BooleanInterface; !/** * The boolean data type. * * @DataType( * id = "boolean", * label = @Translation("Boolean") * ) */ class Boolean extends PrimitiveBase implements BooleanInterface { ! /** * {@inheritdoc} */ public function getCastedValue() { return (bool) $this->value; } }
![Page 19: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/19.jpg)
Complex Data Type• Lists
• Datetime, timestamp, etc…
• E-mail, URI
• Map
• Nodes, Users, Taxonomy terms, etc…
![Page 20: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/20.jpg)
Map• The only complex data type that is not an entity
in core.
• Basically an associative array in object form.
• No storage implications like entities.
• Requires data definition to describe properties much like entities i.e. getFieldDefinitions() or getPropertyDefinitions().
![Page 21: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/21.jpg)
<?php namespace Drupal\xero\Plugin\DataType; !/** * Xero Payment type. * * @DataType( * id = "xero_payment", * label = @Translation("Xero Payment"), * definition_class = "\Drupal\xero\TypedData\Definition\PaymentDefinition" * ) */ class Payment extends XeroTypeBase { protected function getPropertyDefinitions() { return $this->definition->getPropertyDefinitions(); } }
![Page 22: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/22.jpg)
Data Definition• How to handle data types
• create
• constraints
• property definitions
• Typed Data Manager creates data type instances based on data definition for complex typed data.
![Page 23: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/23.jpg)
class PaymentDefinition extends ComplexDataDefinitionBase { public function getPropertyDefinitions() { if (!isset($this->propertyDefinitions)) { $info = &$this->propertyDefinitions; ! $info['Invoice'] = DataDefinition::create(‘xero_invoice')->setRequired(TRUE)->setLabel('Invoice'); ! $info['Account'] = DataDefinition::create(‘xero_account')->setRequired(TRUE)->setLabel('Account'); ! $info['Date'] = DataDefinition::create(‘string')->setRequired(TRUE)->setLabel('Date'); ! $info['Amount'] = DataDefinition::create(‘float')->setRequired(TRUE)->setLabel('Amount'); ! $info['CurrencyRate'] = DataDefinition::create('string')->setLabel('Currency rate’); ! $info['Reference'] = DataDefinition::create(‘string')->setLabel('Reference'); ! $info['IsReconciled'] = DataDefinition::create('boolean')->setLabel('Is reconciled?'); } return $this->propertyDefinitions; } }
![Page 24: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/24.jpg)
<?php !use Drupal\xero\TypedData\Definition\PaymentDefinition; !$typedDataManager = \Drupal::typedDataManager(); $payment_definition = PaymentDefinition::create('xero_payment'); !// You may also set values when creating data type plugin instances. Map // complex data type allows for sending in associative arrays. $payment = $typeDataManager::createInstance($payment_definition); !$payment->set('Amount', '10.00');
![Page 25: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/25.jpg)
Constraints
• Symfony 2 constraints
• Allowed Values is a constraint that extends the Choice constraint.
![Page 26: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/26.jpg)
Serializer• Symfony 2 component
• Abstracts the idea of serialization into normalizing and encoding.
• Supports JSON and XML out of the box.
• Extendable
• Easy to use despite complexity.
![Page 27: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/27.jpg)
Normalizer• Supports normalize and denormalize methods.
• Any additional data processing before encoding (hal+json, jsonld, etc…)
• Drupal provides ComplexTypeNormalizer, ContentEntityNormalizer, and a primitive normalizer amongst a few others.
![Page 28: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/28.jpg)
namespace Drupal\serialization\Normalizer; !use Symfony\Component\Serializer\Exception\RuntimeException; !class ComplexDataNormalizer extends NormalizerBase { ! protected $supportedInterfaceOrClass = 'Drupal\Core\TypedData\ComplexDataInterface'; ! public function normalize($object, $format = NULL, array $context = array()) { $attributes = array(); foreach ($object as $name => $field) { $attributes[$name] = $this->serializer->normalize($field, $format); } return $attributes; } }
![Page 29: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/29.jpg)
Normalized User
<?php !array( 'uid' => array(array('value' => 1)), 'name' => array(array('value' => 'admin')), 'pass' => array(array('value' => 'hashedpasswordsecurityvulnerability')), // and so on... );
![Page 30: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/30.jpg)
Encoder
• Take normalized data and encode in a given format.
• Take data in given format, and return normalized data.
![Page 31: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/31.jpg)
namespace Symfony\Component\Serializer\Encoder; !class JsonEncoder implements EncoderInterface, DecoderInterface { const FORMAT = 'json'; protected $encodingImpl; protected $decodingImpl; ! public function encode($data, $format, array $context = array()) { return $this->encodingImpl->encode($data, self::FORMAT, $context); } ! public function decode($data, $format, array $context = array()) { return $this->decodingImpl->decode($data, self::FORMAT, $context); } }
if } ! return }
![Page 32: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/32.jpg)
namespace Symfony\Component\Serializer\Encoder; !class JsonEncoder implements EncoderInterface, DecoderInterface { const FORMAT = 'json'; protected $encodingImpl; protected $decodingImpl; ! public function encode($data, $format, array $context = array()) { return $this->encodingImpl->encode($data, self::FORMAT, $context); } ! public function decode($data, $format, array $context = array()) { return $this->decodingImpl->decode($data, self::FORMAT, $context); } }
! public function encode($data, $format, array $context = array()) { $context = $this->resolveContext($context); ! $encodedJson = json_encode($data, $context['json_encode_options']); ! if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) { throw new UnexpectedValueException(JsonEncoder::getLastErrorMessage()); } ! return $encodedJson; }
![Page 33: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/33.jpg)
Using Serializer
<?php !$node = \Drupal\node\Entity\Node::load($nid); !$context = array('plugin_id' => 'node'); $my_json = $serializer->serialize($node, 'json', $context);
![Page 34: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/34.jpg)
Putting it all together• Typed Data describes entities, primitives, etc…
with getter/setter, constraints, etc…
• Serializer normalizes typed data and encodes it into a format.
• Guzzle makes a request with the serialized data as a payload.
• Or vice versa.
![Page 35: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/35.jpg)
services: xero.client.factory: class: Drupal\xero\XeroClientFactory xero.client: class: BlackOptic\Bundle\XeroBundle\XeroClient factory_method: get factory_service: xero.client.factory arguments: ['@config.factory', '@logger.factory'] xero.normalizer: class: Drupal\xero\Normalizer\XeroNormalizer arguments: ['@typed_data_manager'] tags: - { name: normalizer } xero.query: class: Drupal\xero\XeroQuery arguments: ['@xero.client', '@serializer', '@typed_data_manager', '@logger.factory']
![Page 36: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/36.jpg)
<?php /** * @file * Contains \Drupal\xero\Form\SettingsForm. */ !namespace Drupal\xero\Form; !use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Serializer\Serializer; use Drupal\xero\XeroQuery; use Guzzle\Http\Exception\RequestException; use Guzzle\Http\Exception\ClientErrorResponseException; use Drupal\Core\Logger\LoggerChannelFactoryInterface; !/** * Provide configuration form for user to provide Xero API information for a * private application. */ class SettingsForm extends ConfigFormBase implements ContainerInjectionInterface { ! protected $query = FALSE; ! /** * Inject dependencies into the form except for XeroClient because we want to * handle errors properly instead of failing and exploding spectacularly. *
![Page 37: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/37.jpg)
/** * Inject dependencies into the form except for XeroClient because we want to * handle errors properly instead of failing and exploding spectacularly. * * @param $config_factory * Configuration factory interface. * @param $query * An instance of XeroClient or NULL if it fails, which is most likely the * case on first load. * @param $serializer * Serializer object. */ public function __construct(ConfigFactoryInterface $config_factory, $query, Serializer $serializer, LoggerChannelFactoryInterface $logger_factory) { $this->setConfigFactory($config_factory); $this->query = $query; $this->serializer = $serializer; $this->logger = $logger_factory->get('xero'); } ! /** * {@inheritdoc} */ public function getFormId() { return 'xero_configuration_form'; } ! /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) {
![Page 38: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/38.jpg)
public function buildForm(array $form, FormStateInterface $form_state) { ! // Get the configuration from ConfigFormBase::config(). $config = self::config('xero.settings'); ! // Open the fieldset if there was a problem loading the library. $form['oauth'] = array( '#type' => 'fieldset', '#title' => t('Xero Configuration'), '#collapsible' => TRUE, '#collapsed' => $this->query->client !== FALSE, '#tree' => TRUE, ); ! $form['oauth']['consumer_key'] = array( '#type' => 'textfield', '#title' => t('Xero Consumer Key'), '#description' => t('Provide the consumer key for your private application on xero.'), '#default_value' => $config->get('oauth.consumer_key'), '#required' => TRUE, ); ! $form['oauth']['consumer_secret'] = array( '#type' => 'textfield', '#title' => t('Xero Consumer Secret'), '#description' => t('Provide the consumer secret for your private application on xero.'), '#default_value' => $config->get('oauth.consumer_secret'), '#required' => TRUE, );
![Page 39: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/39.jpg)
if ($this->query->client !== FALSE) { $account_options = array(); ! try { $context = array('plugin_id' => 'xero_account'); $accounts = $this->query ->setType($context['plugin_id']) ->setMethod('get') ->setFormat('xml') ->execute(); ! foreach ($accounts as $account) { // Bank accounts do not have a code, exclude them. if ($account->get('Code')->getValue()) { $account_options[$account->get('Code')->getValue()] = $account->get('Name')->getValue(); } } } catch (RequestException $e) { $this->logger->error('%message: %response', array('%message' => $e->getMessage(), '%response' => $e->getResponse()->getBody(TRUE))); return parent::buildForm($form, $form_state); } catch (\Exception $e) { $this->logger->error('%message', array('%message' => $e->getMessage())); return parent::buildForm($form, $form_state); } ! $form['defaults']['account'] = array( '#type' => 'select',
![Page 40: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/40.jpg)
<?php /** * @file * Provides \Drupal\xero\XeroQuery. */ !namespace Drupal\xero; !use Symfony\Component\Serializer\Serializer; use BlackOptic\Bundle\XeroBundle\XeroClient; use \Drupal\xero\TypedData\XeroTypeInterface; use Drupal\Core\TypedData\TypedDataManager; use Drupal\Component\Uuid; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Guzzle\Http\Exception\RequestException; use Guzzle\Http\Exception\ClientErrorResponseException; // use Drupal\xero\XeroQueryInterface; !/** * Provides a query builder service for HTTP requests to Xero. * * This matches functionality provided by Drupal 7 xero module and the old * PHP-Xero library. */ class XeroQuery /*implements XeroQueryInterface */ { ! static protected $operators = array('==', '!=', 'StartsWith', 'EndsWith', 'Contains', 'Guid'); ! /**
![Page 41: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/41.jpg)
public function __construct(XeroClient $client, Serializer $serializer, TypedDataManager $typed_data, LoggerChannelFactoryInterface $logger_factory) { $this->client = $client; $this->serializer = $serializer; $this->typed_data = $typed_data; $this->logger = $logger_factory->get('xero'); }
![Page 42: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/42.jpg)
public function execute() { try { $this->validate(); $this->explodeConditions(); ! $data_class = $this->type_definition['class']; $endpoint = $data_class::$plural_name; $context = array('plugin_id' => $this->type); ! if ($this->data !== NULL) { $this->options['body'] = $this->serializer->serialize($this->data, $this->format, $context); } ! $request = $this->client->{$this->method}($endpoint, $this->options['headers']); ! // Add query parameters via getQuery() instead :-( if (!empty($this->options['query'])) { $q = $request->getQuery(); foreach ($this->options['query'] as $key => $value) { $q->set($key, $value); } } ! $response = $request->send(); $data = $this->serializer->deserialize($response->getBody(TRUE), $data_class, $this->format, $context); ! return $data; }
![Page 43: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/43.jpg)
Views Rest Export
• You can also use Views and Rest modules.
• But if you need to serialize for third party APIs, then you have the tools and knowledge to do so.
![Page 44: Pieces of the API Puzzle: Leveraging Typed Data ...softpixel.com/~mradcliffe/files/dco-2014-guzzle-serializer-typeddata.pdfPieces of the API Puzzle: Leveraging Typed Data, Serializer,](https://reader030.vdocuments.net/reader030/viewer/2022041211/5dd0d426d6be591ccb62e696/html5/thumbnails/44.jpg)
Questions?