WEBVTT

NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com

00:00:01.036 --> 00:00:03.716 align:middle
Our Content Browser is sort of working.

00:00:04.016 --> 00:00:05.746 align:middle
We can see our one location...

00:00:05.806 --> 00:00:08.406 align:middle
we just don't have any results yet.

00:00:09.346 --> 00:00:12.776 align:middle
That's because, for whatever
location is selected,

00:00:13.026 --> 00:00:15.516 align:middle
the Content Browser calls getSubItems().

00:00:16.136 --> 00:00:18.236 align:middle
Our job here is to return the results.

00:00:18.466 --> 00:00:20.476 align:middle
In this case, all of our recipes.

00:00:21.106 --> 00:00:26.346 align:middle
If we had multiple locations, like
recipes divided into categories,

00:00:26.526 --> 00:00:30.006 align:middle
we could use the $location
variable to return the subset.

00:00:30.446 --> 00:00:32.986 align:middle
But we'll query and return all recipes.

00:00:33.576 --> 00:00:37.456 align:middle
To do that, go to the top of the
class and create a constructor

00:00:38.006 --> 00:00:41.366 align:middle
with private RecipeRepository $recipeRepository.

00:00:42.906 --> 00:00:51.836 align:middle
Then, down here in getSubItems(), say $recipes =
$this-&gt;recipeRepository and use that same method

00:00:51.896 --> 00:00:56.106 align:middle
from earlier:
-&gt;createQueryBuilderOrderedByNewest().

00:00:57.016 --> 00:00:59.756 align:middle
Below add -&gt;setFirstResult($offset)...

00:01:00.616 --> 00:01:02.836 align:middle
and -&gt;setMaxResults($limit).

00:01:03.856 --> 00:01:07.476 align:middle
The Content Browser comes
with pagination built-in.

00:01:07.816 --> 00:01:12.786 align:middle
It passes us the offset and limit
for whatever page the user is on,

00:01:12.946 --> 00:01:16.856 align:middle
we plug it into the query,
and everyone is happy.

00:01:16.856 --> 00:01:19.876 align:middle
Finish with getQuery() and getResult().

00:01:21.376 --> 00:01:24.536 align:middle
Notice that getSubItems() returns an iterable...

00:01:25.076 --> 00:01:30.316 align:middle
actually it's supposed to be an iterable
of something called an ItemInterface.

00:01:31.036 --> 00:01:34.416 align:middle
So we can't just return these Recipe objects.

00:01:35.026 --> 00:01:42.826 align:middle
Instead, in src/ContentBrowser/, create another
class called, how about RecipeBrowserItem.

00:01:42.826 --> 00:01:48.636 align:middle
Make this implement ItemInterface -
the one from Netgen\ContentBrowser -

00:01:49.326 --> 00:01:51.956 align:middle
then generate the four methods it needs.

00:01:52.686 --> 00:01:57.036 align:middle
This class will be a tiny
wrapper around a Recipe object.

00:01:57.786 --> 00:02:02.766 align:middle
Watch: add a __construct() method
with private Recipe $recipe.

00:02:04.736 --> 00:02:12.776 align:middle
Now, for getValue(), this should return the
"identifier", so return $this-&gt;recipe-&gt;getId().

00:02:14.256 --> 00:02:20.366 align:middle
For getName(), we just need something visual
we can show, like $this-&gt;recipe-&gt;getName().

00:02:21.726 --> 00:02:24.106 align:middle
And for isVisible(), return true.

00:02:24.866 --> 00:02:28.676 align:middle
That's useful if a Recipe could
be published or unpublished.

00:02:29.316 --> 00:02:32.396 align:middle
We have a similar situation with isSelectable().

00:02:32.926 --> 00:02:37.796 align:middle
If you had a set of rules where
you wanted to show certain recipes

00:02:38.016 --> 00:02:42.186 align:middle
but make them not selectable,
you could return false here.

00:02:42.756 --> 00:02:43.946 align:middle
And... we're done!

00:02:44.046 --> 00:02:44.916 align:middle
That was easy!

