WEBVTT

NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com

00:00:01.016 --> 00:00:04.676 align:middle
We can now embed lists, grids,
or thumb galleries

00:00:04.676 --> 00:00:07.616 align:middle
of recipes into any layout dynamically.

00:00:08.176 --> 00:00:09.316 align:middle
That's super cool.

00:00:09.886 --> 00:00:14.346 align:middle
And we could always create more
query types to, for example,

00:00:14.586 --> 00:00:19.166 align:middle
choose between the latest
recipes or most popular recipes.

00:00:19.916 --> 00:00:23.606 align:middle
But what about being able
to manually select recipes?

00:00:24.086 --> 00:00:28.606 align:middle
Maybe we want to feature four
specific recipes on the homepage.

00:00:29.456 --> 00:00:33.946 align:middle
In the layouts area, on the grid,
if you change the Collection type,

00:00:34.446 --> 00:00:37.326 align:middle
we can switch to "Manual collection".

00:00:38.116 --> 00:00:39.216 align:middle
But then...

00:00:39.246 --> 00:00:41.986 align:middle
we can't actually select any items.

00:00:42.906 --> 00:00:47.046 align:middle
To allow items (in our case,
recipes) to be selected manually,

00:00:47.526 --> 00:00:50.166 align:middle
we first need to allow that in the config.

00:00:50.886 --> 00:00:57.436 align:middle
Earlier, when we created the value_types
key, we set manual_items to false.

00:00:58.206 --> 00:00:59.706 align:middle
Change that to true.

00:01:00.546 --> 00:01:05.516 align:middle
And now, when we try the page,
we're greeted with an error!

00:01:06.146 --> 00:01:11.746 align:middle
Netgen Content Browser backend for
doctrine_recipe value type does not exist Yep!

00:01:12.146 --> 00:01:16.686 align:middle
We need to implement a class that
helps Layouts browse our recipes.

00:01:17.276 --> 00:01:19.306 align:middle
That's called a "content browser".

00:01:20.146 --> 00:01:24.626 align:middle
Adding a content browser is actually
done by a completely different bundle,

00:01:24.996 --> 00:01:28.046 align:middle
which you can use outside of Netgen Layouts.

00:01:28.566 --> 00:01:33.086 align:middle
It's handy if you need a nice interface
for browsing and selecting items.

00:01:33.856 --> 00:01:38.016 align:middle
Since the content browser lives in a
different bundle, it's not required,

00:01:38.376 --> 00:01:44.016 align:middle
but I'm going to configure this with a new
config file called netgen_content_browser.yaml.

00:01:44.776 --> 00:01:48.416 align:middle
Inside, set the root key
to netgen_content_browser

00:01:48.846 --> 00:01:52.246 align:middle
to configure the "NetgenContentBrowserBundle".

00:01:53.146 --> 00:01:58.856 align:middle
Inside of this, we get to describe
all of the different "manual things"

00:01:59.246 --> 00:02:01.616 align:middle
that we want to be able to browse.

00:02:02.456 --> 00:02:07.256 align:middle
To do that, add an item_types
key, and, for the first item,

00:02:07.546 --> 00:02:11.326 align:middle
go grab the value type's
internal name - doctrine_recipe -

00:02:11.516 --> 00:02:15.386 align:middle
so that these match, paste,
then give this a name.

00:02:16.046 --> 00:02:16.896 align:middle
How about...

00:02:16.976 --> 00:02:20.236 align:middle
Recipes with a cute strawberry icon.

00:02:21.076 --> 00:02:26.116 align:middle
The only other thing we need here is
a preview key with a template sub-key,

00:02:26.396 --> 00:02:32.956 align:middle
which I'll set to nglayouts/content_browser
/recipe_preview.html.twig.

00:02:33.516 --> 00:02:36.786 align:middle
Oh! And make sure you spell
"template" correctly.

00:02:37.046 --> 00:02:41.236 align:middle
Whoops! Anyways, we're setting
this preview template

00:02:41.726 --> 00:02:45.106 align:middle
because the configuration requires us to...

00:02:45.506 --> 00:02:48.466 align:middle
but we'll worry about creating
that template later.

00:02:49.316 --> 00:02:51.116 align:middle
If we head over and refresh...

00:02:51.446 --> 00:02:53.486 align:middle
we get the same error.

