 All right, hi all This session is about entity validation or the API for entity validation So if that's not something you want to see you're in the wrong room and you still have time to go to a different one So let's get started. I usually start off my sessions with a couple of questions for the audience So how many of you have worked with plugins in Drupal 8 before? Excellent awesome How many of you have written a custom field or entity type? Wow That's quite a lot awesome so what Most of what I'm gonna tell you guys is gonna make a lot of sense So that's perfect. I've given this talk once before a couple of weeks ago for a mixed audience and have the room It's like what's he talking about and so Awesome great audience then so let's get started. I am Cristian van den Ender. I'm a Drupal developer I work at decent in the UK, but I work remotely. So I just live in Antwerp, Belgium Well close to it at least I'm happily married to my wife Sylvie and I always mention it just in case I'm a highly sensitive person which means that at any point in time something in my body may trigger a panic attack or stuff Like that. So if something does go wrong, it's that don't worry too much. It's usually fine So yeah So let's get started. So what is entity validation before I go into my session I just want to mention how I came about the creation of this session. It's um I'm the author of the group module and it's a really complex module So basically I had to touch like every API in Drupal core in order to be able to create it and One of the API's that wasn't really documented and actually Really amazed me was the API to validate your entities with it's pretty obscure There are like hints left and right in the codes in core But in order to really get it I had to like dig through the code Google a lot It really was hard to wrap your head around so my idea was that I would create a session and Tell you about my findings so that you don't need to go through all that pain and efforts So here goes so entity validation is basically what you need to prevent this We've all seen it a lot It's Really annoying, but we can all be thankful that we get these guys because if we wouldn't then we would have Invalid data or code that could be crashing somewhere and we wouldn't know What's up because it was already too late? So thankfully we have exceptions except this little guy shows up way too often So you want to prevent that so what the API in Drupal 8 does is it validates your content entities? So I'm guessing given the showing of hands before that most of you know what content entities are it's like notes You know stuff that your users can create so it validates those and it validates those on multiple levels and When it does that it returns a list of violations It always returns that list it can be empty meaning there were no violations or it can have violations in them meaning validation fields It happens automatically in the entity form so When when you fill out a note form and you're doing something wrong you get this error and it highlights the fields That you know had invalid data. That's actually entity validation at work It's the only place in core where you can really see it as an end user However, it's not a part of the form system So it's being used in the form system and this is the cool difference with Drupal 7 in Drupal 7 Entity validation happened in the form system because it was the form system. There was nothing else It was a validation handler of a form. So if you wanted to validate your entity outside of that well, good luck There were a few ways, but they were all really painful to implement in Drupal 8. That's no longer the case They separated it. So now it's a different API, but it's just being implemented in the content entity form So and it guarantees the data integrity of your entities because well if you ask for your entity to be validated before it's being saved Then it will not save until it's valid It's up to you by the way to handle those violations, but more on that later So when do you want to use this and that's actually pretty simple It's basically whenever you manipulate an entity So there are a couple of examples and one of which is REST, which is the most common one So imagine this you're not using that form that has that validation built in But you're allowing people to create or update or delete well not necessarily delete entities through an API Then how will you? Validate what they have sent to your API. It's it's really hard to do that in Drupal 7 But in Drupal 8, it's really easy because we have that separated API Also when you have custom entity generation Generating codes so when you generate an entity yourself So for instance you have some code that needs to generate a few default notes And then you want to set those as the front page or whatever You can't always know for sure What field someone may have attached to that note So if you call validation on that note and it gives you any violations You know that you didn't provide enough data to have a fully populated notes Same goes for migrations stuff like that whatever you can come up with if it involves manipulating entities You probably want to implement entity validation And it's actually really simple to use so how do you invoke it? That's it You just call validate on an entity and that's it or Entities have fields if you want to validate just one field You get that field and you validate that or if you want to go even deeper and you want to validate one entry in that field one Delta Just grab it It's really that simple although You need to do something with that list of violations So you that you don't just call it because well then you have Drupal telling you yeah There's some stuff wrong here, and then you're just ignoring it So ideally you want to save that in a variable and then do stuff to it So how do you work with these violations? Well first of all checking whether there are any is easy you just get that list and you call count on it Because it's a countable object So if it's zero then you know you pass with flying colors if it's not then you need to do something But it extends iterator aggregate so you can loop over it and get all of the violations There are some more funky methods you can use you can get all of the violations that occurred just on the entity level Or you can get all of the violations that occurred on a single field if you want to read more about that You know slides will be available later on you can check out that interface If you want to use a single violation, you know in a loop or something you can get the message from it And that's just a human readable message that tells you what went wrong so it can be something like The username is not unique Something like that and then that message will show up There's other methods too like getting the property path more on that later because that's pretty complex Or you know getting the value that was invalid to get a better message yourself or something If you want to read more about the single violation, it has an interface as well as well. Just check that one out So where do these guys come from? So there are four levels of entity validation You can validate entity as a whole and in order to demonstrate this properly. Let me first sketch a situation So imagine there's four people standing in a queue. There's a lot of psychological quizzes like this that start like this But imagine there's four people in a queue and they can only see the people in front of them If you ask a question to the last one in the queue it can tell It can give you an answer about Themselves or the people in front of them if you ask it to the second to last in the queue again The people in front of them or themselves, but they don't know anything about the guy behind them So that's how you need to imagine this the higher level you invoke validation on The more it can tell you because it knows more about what's coming next if you invoke it at the deepest level It only can tell you something about itself So you can validate the entity as a whole you can also validate a field on an entity now a field on an entity is You know the human readable Express the human readable or human understandable way of expressing this but for Drupal a field is actually a list of entries like you know All the deltas But you'll see that in a second So you can also grab a single entry of a field on an entity and validate that or you can go even deeper and validate a Property of that single entry So in an obligatory car analogy that would look something like this you either validate a car entity You validate the field wheels you validate a single wheel in that field In in that field or you validate a property like tire size of a single entry in that field In Drupal speak it looks like this So if you want to validate the entity you're validating the content entity interface Actually the fieldable entity interface. That's where the validate method is on But 99% of the time you're using the content entity interface, which extends the fieldable entity interface On the second level you have the field item list interface Which is the the full fields one entry of that field is the field item interface And then all of the properties below that are just type data interfaces Now there's a small lie in this slide, which you'll probably have spotted if you went to the session about type data All of the above are also typed data interface. It's just easier to remember it like this So the four levels in detail I'm quickly going to skip through this because it's just examples and I've got a feeling that this The audience understands it already. So when you're validating an entity as a whole You're actually because you know all of what's going on on itself and everything below you You're you can validate across multiple fields on that entity. That's why you want to validate on that level So for instance if you want to add a fuel tank and an electric engine to a car entity You're gonna have a bad time So that's where entity validation is a good place to you know throw a violation Because it can tell what fields it has and then compare the values of those fields and tell you what's wrong You can also do field independent Validation on the entity level because you don't necessarily need to look at your fields For instance, if you're trying to save a third car Entity when you're flat broke you should get a violation saying you're flat broke. Please don't buy this car so that's all of the types of validation that you should do on the entity level you can also do it on a field and That's wrong, it's supposed to say multiple field entries, so whoever just pimps my slides mistyped that So it's validation across multiple field entries So for instance imagine that field with the wheels if you want to add to a monster truck wheels and to a mini bike wheels To the same car. It's probably not gonna run. So that's where you would throw that violation When you are validating a single entry you can validate across multiple properties. So again the lower level So for instance if you have a wheel and it has a different rim size than a tire size probably not gonna work and Then finally at the deepest level you can validate the property So basically if you have a tire made out of woods you want to throw a violation saying select a different material So how do you define your own validation? It actually needs two classes, but it's not that hard So one of them is a constrained plug-in and this is why I asked you about the plug-in system so it's Basically quite easy to define you just need these guys like a unique ID every plug-in needs it You need a human real readable label not necessarily But I prefer it if people add that because then you have an easy way of telling what it is It doesn't really require a type more on that in a second Or a list of types that it supports and well like any plug-in you put it in my module source plug-in validation constraints That's where you should put it So it looks kind of like this So as you can see it's it's pretty empty There's just a couple of messages there. Those are being used by the validator And you'll see that the type key is missing So what about it? Well, it's missing because score actually doesn't use it It used it in one place and that's the plug-in manager and that one That one actually Just allows you to filter the available plugins on a type like this plug-in supports entities this plug-in supports field items This plug-in supports strings or whatever. It just allows you to filter nothing more So if you want to apply one of your constraints, there's nothing stopping you from applying it to the wrong place Because Drupal doesn't seem to use it. So if you still want to provide a type If you don't it defaults to an empty array Which means you can apply this to nothing which doesn't really make sense if you provide false You tell Drupal that you can apply to everything You can also be specific and say this constraint can only be used for a b c or d I'm gonna shake my legs for a second and just feeling a little flushed So this is what it looks like. So You've got entity and entity reference here Which basically means this constraint can be applied to an entity or a property of the entity reference field that actually references an entity So they can also take options which are completely optional a lot of constraints don't take any but if you want to to have you know Options like if you have a constraints for length, you don't want to create a constraint that says well length 30 32 length 31 you just create one and you allow it to accept an option that says how long can this field value be? You simply define them as object properties on your constraints. That's it And you can accept any number of options as an array whenever someone adds your constraints How to pass those options discovered later? So accepting options So as you can see there's this one option here. It's called type and If you if people add your constraint in it just give a flat-out value Drupal will look for a default option So you could add that constraint with nine and it would map nine to type It's just that as soon as you start accepting multiple options Well, then people will need to provide an array where the keys are the names of the options and the values are obviously the values So now we get to the validator class. This is the the harder part and this is coming from symphony So They have a magic class name. So by default whatever you name your constraints Add validator to that and that's the name of the class you need to have for your validator That's the class name you need to have but you can change it You can change it in the validated by method that you can add to your constraint And the reason you can change it is because there are good use cases for reusing a validator Suppose we had that length validator and the constraint that was implementing it Actually had pretty non descriptive messages It's perfectly fine to create your own constraint with better messages that are better suited to your use case and just reuse that Validator then all you had to do was write the same constraints and just change the messages The value it receives and this is important because earlier I said, you know, all of them are type data But this is why I split them up because at this point Drupal does some magic I can explain that if anyone has questions about it, but it's too deep into core to Dedicate session time to it. So basically when you call validate on the top level on the entity the validator receives and full Entity if you call it on the feel item list the validator receives a feel item list So and it goes on like that. So it's really important to know where validation was called from or initiated from And one of those things why it's really important to know that is Because if you did specify a type for your constraints You can be pretty sure what you're receiving if you specify that it can only have entities Well, your validator can assume that it will get entities if you didn't specify a type Or it's a generic validator then well, you need to double-check what you're getting like check for interfaces or something There's one different There's one small exception though if you're validating properties, you're getting the raw value of that property You're not getting the type data. You're just getting the raw value So if you had a property targets type for an entity reference field, it would just give you the name of the entity type So in an earlier earlier Slide we saw the username constraints So as you can see we just added validator to the name It's automatically picked up and the username constraints is applied to a field item list so a full field So as you can see it receives items because there's multiple entries possible in a field Yet the username fields only has one entry so further down the code They just grab the first item and they start validating that Why didn't they apply it to the first delta? Well, because it's easier to apply your constraints to a field item list than it is to a field item You'll see that too So I'm mentioning the next part for completeness. It's the property path So it tells you so suppose there's an error It tells you where it occurred. So follow me on this one. So The property path defaults to the item being validated So if you just want to throw an error from your validator for the thing that is being validated Don't do anything. Just throw that error. You don't need to worry about the property path But you can be more specific so suppose We started validation on the entity and at one point It went over a couple of fields and then it went into a field where there was an error at That point the property path has already been Populated with the name of the fields because you started it at the entity the last guy in the line He said well I'm trying to validate the person in front of me and that person is you know making noise So something is going on So that is already one part of the path the person in front of you and then that person can just you know Say that well. Yeah, it's me. That's doing something wrong So he doesn't provide the path or well actually it's the guy in front of me So he provides the path to the guy in front of him There is an example of that in a very complex validator There's few examples in core, but this is one of them So if you want to check that out in detail check out that class, but it basically basically looks like this So when you started validating from the top level and something is wrong with the first value Of field foo then it would give the full path to that value But if you started validating from the field level it would give you know the delta and the property But no longer the name of the field because it's already happening on the fields Validation is being invoked on the fields. So the lower in scope you invoke validation the Shorter your property path will be Again, you probably won't need this I'm just mentioning it for completeness if you write your own validator. That's really complex You're gonna. Thank me for having mentioned this Because it can be a real pain to discover that part So how to apply validators and this is the most important part. We've seen what it is We've seen why you should use it. So how do you apply it? There's several ways to do it one is in annotations when writing something that requires an annotation the other way is by invoking The or calling the ads constraint methods and then finally there's one tiny exception because well, it's Drupal So if you define your own content entity type all you need to do is provide a constraints array and you'll notice that after the Equation, there's an empty array in the annotation That's because I'm not providing any options if you were to provide options That's where you would do it if you were to add a string it would use that one option scenario It would just map that string to that one property if your constraint has multiple properties It would require an array with keys and values So every comment entity is that is being validated will always Call that constraint along with all of the constraints on its fields You can also alter someone else's entity type because well if you create them You can manipulate the annotation if you didn't create them you can't So how do you add it to someone else's entity type? Well, you just call add constraints and it's like that for most of the following use cases Usually you just call add constraints You type in the name of the constraints and then you add an array of options if they are required So on the field level so the field item list it's the same So when you are defining a content entity you usually have what used to be called properties in Drupal 7 now They're called base fields So you just add a base field and you can't add constraints and it it's a constraint that applies to a field item list Well, it don't work if you want to do it to someone else's base fields or even bundle fields again You just alter them right The field entry level so one delta within a field item This is a special use case So a single delta within a field item list is actually the thing that you define as a field type So an entity reference field a date field all of them have this plugin for a field type And if you want to have a constraint that applies to every single delta of a field This is where you would apply it if you create your own field type if not guess what there's There's a hook field info alter to alter someone else's field type and then finally we have that especially at all snowflake There's the field property level the deepest level The most common place where you will use this is not actually on the property, but it's on the field item list Which sounds weird, but it actually makes sense because this is the thing you use the most Defining base fields on your entity type So Drupal made it easy By creating a method add property constraints and then you can provide a full array of constraints each of them with their Configuration that will apply to a specific property now most fields have a value property But there are more complex fields like entity reference fields They have this target ID entity target type all sorts of properties if for some reason you want to Validate any of those you can by adding a property constraint That's where you'll use them the most so Which is why again we have hook entity base field info alter and oak entity bundle field info alter if you want to alter someone else's entity type space fields But if you want to apply it to the property itself So if you are defining your field type and you are defining the properties of your own field type Then again, it's just add constraints So this is that special snowflake if you want to do it on the base field level you add the property constraints If you are defining your own field type or altering someone else's field types Property definitions you can do it there and then it's just add constraints because then you're just adding constraints To a typed data thingy as you can see by you know the data definition creates call there So, um, that's it. I hope that made sense. Um, I I I before I go to questions I'm gonna repeat the question that was asked when I was dry running this session in Ghent Which is actually a very good one People asked me like so we have this API and it's being invoked from the form system So why isn't it on by default? Why if I call it? I still have to work with the violations and that was a really good question the reason it's not on by default is because Drupal can't possibly know what to do with violations Whoever decided to create a constraint Had a specific reasoning mind and Drupal can't guess all of those reasons so The way it uses that is it just Says well, we need to have validation in the form system But outside of that it's perfectly fine to save an entity without it being validated Um, if I have time left after questions, I'll show you the code that's doing that Because well to most people it doesn't make sense that you can save entities without them being validated So you really need to call it yourself. That's the message. I want to bring across Don't expect it to happen automatically Do it yourself So, uh, before we hit the questions join us this friday. I'll be there So if you have any more questions about about this talk if you have any more questions about the group module About decent just, you know, find me and I'll be happy to answer them So now does anyone have any questions? The mic's up there by the way Yes Yes, do me a favor and walk up to the mic because then I can hear you better. Um And I don't have to repeat the question then I think That guy has finished so What is the solution for stavisk codes and specifically for For a rest api if you want to Detect the error, but you don't want to parse the text the text to find here. Sorry Did you get that? Yeah, so the question is what do you do for status codes? Um, well Basically, that is not up to the entity validation api Because the entity validation api just validates the entity and it should always be the same status code when validation fails But it's up to you to provide a good message So you have a status code when you do a puts or an update a puts or create Put uh, fuck it. I'm sorry. I'm not that familiar with htp codes So, um, but what what what you do have is when you return that status code that says invalid data provided You can specify your own message and that is where you just grab that violation You call the get message method and that's what you ship off and then it's up to them to deal with it But specifically with the rest api in court The entity Foundation the the error that they pass is really it's not necessarily the message that you want to show to the users But if you have a mobile app or something like that Yeah, okay, um in that case I would recommend the thing I said at the start of the session um It requires you to swap out a constraint But if you really want those messages to make more sense for a user Just add your own constraint that uses the exact same validator and swap them out Just replace the original with your version that has better error messages but what if you have like different kinds of error validations that You want to detect specifically which type of error it was Um, yeah, it just gives you the list of all of them, but seeing as you get that list Um, there's more methods on on a violation By the way, you can see what it was that failed what constrained So you could probably call one of those message methods and check for the constraint that failed So you could do that in your code. I just showed the most common use cases Which is just getting the human readable error messages But there's more you can get from a violation too and then you don't have to compare the error messages You can compare other stuff. That's that makes more sense from a developer's point of view All right, thank you. All right Anyone else Yeah, walk up to the mic, please. Awesome Can you also remove a constraint? Yeah Yeah, you can um, I only showed the add constraints, but I'm pretty sure there's also a remove constraint methods. Um I would advise against it because there's Almost always a good reason why they are there But if you want, yeah, you can remove them The session's goal though was to ensure that after you leave the session and start implementing this It would guarantee your data integrity and what you just asked me is actually, you know, the other way around So, um, if you alter the behavior of an entity, it can make sense to remove a constraint Yeah, in that case you can remove it. Yes It's perfectly possible. You can remove it because when you call add constraints it just actually adds it to uh, Protected array inside that class so I can't imagine they're not being a method to remove it. It should definitely be possible Oh, yeah So you can get the whole list and then remove it from the list and then set the list minus that one item again Maybe the reason there's not a remove constraint is because it doesn't well It's like, you know, it should have this big ass label saying be careful when you Go down this path. So maybe that's the reason they didn't add it to discourage people from doing it But there are valid use cases and as you know, the friendly person at the upfront just mentioned You can grab the whole list remove one item and just add the list back But then you need to have access to the code that it's that's valid No, so they're no sorry They need to have access to the code that calls the validate method Um, well Yeah, you always so You always need to be at a point where you can add a constraint in order to remove a constraint But you can do that to anything as we've seen Because if you're defining your own stuff, then it's really easy, but you can alter an entity type You can alter a field type. Um, you can alter an entity's base fields So you should be able to get to that point where you would normally call add constraints But in your case remove a constraint Is there is a method to remove constraint? No, so there's no method to remove it as This person has just looked up But there is a way to get the full list Which will give you all of the constraints that are currently on that item And then you can manipulate that list But there is a method to also set all of the constraints So you're basically overriding the original list with your version of that list that has one fewer item in it Okay, I think I'll get it now Okay, if you want, um, you know come up to me after and I'll show it Okay, so anyone else? Hey, um, when you create a constraint validator, there's um, you call add fat So if there's something that's failed you would call add violation Yeah, but that applies to context Do you know or would you be able to explain what the context is? Is it ever used in Drupal? Yeah, so um that context I didn't mention it But I did implicitly it's basically what I said when Every validator already knows where it's at So when you're validating a field item list, it already knows that it's at that field item list So which is why I mentioned you don't need to um, you don't need to populate that Property path yourself in most of you know, most of the use cases um, so Yeah, it's it's it's um the context is basically the thing that tells your validator where it's at If you want to specify your own property path for some reason You can also use that context and then go from there instead of add violation You call build violation, but then you need to specify a lot more info You need to invoke the ads ads path method to tell, you know, where it's it's more complex But there are examples in core, but generally you just call add violation And it does all the rest for you Anyone else? Nope, okay, um, so I'm gonna be honest. I never evaluate evaluated the session before so if I have zero evaluations I'm not going to be mad at you Um, but if you do feel generous and kind, please validate it. It's my first time speaking at Drupal con So I was actually really stoked and psyched Um And nervous But um, yeah, let me know what you thought Um, so thank you and now a very shameless plug We're hiring but also we have these cool t-shirts now And my colleague ash up front has a couple of them So if you want one of these t-shirts with that little rocket on it for the future of work concept, um, just contact him Um, you know first one gets one, I guess um, so if you're interested in Working remotely for a uk-based agency or working from the uk if you're interested in a mandatory five weeks sabbatical Or as I was just telling my colleague I took a nap before this session if you're interested in being paid to take a nap Come talk to me. All right