Defining Model Relationships0:00
You're getting the hang of fields now, right? They're very simple to add, but they're very powerful and very versatile. But models in Laravel applications are really isolated things. They're often related to other models inside your database. For example, a Book might need a Publisher. That's another model. An Author. That's another model. Perhaps people can come into our library and listen to audio recordings of books. That has many relationships. A Book has a Genre and a SubGenre, which are likely models inside our database. So if Nova is going to be a full-fledged administration panel, it needs to provide a way of defining relationships between models. Thankfully, it does, and it's incredibly simple. Let's take a look now. Before we dive in, note that I've gone ahead and added the resources necessary for this episode. So I've created Authors and Genres and Publishers and Recordings. The source code for those
Adding BelongsTo Author0:59
Before we dive in, note that I've gone ahead and added the resources necessary for this episode. So I've created authors and genres and publishers and recordings. The source code for those resources is linked below, so go and check them out. But they're very similar to how we define books. There's nothing special going on. Let's go ahead and first of all create a link between a book and an author. Inside our Nova Book resource, perhaps under title, I'm going to pull in a new field called BelongsTo. That's right. Just like text and tricks and avatar, your relationships are defined using Nova fields. So in BelongsTo, I'll call the make method. And rather than using an attribute, a column name here, I'm instead going to use the method name of the relationship on the model. So if I jump into the Book model, note that we have an author method, which is a BelongsTo relationship. So here, I'll call author. And with no more code than that, if I dive into the index again,
So if I jump into the Book model, note that we have an author method, which is a BelongsTo relationship. So here, I'll call author. And with no more code than that, if I dive into the index again, note that we now see the author for each and every Book. If I click on this link, I'm taken to the authors page. And if I click to edit a Book, I'm able to select from a dropdown to choose from the list of authors. In this case, George Orwell. Note that by default, the relationship is required. I have to choose a value. You can alter that if it makes sense by chaining the nullable method onto the relationship. If you do that, we're now able to select null as an option from this dropdown. For an author, however, it makes a lot of sense that we have to require an author for a Book. All Books have an author. So we'll leave that as required by default. Another method that we can add to our relationships is sortable, much like a standard field.
Sorting and Searching Relations2:44
All books have an author. So we'll leave that as required by default. Another method that we can add to our relationships is sortable, much like a standard field. If I add that to the author, sortable, then when I go back into the browser, I'm now able to click this dropdown in order to group, basically, by the author. Now, this is done using the author's ID. So it's not going to be an alphabetical order, but it does allow us to basically group by author to very easily see all the books an author has created. In the same vein, it would be nice if I were able to search, for example, Jane Austen here and see all the books written by Jane Austen. Well, it turns out you can search on related fields in Nova. If we jump back into our resource and head up to the search parameter at the top here, I can add a new line, and I'll search on the author relationship and use the name attribute.
and head up to the search parameter at the top here, I can add a new line, and I'll search on the author relationship and use the name attribute. So I'm searching on the author. I use a dot. So this is standard dot syntax that you're probably used to in Laravel, and then I'm interested in the name attribute. If I now search for Jane Austen again, note that I just see Jane Austen's books. So Nova's searchable relations is incredibly useful for situations like this one. Okay, that's author. How about we add publisher? Publisher will work a little bit differently, but the basics are the same. I'm going to create another belongsTo relationship. I'll make it. I'll call publisher, seeing as that's the name of the method on the model, and we should now see the publisher in the index. Now, in our case, I don't really want to see the publisher in the index.
seeing as that's the name of the method on the model, and we should now see the publisher in the index. Now, in our case, I don't really want to see the publisher in the index. I just want to see them inside the detail view, and obviously when we're creating or editing a book. So in order to solve that, I can call the hideFromIndex method, and sure enough, on the index, we'll no longer see the publisher. However, much as we can search for the name of an author, it would be nice to filter based on the publisher of a book, and as we've already covered on fields, I can add the filterable method. If I add the filterable method, we'll now see a filter in this dropdown here where I can select a specific publisher to limit books to, and now I only see that publisher's books. Some very powerful methods that you can use on relationships to build out the functionality for your particular Nova resources. That's belongsTo relationships.
Adding HasMany Recordings5:06
Some very powerful methods that you can use on relationships to build out the functionality for your particular Nova resources. That's belongsTo relationships. The other basic relationship type is hasMany, and for a book, a Book has many Recordings. It has audio recordings that we can upload. You define in a very similar way. Let's go back. I'm going to come to the bottom of the fields here just because that's where I like to place my hasMany relationships, and I'll pull in, once more, the hasMany field. I'll call the make method, and we'll add recordings. Now, if I wanted to call this audioRecordings, things are going to break. Let me show you. I'll refresh this. Note that it says the class App\Nova\AudioRecording is not found, so we have to explain to Nova how this works. We can add a parameter called resource.
I'll refresh this. Note that it says the class App\Nova\AudioRecording is not found, so we have to explain to Nova how this works. We can add a parameter called resource where we give the fully qualified class name of the Nova resource this links to. In this case, it will be the Recording class. If I add that and then refresh the page, I'm going to see all of my books again, and let's go and search for Emma, the book Emma. Inside here, note that we now get a new problem called to undefined method audioRecordings, and that's because on a Book, we don't have a method called audioRecordings. In fact, if I go to Book, we just call it recordings. You could either change this method name, which is not the best idea, or perhaps more likely, you can add a second parameter here,
You could either change this method name, which is not the best idea, or perhaps more likely, you can add a second parameter here, which is going to be the name of the method inside your model, recordings in this case. Once we've added that, this page will load without issue, and down at the bottom in a separate panel, we see audio recordings. These parameters come in useful if the title you want to use is not the same as the method and resource that you want to link. Note that basically, in our audio recordings panel, we have a separated index. That makes a lot of sense, doesn't it? We're able to search on the title, so I could search, for example, for chapter one,
That makes a lot of sense, doesn't it? We're able to search on the title, so I could search, for example, for chapter one, to just see Emma chapter one, and I'm able to create a recording directly from here. Now, if I click create recording, I'm taken to the resources/create page, and it pre-fills this relationship with the book, Emma. Really nice. Let's add a new chapter, so I'm going to add chapter three. I'll choose a file, and I'll pick the appropriate file for chapter three. Once I've done that, I can create the recording, and note that we have this awesome inline audio field that Nova provides for us,
Once I've done that, I can create the recording, and note that we have this awesome inline audio field that Nova provides for us, where I could play this chapter directly from the administration panel. In our case, I'm going to click this link here to go back to the book, Emma, and now note that we have chapter three up here. I've pre-fixed these with Emma, so I'm going to go in and edit this, and I'll change this to Emma chapter three. I'll click update recording. The audio field is required. Ah, so here we have a slight issue that we need to fix inside our resource.
The audio field is required. Ah, so here we have a slight issue that we need to fix inside our Recording resource. If I jump into the Recording resource, here I have audio, and I've set the rules to required, but really it's only required when you're creating. So I'll add required there, and I'll remove it from here. If I do that, I should now be able to update the Recording, and everything works as expected. One last thing with this index that would be nice, note that we don't see the audio recording inline on the index here,
One last thing with this index that would be nice, note that we don't see the audio recording inline on the index here, but I think it would be really nice if we could see that audio recording inline. Certain fields don't show on the index by default, but you can force them to show using another method. We'll jump into the Recording resource, and here on the audio field I'm going to add a new method called showOnIndex. If we add that method, inside the hasMany resource, we now see all of these audio items, and I'm able to click to play these inline.
Peeking Related Details9:13
we now see all of these audio items, and I'm able to click to play these inline. Super cool. Whilst we're in the details view, notice that we have the author and publisher shown here, and we obviously have links to these items, but the links don't do anything other than show us the title we've defined on the resource, and allow us to click to see that individual item. Occasionally, it would be nice to get a bit more information about this relationship without having to click into it,
Occasionally, it would be nice to get a bit more information about this relationship without having to click into it, and Nova supports that functionality as well. Inside the related resource, I'm going to choose which fields I would like to see. I'll select the avatar, the name, and the biography, and I can drop down and add the showWhenPeeking method to these fields. With that done, if I come back and now hover over the author, note that we have a pop-up that appears that shows me those fields. This is even more useful in the index where we have the author displayed here. Rather than having to jump into each of these,
This is even more useful in the index where we have the author displayed here. Rather than having to jump into each of these, I can quickly scroll through. I can see the biography for each one, and I'm able to even toggle that on or off to read it and then hide it. I don't have to jump into the resource itself. It's enough for me to see exactly what's taking place, and I can carry on browsing the index. So show when peeking makes working with relationships so much easier. There are a couple more relationships I want to add,
So show when peaking makes working with relationships so much easier. There are a couple more relationships I want to add, and they're basic belongsTo relationships for our genre and sub-genre. Let's add them now. Back inside our Book resource, I'll add a new belongsTo relationship for the genre, and I'll add a belongsTo relationship for the sub-genre. Let's see if that works. Note that we have an error that says the sub-genre is not found. It's the same error we saw earlier.
Note that we have an error that says the sub-genre is not found. It's the same error we saw earlier. It basically means that I need to add a field here for the resource fully qualified class name, which is going to be genre. And that's obviously because it's not able to work out that the actual resource backing sub-genre is actually genre. So we can hard-code it in order to tell Nova how that works. If we refresh, we should now see the genre and sub-genre for each item here. Now, whilst we're on the topic,
Customizing Genre Resource Fields11:30
If we refresh, we should now see the genre and sub-genre for each item here. Now, whilst we're on the topic, the genre resource has a couple of cool things I'd just like to show you. So let's dive into the genre resource. First of all, note that we're using the searchable relationship, so you're able to search not only for a genre, but also for its parent genre if it is a sub-genre. I'm also able to support eager loading. So if in Nova there are resources you always want access to without having to make lazy database calls,
So if in Nova there are resources you always want access to without having to make lazy database calls, you can add the with parameter, very similar to how you add the with parameter in Eloquent Models. And it will automatically load that relationship or those relationships in whenever this resource is loaded. I'm going to skip through fields, but note that we define, for example, the name, and belongsTo, and hasMany, and hasMany. So relationship fields.
and belongs to, and hasMany, and hasMany. So relationship fields. But check this method out. If you want to change how things are displayed based on where they're displayed, you can add separated fields on a resource. In this case, if we are on an index of genres, I'm going to show a different set of fields. I'll show the ID and make it sortable, but then I'm going to show a computed text field.
I'll show the ID and make it sortable, but then I'm going to show a computed text field. So rather than just showing the name directly from the database, I'm actually going to check if this is a sub-genre, and if it is, I'll show a sub-genre name, followed by a forward slash, followed by the parent name. Otherwise, I'm just going to show the name on its own. What does that look like in reality? Well, if I go to the genres resource, you can see on the index here, if it's a sub-genre,
Well, if I go to the genres resource, you can see on the index here, if it's a sub-genre, I see the sub-genre, followed by the genre. If it's a top-level genre, I just see that. I think it's incredibly powerful that based on where we are inside the application, we're able to change how fields are displayed. We can say, only show these fields on the index, only show these fields on detail, only show these fields when creating or updating,
only show these fields on detail, only show these fields when creating or updating, and that will take precedence over our default fields method. If I head back to the book Emma, we can see now the genre and sub-genre, but if we edit, we actually come across a bit of a problem. From the dropdowns here, I can select any genre, let's say romance, and I can select any sub-genre, let's say hard science fiction. Now in the database, that sub-genre is not linked to that genre.
let's say hard science fiction. Now in the database, that sub-genre is not linked to that genre. However, there's nothing stopping me selecting it in this dropdown. That is a real problem, and it's something that Nova provides a solution to. I'll show you in the next episode.