00:02:54.086 --> 00:02:59.886 align:middle
That's because we need a backend class
that will connect to this new item type.

00:03:00.616 --> 00:03:06.516 align:middle
Creating a backend is a simple process, but
it does require a few different classes.

00:03:07.246 --> 00:03:11.426 align:middle
In the src/ directory, let's create a
new directory called ContentBrowser/...

00:03:12.006 --> 00:03:16.466 align:middle
and inside of that, a PHP class
called RecipeBrowserBackend.

00:03:17.446 --> 00:03:22.546 align:middle
This needs to implement BackendInterface:
the one from Netgen\ContentBrowser\Backend.

00:03:23.406 --> 00:03:30.846 align:middle
Then, go to Code Generate (or "command" + "N" on
a Mac) to implement the nine methods this needs!

00:03:31.356 --> 00:03:33.706 align:middle
Don't worry: it's not as bad as it looks.

00:03:34.586 --> 00:03:38.866 align:middle
Finally, to link this backend class
to the item type in our config,

00:03:39.346 --> 00:03:41.866 align:middle
we need to give this service a tag.

00:03:42.736 --> 00:03:48.556 align:middle
We'll do this the same way we did earlier
for the query type: with AutoconfigureTag.

00:03:49.106 --> 00:03:53.186 align:middle
In fact, I'll steal this
AutoconfigureTag since I'm here...

00:03:53.646 --> 00:03:54.176 align:middle
paste that...

00:03:54.516 --> 00:03:56.466 align:middle
and add the use statement for it.

00:03:57.306 --> 00:04:02.126 align:middle
This time, the tag name is
netgen_content_browser.backend,

00:04:02.746 --> 00:04:05.816 align:middle
and instead of type, use item_type.

00:04:06.566 --> 00:04:11.086 align:middle
Set this to the key we have in
the config: doctrine_recipe.

00:04:11.776 --> 00:04:12.946 align:middle
Paste and...

00:04:13.376 --> 00:04:16.236 align:middle
cool! This time when we refresh...

00:04:17.476 --> 00:04:19.356 align:middle
the error is gone.

00:04:20.016 --> 00:04:22.826 align:middle
Let's temporarily add a new
Grid to the layout...

00:04:23.616 --> 00:04:25.556 align:middle
and choose "Manual collection".

00:04:26.496 --> 00:04:28.026 align:middle
Now... check it out!

00:04:28.446 --> 00:04:32.436 align:middle
Because we have a backend,
we see an "Add Items" button!

00:04:33.046 --> 00:04:34.526 align:middle
And when we click it...

00:04:34.946 --> 00:04:38.946 align:middle
it fails. That shouldn't be too surprising...

00:04:39.106 --> 00:04:42.206 align:middle
since our backend class is
still completely empty.

00:04:42.946 --> 00:04:46.826 align:middle
If you want to see the exact error,
you could open up the AJAX call.

00:04:47.626 --> 00:04:52.046 align:middle
The content browser system works
like this: in these methods,

00:04:52.246 --> 00:04:56.716 align:middle
we describe a "tree structure",
kind of like a filesystem.

00:04:57.376 --> 00:05:03.536 align:middle
"Locations" are like directories and
"items" are like the "files", or,

00:05:03.676 --> 00:05:06.456 align:middle
in our case, the individual recipes.

00:05:07.346 --> 00:05:09.216 align:middle
We're going to keep things really simple.

00:05:09.636 --> 00:05:15.366 align:middle
Instead of having different "directories" or
"categories" of recipes that you can browse,

00:05:15.736 --> 00:05:22.586 align:middle
we're going to have a single directory - or
"location" - that all recipes will live inside.

00:05:23.246 --> 00:05:26.876 align:middle
You'll see what this looks like
in the UI in a few minutes.

00:05:27.816 --> 00:05:31.156 align:middle
To get this working, inside src/ContentBrowser/,

00:05:31.606 --> 00:05:35.046 align:middle
we need to create a class
that represents a location.

00:05:35.716 --> 00:05:38.216 align:middle
I'll call it BrowserRootLocation.

00:05:39.176 --> 00:05:40.076 align:middle
This class...

00:05:40.306 --> 00:05:46.406 align:middle
isn't super interesting: it's just some
low-level plumbing that we must have.

