 All right, good afternoon, everyone. So the title of this talk is, How Do I Type Data? There's no typo in the title. This talk is not about data entry or typing layouts, no matter how fascinating those are. I love Dvorak myself. But today we will talk about the Elusive Type Data API in Drupal 8. So in the last couple of years, I've explored this once undocumented API a couple of times, and a few similar talks at mid-camp in Chicago, Drupal Camp Atlanta, and Drupal Camp Ohio. And each time I just go a bit deeper down the rabbit hole, so to speak. So one of the titles of the talk was down the rabbit hole with Type Data. Before I begin, a little bit about myself, my name is Matthew Ragliffe. I work for a small software and web development company called Cosata. We're based out of Columbus and Athens, Ohio in the United States. We make a visual programming software called Vuro. It's not related to Drupal, but it's for visual artists and to create dynamic art installations and applications. But on the website of things, we use Drupal. So I've been involved in the community since 2007, roughly, making contributed modules. I like hacking on things. I like integrating with APIs and figuring out the unknown. And I have a lot of opinions. But enough about myself. So to describe the talk, it's a summary. I'm going to go over the concept of type data and what use cases you might find you would use it for. And then a little bit about why you would choose type data over, say, entity API. And finally, we'll get into working with data definitions and data types and how you go about using data type and instantiating it through services and into some complex examples. And finally, we'll look at unit testing type data. So to begin with, we have, in my own words, I describe the type data API as a meta for primitive and composite data types in Drupal that are not otherwise defined and provide a strongly typed interface that can be passed into various subsystems. So if we think back to programming class in college and you think about what a primitive data type is, that's a basic or built-in type of a programming language, composite data types, or any data type that are made up of primitive or other composite data types. So it structs in classes. But again, we're talking about a meta around data types. So type data API is a low-level API described data within Drupal and not necessarily about instantiating and creating new PHP types. It's a meta language around describing data. And I don't know about any of you, but I find myself integrating Drupal with external web services APIs all the time. I mean, does anybody integrate Drupal with third-party APIs? Right? A lot of people. And using those APIs, they're often described their own kind of data, and it's not in Drupal data. For instance, in the education world, we have the IMS global specifications, LTI, LIS. I work with the accounting software, Zero, to integrate with that. Salesforce, YouTube, all sorts of APIs that describe data in their own special snowflake way. Or maybe it's Drupal that's a snowflake way. But in any case, so I began just the history lesson here. One of the examples I give is how I integrate with the Zero accounting platform. So I'm just going to describe that a little bit. And so you'll understand where I'm coming from in this use case. So Zero provides a flexible API. And I created a module back several years ago to integrate with the Zero API so we can easily just post invoices with a couple clicks and go back to coding, because we're not accountants. We just want to get our invoices done with, right? Integrating with e-commerce systems, creating bank transactions. All you need to know there about zeros, you have accounts receivable and bank reconciliation. So a bank transaction kind of marries the two together. Again, this data is stored in Zero. It's offsite. And I'm not going to shill Zero. It's whatever it is. And you don't really need to know accounting. But in this example, I'm just describing the types of data. So back in Drupal 6 and Drupal 7, I created this module. And I'm using curl and Drupal HDB requests to make O-off sign requests. I do a little form validation. And then I finally model the data as associative arrays, because it's Drupal we love our associative arrays. So it's not strongly typed. This was before entities. And then I also had to use some external PHP libraries to do the curl stuff. And things I don't really want to maintain like this. Let's move on. So to reiterate, when I was looking at implementing Drupal 8 and implementing my module in Drupal 8, the goal was, again, it's to make HTTP requests to integrate with external systems. And what I want and what I need is to describe zero types into something that Drupal understands and vice versa. So I have both primitive and complex data types. And these need to be flexible enough to handle the custom use case of not relying on storage. Sorry, again, I don't like storing things in two places. It's just my opinion. So that was one of my little criteria. So as I begin looking at Drupal 8, we have these awesome things like Guzzle and the O-off subscriber plugin. We have Composer to bring in all these libraries. But again, I don't really want to go into Guzzle because there's another session and Composer because there's another session about Composer on Thursday. But to make a long story short, I found a nice little symphony bundle. I was able to pull that in so I could handle pretty much the Guzzle stuff on its own. I can focus on other things. We also got the serialization module, which ties into type data. I'll explain that in a little bit. And that comes, again, in a little history lesson. Back in 2012 in Munich, I was around in a Birds with Feather session around a table in the Coder Lounge. And I was surrounded by all these fascinating people who were talking about the HTTP specification and RESTful APIs. And the end result of that was we brought in the serializer component into Drupal Core and we created the serialization module. So I got serializer, I've got Guzzle. That has pretty much solved most of my module. But the one thing I was missing, of course, is a way of describing data. And that's where I found type data. I mean, it makes sense. Something that's called type data, describing data type. So I was like, oh, OK, this sounds like what I need, right? So looking at the types of data I'm integrating, in my use case, I've got simple data types like amounts or tax amounts, like they're integers and floats. I've got relatively simple but structured types, such as accounts. These are bank accounts or sales accounts. I've got relatively complex types, such as an invoice, which include other data types like accounts and contacts and a list of line items, amongst other things. So again, speaking of line items, line items aren't themselves kind of on their own. They're only included in other data types. So I began to evaluate type data and say, OK, why type data? It looks like a flexible way of defining and describing my data types from outside of Drupal. Type data is injectable. I can use it in forms and routes, route controllers, normalizers. So it's going to fit in with these other tools that I found. And in my opinion, I think that type data will work well with other core and contrib modules, such as rules. On the other hand, type data is a low level API. So if you're used to the entity API and all the cool things you get, like access handlers, form handlers, caching, type data is lower level API where you don't get those things. So you have to kind of weigh whether you're describing data that really needs all those things or you're describing data that's kind of on its own. In my opinion, in this case, I think that entity API had too much baggage. So that's why I looked at implementing type data. Again, you can override and ignore things. But will other contrib modules behave the same way if I make and I implement entities and I'm not using them? So I just wanted to have a lower level representation. And I think that's a better fit for external APIs. All right, that's my use case. So a little bit about type data. And it really comes down to this one class, and that is the map class. And that is the one complex data type in Drupal that does not describe an entity. So maps are basically associative arrays. OK, maybe that's a little exaggeration. So a map, kind of like the hash map or the hash in C sharp, it's an iterator. So you can do iteration on it. You have definite, you can define properties. So you can have constraints. So it's a little bit more than an associative array. So when I talk about maps, usually every complex data type I have is extending map in a certain way, just because it's so flexible. So how do I define a data type? It's a plug-in, like most things in Drupal. This plug-in extends the type data class or the map class. Is everybody not familiar with a plug-in is? Good, they're all the things. The data type plug-in has a constructor that I usually inherit. So a lot of my classes are pretty much empty. So it's pretty much implementing a class. And that's why I'm using map. Although, you might want to add some useful methods. So what does the plug-in look like? We've got our annotation type, data type. We've got our plug-in ID, translation for a label. And then I'm going to skip down here to list data type. A list data type isn't necessary for all type data, but it can be particularly useful if you're implementing a web service and you need to target a specific normalizer. And then finally, we have something called a data definition class, or definition class. So what is a data definition? And that's where we are describing properties. This is very, you're probably kind of thinking it's similar, and of course it is similar, because it's the lower level API. It's similar to your base field definitions that you're using in your entity API. But it's, again, it's much lower level. You're only defining properties. Definitions offer constraints, such as regex, choice, all the symphony constraints that we're now getting used to. You probably can't implement some of the durable constraints that allow value, because it's got some implications about entities. And you can assign labels and descriptions. And each data definition describes another data type. So again, the difference between a data type and a data definition is sort of similar to the fact that the definition describes properties of a data type. And the data type is the logical means of representing the data. So an example of a definition, let's say I'm implementing a color. It has three properties, red, green, and blue. And so I'm going to describe this as the typed example color. It's an example module of mine. So we have the each data definition, we use the create method, static method, and we supply a type, which is the plug-in ID, the data type plug-in ID. So again, this is very similar to base field definition and the create method on that, where we pretty much just implement a get property definitions method in your definition class. And it's probably going to extend complex data definition base. I'm not sure if anyone else is confused, but I see get property definitions, data definitions, return this property definitions, right? It's like I'm at Disneyland. I'm going around and around at the teacup ride. And typed data can be confusing, because some of the terms are similar. And it is also recursive. So when you're going through it in your debugger, you're constantly going back and forth through typed data manager. In another example here, I'm describing a more complex type. So in this data definition, you describe a type that uses the previous definition and sets it to the primary color. And again, this is an example, let's say I'm describing a set of colors. And when you describe a set of colors, you have your primary color and your secondary color, your color wheel, and never thought you'd hear a color theory being mentioned in a coding and development session, did you? Well, so we get down here to the list data definition, which is a little different. So what a list definition does is allow us to describe a property on our data that's an array, basically an index, acts as an index array, a typed index array. So we use list data definition as the class, but the actual data type class is going to be, you know, the item class of that data type. Again, just to highlight that. So let's apply what we now know to a different example. So let's say we are implementing a Spotify API. Just as an example, Spotify has an album model which has a list of tracks, a list of artists, a type, a name. So how would we go about describing that in Drupal and in data definition? So we would have an album definition as our data definition class. It would describe, our get property definitions would describe a list of properties. So we have name and type and artists, and each property is instantiated with a data definition of a certain type, right? It's starting to make sense to visualize it in this way. You can map out your data types. So that's about it for how to create data types, but how do you actually use typed data, right? And so in summary of that, there's a couple of ways to start instantiating data types. So you can use the typed data manager. So we have a plugin manager to create data types and data based out of default values. One of the biggest uses of typed data is through the normalization service. And I'm using this myself to transform what the data I get from the API into something Drupal understands. And then through that, you can use serializer and guzzle in a consistent and maintainable way to communicate with third party APIs. And then I've also started to explore typed data beyond what is out of the box to add some kind of helpers, which I'll get into in a little bit. So the basics, very similar to entity API. We have getters and we have setters on our typed data classes. And we also have the get value and set value. These are also available, you probably have seen these and used these in entity API. And that's because you're going down level all the way down to those primitive data types eventually. And our get value is going to return native PHP types like array or string, but it's not going to return you an integer or float. Particularly in the default typed data normalizer is always going to return you a string. So if you have like a very specific use case for exposing strict integer and float types in your API in JavaScript, then you'll have to work around that by using the get casted value method. And finally, typed data manager creates data from definitions. So let's try and do that. So this example again is coming from my typed example module which I've recently published in my sandbox to do that. It's typed data manager as a service. We get the service hopefully through injection and not through the Drupal object. We call the create data definition method and we provided a type. So this is going to return the definition that complex data definition that we defined earlier. And then I can give it some default values if I want. So again, when you use map, your values that you're, if you wanted to create is just an associative array. Again, map is almost like an associative array itself. And then I can use the create method on type data manager to actually create an instance of the data type defined by the definition with those default values. Moving on, say we want to get the representation for the primary attribute or property. If you use the get method, you're going to get the typed data representation. But if let's say I wanted to get the actual raw value, again, I would need to go and call get value or get casted value on that return value. If I did, for instance, data get value, that would return to me an associative array similar to what we saw over here. Yeah, let's skip a slide here. All right, how about another example? So let's say I want to create a form, a select list in a form populated by type data. So I do this in my zero-model, providing a select list of accounts, default accounts. We'll ignore kind of this black box method for now. All it's going to return is a list of accounts which, you know, through Guzzle. I can, because I'm using the complex data and using map, my account, it's an iterator, so I can go through each account, get the property for code and get its actual raw value, and then create a little associative array to then populate my select list down below. Sorry to make sense. It's kind of, you know, familiar with the entity API. So let's go into a little bit more in an advanced example about normalization. So I had to kind of figure this out on my own a couple years ago, since there was no documentation at the time. And normalization is the act of transforming data to a normalized form from like a specific form. So we have our droveled data types, and normalization is when you turn them into what is needed, a generic form, so what is needed for communicating with the API. If you've never defined a normalizer before, it's, again, it's another service. It's a tagged service, which basically means it has another compiler pass if you're not familiar with the symphony container. So these services have like a little extra thing going on with them. You'll want to inject the type data manager service. The first important thing to remember about the normalization class is this supported interface or class. And actually just recently there was a commit in I think it's coming in 8.2 that makes this optional. But for me, I need to implement this because zero being a special snowflake API has some requirements. And so I want to target my data type. So this is one way of having your normalizer be restricted to just your data types to have an implemented interface. Skipping down a little bit, I'm gonna ignore the normalized method, which is pretty straightforward. So when I get data back from the API, I denormalize it into type data. Again, I'm gonna use the type data manager to create a list data definition. And then you'll see this use of this context variable. The context parameter doesn't really have any documentation around it in symphony. It's coming from symphony. And this is one of the gotchas that I ran into. I was, what is context parameter? And it's essentially just a free form parameter that serializers and normalizers use. So it could be anything. But in this case, we passed the plugin ID via the plugin ID key of the context parameter. Again, the context parameter was added in 2013. The commit message for it is kind of amusing. Benjamin Eberle committed a completely refactor to serializer options pull request to push context information directly and avoid state and dependencies between serializer interface and encoders slash normalizers. Again, not really straightforward of what that means. And that's literally all the documentation there is on that, except for this session. So how do I use normalizer? You know, normalizer is great by itself. It's not really gonna do anything. So Guzzle and serializers is how you do that. There's an excellent session on the Guzzle on Thursday at 10.45. Unfortunately, at the same time as that composer session. So you'll have to choose your battles there. So let's just run through a little quick example. The first thing I need to do is I need to serialize my data. So this is how normalizer works. You call serialize on it. And then the serializer component in symphony is gonna go down the chain and normalize first and then call the encoder to encode the data in XML or JSON. So here's that context parameter again. Again, I've got an extra thing in there. Just a random thing, not documented anywhere. I call serialize on it. I'm sorry, I call serialize on the data. So that's my data type that I have my instantiation of type data. And then I give it a format and give it the context. I get the XML back in this case and I put it in my body of the message I'm gonna send. And then I call the Guzzle client method with the appropriate method on the endpoint passing in request options. And I get back a nice PSR7 response. And then I can then with my response and I'm lucky in that when I post an account I get an account. May not always be the case in the APIs you implement but it should be. I will deserialize the contents of that body into the respective type data. And that's essentially what I'm using most about type data in Xero. But I need a little bit more. So in Xero I implemented a kind of like a little class to help me build forms. So I've recently abstracted that out into the typed element builder service in the typed widget module. So this creates, it dynamically creates form elements from data definitions and plug-in IDs. It even supports entities and fields but it's main use cases for things that implement map and primitive data types. Hopefully maybe I can, I'm working with Fago to maybe make this, we might put it into rules as well. To drive the data selectors in rules because it essentially does what that does. So it's a little, it's pretty easy to use. I'll give an example. So we have basically this one method that I primarily use before is get element four. I pass it the plug-in ID of the data type and I get a form element back. In this case, date time ISO 8601 type is gonna return the HTML5 form element, render element in Drupal that gives me an HTML5 date element. And I can do like get element four on the data representation for user which at the moment is just gonna call the form handler for entities. Or I can just get a, I can pass a second option to get just the mail property on the entity user data definition. So a little example. Again, let's say I have a form, is it playing? Yep, so I have a form, I get my element builder. I call my method, get element four. I pass it a plug-in ID. So we're gonna use that same complex data type that had the name, the primary color and the secondary color list. I can then, skipping down a little bit, let's look at the submit for this. I have a form so I can get my values for my form state, right? That's familiar in Drupal 8. And then using type data manager, I create my data definition from my, using a data type. And then with the data definition, I can get back to type data using type data managers, create method, passing in my definition and my form state values. So I just basically created a form in about three lines of code. If I, other than writing that class. So it would look a little bit like this, right? Color red. In this case, it created an HTML5 element. So I have all that nice fancy browser validation. There's me failing to select with a mouse. Show you. And that's it. So that's my module that I wrote recently. It's now on Drupal.org. Does anybody like it? No? Awesome? Okay. That made me feel better. Now I might get a stable release. So I think it's, in its, I think the code is kind of ugly and it needs to be improved. It needs help to make it a little bit more extendable. So I'm, you know, if you have opinions on this, please go ahead and post in the issue queue. I'd like to add a little bit to it. I think that type data, when you implement your own type data, you're missing things like you get on fields or on entities like form handlers. So maybe we can add to the, like a default interface or abstract class that builds a form, you know, build element or view element. I'm doing that a little bit in zero module. Okay. So that was really cool, right? That was an awesome little thing. Now we're going to go to something a little bit uglier and we're going to a little downer. I'm not sure why I chose that path. But we're going to talk about PHP unit and type data. I like unit tests. They're awesome and fast, but type data manager is big and scary. Type data calls itself. So mocking is not straightforward and data definitions use static methods, which is not great. Again, static methods are not great for isolating unit tests. And you also need to mark the service container. But on the other hand, I like seeing pretty coverage and complexity graphs. So that's why I went down that rabbit hole. One of the questions I like to ask myself and what I'll answer here in the following code slides is what is actually necessary for decent code coverage? Again, this is something you could ask yourself as you're watching these two other sessions, you know, one today at five, automated testing, PHP unit all the way. So again, if you want to learn PHP unit, you probably should go to that session. I cut out a few slides because I didn't want to steal their thunder. And again, there's another one about evaluating different types of testing tomorrow at 1045. So I highly suggest going to those sessions if you're interested in PHP unit. Unit test for data definitions are pretty easy to get coverage on, right? I usually only have the get property definitions method and all it's doing is returning an array an associative array of property names of other data definitions. And those data definitions are usually covered by course tests. So I'm not sure what the value, how much value there is in testing this other than just getting coverage, simple coverage. For example, if I look at this test, I'm testing that color class, right? I really just have to instantiate the color definition and then call the get property definitions. And one of my testing labels. There's no logic there, right? I'm not sure. I don't really see that much value in that. But on the other hand, we've got something a little bit more complex and that is type data manager. And it's a little bit uglier. So one of the issues with testing type data is that the type data manager, when it calls its create method, or when data definitions are called through a create method, the complex ones will call type data data manager through the Drupal object. This is bad, right? We have to mock a service container. We can't inject, the dependency isn't injected. Sometimes we need to mock the object in a specific order. So like in my normalizer class, you know, I need to return things in a certain order so that type data, when I call type data manager, it's going to go recursively down through the data type. So it has to be done in a certain order. Again, when I originally wrote a class, I hadn't found the return value map method, which helped a lot later. So you can be maybe a little sloppy if order isn't important, but if order is important, you might have to rely on things, consecutively mocking objects, which I think is an anti-pattern. The main method you're going to mock is the get property instance method. Sometimes I mock the get definition method and get definitions, but mainly I really just need to mock get property instance and return a mock of the data type. So an example with prophecy. If you're not familiar with prophecy, that is included in Drupal Core. It's what's called an opinionated testing framework, but nowhere does it say why it's opinionated. So you have to find, if you dig in the issue queue on GitHub, you'll find out that it's opinionated because it doesn't mock bad things like static methods, magic methods, or methods that return the this object. So methods are useful for object chaining. So if you need to do any of those, you can't use prophecy, but prophecy is usually a little less verbose. So without further ado, here's some bunch of slides of code. So this first set of calls is pretty simple. I'm not using prophecy. So when I'm, I don't usually bother with mocking the simple primitive data types because it's just so easy to do so. I'm not really saving any time. So I'm gonna mock out some, a string of a couple floats, you know, my float for a color. And then finally down here, I'm going to prophesize the color data type. And I'll just, for now, I'm just mocking the get method with the red parameter to return this float data I set up here. And then down here, I'm really lazy in this example actually. So I instantiate a list, an item list. Sorry, I mock, I prophesize an item list class. And then I, you know, an item list is sort of like an index array. So I mock the get method with the first index. And again, I'm lazy, so I'm just returning the same data type I instantiate there. That's probably, but I'm lazy. So type data manager, I prophesize that and I'm gonna just mock out, get property instance with different arguments. And then, you know, I have to build a container and set my type data manager service into the container. And then finally, I can create my data type and call the set value method on it. And again, set value, I can just pass in like an associative array as my values. So what am I testing? This is what I'm testing. One line I'm testing. This is kind of, I did all that. So I could basically assert that when I call the getter on my custom data type, I get the right class. And that's all the value I can get out of it. And the reason is I've mocked those, the return values of that getter is mocked out. So if I asserted that the return value of get red, get value was 255, there's no value because I just mocked that value. I am like, hey, that's not a valid test. So really, that's pretty much what my goal is in testing is to assert that I'm getting the right instance out of it. This is just using a simple little data provider to make it even shorter in this example. So let's look at something, using the PHP unit mocks. So this is what I, I learned prophecy a little, like in the last year, but I wrote the test for zero like two and a half years ago. So this is a little lugular. I'm testing my normalizer class. I mentioned before that I didn't find the return value map method until after I'd written this, written the code for this a while ago. So what I have to do here is, call this a little bit more verbose code, mock out, create method, mark out the get property instance method, and then I have to go, and this is just a small snippet, this function is huge. Because I have to, I have all this data here that I'm mocking, and then I have all these other data definitions that I have to instantiate and mock. There's one that's even longer because I have to, I have a code path that splits whether you have one item or two items, and the output is slightly different. But that's again, essentially, so I can call the normalize method with my items and assert that I got an associative array that matches my expected input for zero based on my type data. That's a lot, right? I just showed you like what, four or five lines of, five slides of code or something like that. So to apologize, I wrote a poem about mocking Drupal. What I learned not to do, I resigned myself to that fate. Besides, core does it too. So please do not hate this container so small. It's not actually in my app at all. I do not wish to load or directly invoke the container to get variables into my code, but to be a better maintainer, forgive what I leverage. All I want is pretty coverage. An alternative opinion is that I'm crazy. And you know, it's true. Why go through all of that, right? I mean, that was a lot of effort to run tests, you know, to do all that. I mean, the reason is my previous code wasn't testable. And I used untight to search the right. I'm one strictly typed. And if I wanted to use an integration test, I couldn't do it. Because if you ever written an automated test for an external API before in a public repository, you can't go embedding your API keys into GitHub or Drupal.org, that's, you know, they're gonna get stolen, right? So this is something that, you know, you have to do that if you're implementing web services APIs and you want to have code coverage. And I also think that data definitions and types better model non-Drupal data, which makes it a little less complex and a great on the normalization side. Again, you probably could get away with just doing a normalizer, a custom normalizer, but you'd, again, you'd have to really to target that entity that you've done. And I don't need storage. I don't need access handlers. I'll let my forms deal with that. And my routes deal with access. My rest things will deal with access. And I think it'll integrate better with Drupal contrib. So what did we cover today? Well, we went over how to create data types and data definition plugins. We went over how to use typed data and how to instantiate data types and use it with serializer and to implement, to integrate with third party API. And I shut off typed widget today and they are going to create forms from data definitions. And we looked a little bit about testing with PHP unit to mock typed data manager. There's still a lot more to learn. I'm still going down the rabbit hole like there's type config, which is another topic of its own. So there's a similar service for creating and data types from configuration. I think there's a lot of to-dos there to turn it into typed data. But I like to open up the floor to ask and discuss or share typed data use cases, questions about typed data manager, typed widget, comments about typed data API, complexity improvements. We also have the evaluate the session, if you could or you can tweet me. Again, my username on triple autos and Radcliffe, but I am Matt Kiname on Twitter. Any questions? Go ahead and step up if you, I know it's a little hard. If you use the mic, that'd be appreciated. Yep, in the example and one of the slides in the beginning, there was the getter, where it said get read or no, actually get primary and then get read. Is there a way of doing it without strings? Like it's typed data anyway, so it should be possible to use a variable name or something. Yes, so the question is, is it possible to use a variable name to get, to use the getter? And the answer is yes. I could have created a function to say, hey, just get a property name and then pass that property name in. Yeah, you can do that. For the sake of an example, I wanted to be verbose. The answer is your question. Okay. Yeah, can you step up to the mic? I just, wow, wow. Okay. What if I want to store data somewhere in database, for example? Okay, so the question was, what about data storage and what if I wanted to store data in the database? So again, the disadvantage of typed data is that you don't get all the nice to haves like the entity. So entities provide storage, you get field storage. So you would have to implement your own. So if you really didn't want to use entities and you wanted to implement your typed data with storage, you could then perhaps implement a service that calls the database service and then do queries yourself to insert data based on that. An example could be storing data in the JSON type. That'd actually be a really fascinating little module to write would be to create like an entity storage layer that instead of saving data in the columns, just uses serializer to JSON and code and store it in the database directly or sort of JSON decode. I think that'd be a fun little module. It's particularly if you use Postgres SQL. Did I answer your question? I think you mentioned it, it's just to confirm but does the entity API and the field API eventually trickle down to the typed data so or is that completely separate? So the question is, does the entity API and the field API trickle down to and use the API and the answer is yes. Because your entity field definitions, so you think about base field definition, so if I create a base field definition of integer, that is actually a field item of field item integer and that's composed itself of property definitions and those are usually implementing data definition and so the data definition create methods on the field item integers properties are going to be primitive data types and so eventually when you call get on that property, on the entity, you then have to call get value which is probably, I believe is wrapping calls down to the primitive data types. Any other questions? Okay, one last thing, I'd like to welcome everyone to join us for the contribution sprints on Friday in the first time Sprint Sprinter Workshop to help set up a AAA environment to work on core with the mentored core Sprint where people like myself, I'm a mentor, you work with us to work on issues and then also the general sprints if you're more familiar with core process to just join the general sprints and work on a topic like type widget, although I'm not sure if I'll have much time on Friday to do that as a mentor, you're welcome to submit patches for type widget or maybe rules, work with Fago, see if he needs help on implementing type data in rules. We have another question, go ahead. So the question is, can I store type data into a field value? So probably there is nothing that will save field values outside of an entity. So again, you'd probably just use an entity to do that and use field off an entity. You could theoretically, you might have to override some methods on your field type or basically nor some methods on that field type but you could get the value and then, again, you'd have to go through database storage, not service, similar to your other question. If you want to talk to me about afterwards, I can talk through, basically it's sometime to the field, the field data type has to just be any and then that lets you just store the data but you make sure that your field handler is actually creating the instance of like data that returns but I can talk to you after because it's quite easy. So yeah, Peter mentioned that he is, go ahead and repeat what you said. Pretend this didn't happen before. So just to add to that question, I've actually done that. You can store type data in a dump field that's serialized in the database. You have to use the core data type of any which allows any type of data to be stored. It doesn't make any assumptions about what it is and you make sure that your field handler is actually returning the correct type data by basically adding a step that creates the instance of type data from that serialized data. So I've got some examples if you wanna talk through it after I can show you as well. All right, if there's any more questions, you just see me afterwards. Let you go and have a break and enjoy the next session.