00:02:45.536 --> 00:02:48.126 align:middle
Back over in our backend class, we need

00:02:48.126 --> 00:02:52.706 align:middle
to turn these Recipe objects
into RecipeBrowserItem objects.

00:02:53.426 --> 00:02:55.306 align:middle
We can do that with array_map().

00:02:56.106 --> 00:03:02.296 align:middle
I'll use the fancy fn() syntax again, which
will receive a Recipe $recipe argument,

00:03:02.466 --> 00:03:06.536 align:middle
followed by =&gt; new RecipeBrowserItem($recipe).

00:03:07.936 --> 00:03:10.286 align:middle
For the second arg, pass $recipes.

00:03:11.546 --> 00:03:16.566 align:middle
This is a fancy way of saying: Loop
over all the recipes in the system,

00:03:16.826 --> 00:03:23.046 align:middle
create a new RecipeBrowserItem for each
one, and return that new array of items.

00:03:23.106 --> 00:03:25.726 align:middle
All right, let's see what this looks like!

00:03:26.116 --> 00:03:33.246 align:middle
Refresh the layout, click on the
Grid, go back to "Add Items" and...

00:03:33.696 --> 00:03:36.426 align:middle
got it! We see ten items!

00:03:37.376 --> 00:03:39.826 align:middle
But we should have multiple pages.

00:03:40.486 --> 00:03:44.766 align:middle
Ah, that's because we're still
returning 0 from getSubItemsCount().

00:03:45.156 --> 00:03:45.866 align:middle
Let's fix that.

00:03:46.436 --> 00:03:47.866 align:middle
Steal the query from above...

00:03:49.046 --> 00:03:55.396 align:middle
paste, return this, remove
setFirstResult() and setMaxResults(),

00:03:55.836 --> 00:04:03.486 align:middle
add -&gt;select('COUNT(recipe.id)'), and then
call getSingleScalarResult() at the bottom.

00:04:04.696 --> 00:04:07.876 align:middle
And just like that, when we refresh...

00:04:08.726 --> 00:04:10.326 align:middle
and open the Content Browser...

00:04:11.736 --> 00:04:13.656 align:middle
we have pages!

00:04:14.626 --> 00:04:17.296 align:middle
Ok, but could we search for recipes?

00:04:18.006 --> 00:04:18.886 align:middle
Absolutely.

00:04:19.326 --> 00:04:21.976 align:middle
We can leverage search() and searchCount().

00:04:22.816 --> 00:04:23.616 align:middle
This is simple.

00:04:23.786 --> 00:04:31.396 align:middle
Steal all of the logic from getSubItems(),
paste into search() and pass $searchText

00:04:31.396 --> 00:04:35.676 align:middle
to the QueryBuilder method, which
already allows this argument.

00:04:36.536 --> 00:04:40.996 align:middle
If you want to have a bit less code
duplication, you could isolate this

00:04:40.996 --> 00:04:43.136 align:middle
into a private method at the bottom.

00:04:44.246 --> 00:04:46.716 align:middle
Also copy the logic from
the other count method...

00:04:47.166 --> 00:04:51.036 align:middle
paste that into searchCount(),
and pass it $searchText as well.

00:04:52.436 --> 00:04:56.606 align:middle
And just like that, if we move
over here and try to search...

00:04:57.046 --> 00:04:59.646 align:middle
it works. That's awesome!

00:05:00.766 --> 00:05:06.986 align:middle
Alright - select a few items,
hit "Confirm" and...

00:05:07.676 --> 00:05:09.766 align:middle
oh no! It breaks!

00:05:10.146 --> 00:05:11.816 align:middle
It still says "Loading".

00:05:12.566 --> 00:05:17.096 align:middle
If you look down on the web debug
toolbar, we have a 400 error.

00:05:17.526 --> 00:05:25.216 align:middle
Dang. When we open that up, we see: value loader
for "doctrine recipe" value type does not exist.

00:05:25.776 --> 00:05:31.616 align:middle
There's just one final piece we need: A
very simple class called the "value loader".

00:05:31.946 --> 00:05:32.976 align:middle
That's next.