00:05:47.396 --> 00:05:53.346 align:middle
Make this implement LocationInterface, and
below, generate the three methods we need.

00:05:54.216 --> 00:05:58.956 align:middle
Again, this class will represent
the one and only "location".

00:05:59.406 --> 00:06:03.316 align:middle
So for getLocationId(), we can return anything.

00:06:03.906 --> 00:06:05.386 align:middle
I'm going to return 0.

00:06:06.176 --> 00:06:08.186 align:middle
You'll see how that's used in a second.

00:06:09.056 --> 00:06:13.376 align:middle
For getName(), this is what will
be displayed in the admin section.

00:06:14.016 --> 00:06:15.496 align:middle
I'll return 'All'.

00:06:16.216 --> 00:06:18.756 align:middle
And for getParentId(), return null.

00:06:19.736 --> 00:06:23.906 align:middle
If you have a more complex system
with multiple sub-directories,

00:06:24.156 --> 00:06:27.256 align:middle
you could create a hierarchy of locations.

00:06:28.076 --> 00:06:31.246 align:middle
All right, let's update our
backend class to use this.

00:06:31.936 --> 00:06:38.196 align:middle
Up here, getSections() will be called as soon
as the user opens up the content browser.

00:06:38.926 --> 00:06:43.916 align:middle
Our job is to return all of the
root "directories" - or "locations".

00:06:44.476 --> 00:06:49.956 align:middle
We have just one: return
[new BrowserRootLocation()].

00:06:50.836 --> 00:06:56.446 align:middle
After this is called, the content browser
will call getLocationId() on each one

00:06:56.896 --> 00:07:00.646 align:middle
and make an AJAX request to get
more information about them.

00:07:01.366 --> 00:07:06.686 align:middle
For us, this will happen just
one time where the id is 0.

00:07:07.316 --> 00:07:15.886 align:middle
It looks weird, but all we need to do is
return that same location: if ($id === '0'),

00:07:16.346 --> 00:07:19.486 align:middle
then return new BrowserRootLocation().

00:07:20.356 --> 00:07:23.866 align:middle
Notice I'm using '0' as a string, but...

00:07:23.956 --> 00:07:27.516 align:middle
in getLocationId() we returned an integer.

00:07:28.316 --> 00:07:33.736 align:middle
That's because the id will be passed
into JavaScript and used in an Ajax call.

00:07:34.606 --> 00:07:36.986 align:middle
By the time it gets here, it'll be a string.

00:07:37.486 --> 00:07:39.296 align:middle
A small detail to keep in mind.

00:07:40.156 --> 00:07:45.156 align:middle
At the end, just in case throw a
new \InvalidArgumentException()

00:07:45.526 --> 00:07:48.616 align:middle
and pass a message about an invalid location.

00:07:49.386 --> 00:07:52.586 align:middle
Ok! So our backend has one location.

00:07:53.366 --> 00:07:57.496 align:middle
For the other methods, let's
return the simplest thing possible.

00:07:58.346 --> 00:08:04.086 align:middle
Leave loadItem() empty for a moment,
for getSubLocations(), return [],

00:08:04.606 --> 00:08:10.886 align:middle
for getSubLocationsCount(), return
0, for getSubItems(), return [],

00:08:11.416 --> 00:08:17.076 align:middle
for getSubItemsCount(), return
0, for search(), return []...

00:08:17.656 --> 00:08:21.286 align:middle
and finally, for searchCount(), return 0.

00:08:22.146 --> 00:08:25.716 align:middle
Phew... We'll talk about
each of those methods later.

00:08:26.316 --> 00:08:30.456 align:middle
But our backend class is at
least somewhat functional now.

00:08:31.446 --> 00:08:33.836 align:middle
If we refresh the admin area again...

00:08:36.366 --> 00:08:40.116 align:middle
click on our grid, and go to "Add Items"...

00:08:40.706 --> 00:08:44.556 align:middle
it loads! Say "hello" to the content browser!

00:08:45.046 --> 00:08:51.286 align:middle
It's currently empty, but you can see the
"All", which is from our one location.

00:08:51.946 --> 00:08:54.306 align:middle
There are no items inside yet...

00:08:54.816 --> 00:08:58.886 align:middle
because we need to return
them from getSubItems().

00:08:59.286 --> 00:09:00.986 align:middle
Let's do that next

