WEBVTT

NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com

00:00:01.026 --> 00:00:04.776 align:middle
As soon as we changed our Grid
type to use a Dynamic collection...

00:00:04.956 --> 00:00:06.756 align:middle
it stopped loading.

00:00:07.096 --> 00:00:10.366 align:middle
The error is hiding down here in this AJAX call.

00:00:11.046 --> 00:00:14.506 align:middle
The best way to see it is to
open that URL in a new tab.

00:00:16.236 --> 00:00:21.396 align:middle
There we go: Value converter for
App\Entity\Recipe type does not exist.

00:00:22.196 --> 00:00:28.996 align:middle
Okay, so far, we've created a custom "value
type" for Recipe, which was just this config,

00:00:29.406 --> 00:00:34.406 align:middle
and a custom "query type" which allows
us to load a list of the latest recipes

00:00:34.596 --> 00:00:38.516 align:middle
by running the query inside
of the associated class.

00:00:38.516 --> 00:00:41.766 align:middle
Now we're getting this value converter error.

00:00:42.376 --> 00:00:49.746 align:middle
A value converter is really simple: it's a class
that transforms the underlying object - Recipe -

00:00:49.936 --> 00:00:52.696 align:middle
into a format that Layouts can understand.

00:00:53.346 --> 00:00:59.896 align:middle
In that same src/Layouts/ directory, let's
create a RecipeValueConverter class...

00:01:00.046 --> 00:01:03.996 align:middle
and make it implement ValueConverterInterface.

00:01:04.046 --> 00:01:08.256 align:middle
You know the drill: go to Code -&gt;
Generate (or "command" + "N" on a Mac)

00:01:08.536 --> 00:01:13.476 align:middle
and hit "Implement Methods"
to generate the seven we need.

00:01:13.886 --> 00:01:18.496 align:middle
I know, that sounds like a lot, but
these are super easy to fill in.

00:01:18.496 --> 00:01:24.516 align:middle
First, for supports(), Layouts will
call this method every time it has a

00:01:24.516 --> 00:01:26.546 align:middle
"value" it doesn't understand.

00:01:27.116 --> 00:01:34.036 align:middle
We want to tell it that we know how to convert
the $object if it's an instanceof Recipe.

00:01:34.196 --> 00:01:41.936 align:middle
Second, for getValueType(), return the internal
key of our value type: doctrine_recipe.

00:01:44.966 --> 00:01:46.346 align:middle
Next is getId()...

00:01:46.686 --> 00:01:51.626 align:middle
and we're literally going to return
our ID with $object-&gt;getId().

00:01:51.626 --> 00:01:57.816 align:middle
We don't have autocomplete on this, but
we know this object will be a Recipe.

00:01:57.906 --> 00:02:03.886 align:middle
For getRemoteId(), just return
$this-&gt;getId($object).

00:02:03.886 --> 00:02:10.996 align:middle
This method is only important if you plan to
use the import feature in Layouts to move data,

00:02:11.196 --> 00:02:14.026 align:middle
for example, between staging and production.

00:02:14.856 --> 00:02:20.536 align:middle
If were planning to do that, you could give
your objects a UUID and return that here.

00:02:21.786 --> 00:02:27.116 align:middle
Down here, for getName(), this will be a
human-readable name shown in the admin area.

00:02:27.846 --> 00:02:33.596 align:middle
This time, to help my editor, let's
assert() that $object instanceof Recipe.

00:02:34.596 --> 00:02:35.676 align:middle
Two things about this.

00:02:35.986 --> 00:02:42.236 align:middle
First, we know that this object will always
be a Recipe because, up in supports(),

00:02:42.326 --> 00:02:45.236 align:middle
we said that's that only object we support.

00:02:45.946 --> 00:02:49.446 align:middle
Second, if you haven't seen
the assert() function before,

00:02:49.846 --> 00:02:54.586 align:middle
if the $object is not an instanceof
Recipe, it will throw an exception.

00:02:55.186 --> 00:03:00.316 align:middle
It's a really easy way to tell your
editor - or other tools like PHPStan -

00:03:00.546 --> 00:03:03.986 align:middle
that the object is definitely
an instance of Recipe....

00:03:04.176 --> 00:03:09.836 align:middle
which means now we get autocompletion
when we say return $object-&gt;getName().

00:03:09.836 --> 00:03:12.936 align:middle
Next is getIsVisible().

00:03:13.266 --> 00:03:14.656 align:middle
Just return true.

00:03:15.496 --> 00:03:20.866 align:middle
If your recipes could be published or
unpublished, for example, then you could check

00:03:20.866 --> 00:03:23.226 align:middle
that here to return true or false.

00:03:23.226 --> 00:03:28.276 align:middle
Finally, for getObject(), return $object.

00:03:28.276 --> 00:03:34.486 align:middle
I know, that seems a bit silly, but this is
a handy way for you to change your Recipe

00:03:34.636 --> 00:03:36.776 align:middle
after it's loaded if you needed to.

00:03:37.096 --> 00:03:40.226 align:middle
But that's not something that we need to do.

00:03:40.226 --> 00:03:41.416 align:middle
And... done!

00:03:41.976 --> 00:03:48.886 align:middle
This time, unlike with the query type handler,
autoconfiguration takes care of everything...

00:03:49.136 --> 00:03:51.936 align:middle
so we don't need to add a manual tag up here.

00:03:52.516 --> 00:03:56.836 align:middle
Watch: move over and try
refreshing the AJAX endpoint first.

00:03:58.076 --> 00:03:59.136 align:middle
That works!

00:03:59.136 --> 00:04:01.676 align:middle
Now go over, refresh the layouts admin page...

00:04:02.976 --> 00:04:05.436 align:middle
and whoa. Check it out!

00:04:05.746 --> 00:04:08.596 align:middle
We see a bunch of items on our Grid!

00:04:09.406 --> 00:04:13.166 align:middle
If we click that, we see
the items loading below.

00:04:13.356 --> 00:04:14.686 align:middle
That's awesome!

00:04:15.296 --> 00:04:18.886 align:middle
Notice that we only had to
choose "dynamic collection".

00:04:19.236 --> 00:04:25.206 align:middle
We... never told the system that we wanted
to use the "latest recipes" query type.

00:04:25.906 --> 00:04:29.836 align:middle
That's simply because we
only have one query type...

00:04:30.136 --> 00:04:32.866 align:middle
and so Layouts guessed that's what we wanted.

00:04:33.446 --> 00:04:39.206 align:middle
If we added a second query type to the system,
we would see another select drop-down here

00:04:39.446 --> 00:04:45.136 align:middle
where we could choose between latest recipes
and "most popular" recipes, for example.

00:04:45.206 --> 00:04:51.456 align:middle
So this is using our "latest
recipes" query type to get 25 results.

00:04:51.966 --> 00:04:57.256 align:middle
If we were trying to recreate this
area here, we would only want 4.

00:04:57.256 --> 00:05:00.476 align:middle
So let's limit the number of items to four.

00:05:00.986 --> 00:05:04.746 align:middle
Cool! What does this look like on the frontend?

00:05:05.316 --> 00:05:06.136 align:middle
Let's find out!

00:05:06.466 --> 00:05:09.606 align:middle
Hit "publish and continue editing" and....

00:05:09.606 --> 00:05:12.526 align:middle
once that saves, go over and refresh.

00:05:13.366 --> 00:05:15.646 align:middle
It should show up right here but...

00:05:15.906 --> 00:05:18.426 align:middle
we see absolutely nothing!

00:05:18.866 --> 00:05:21.586 align:middle
Or... it seems that way at first.

00:05:21.936 --> 00:05:23.736 align:middle
But when we inspect element...

00:05:24.156 --> 00:05:25.626 align:middle
and zoom in a bit...

00:05:26.286 --> 00:05:31.566 align:middle
there's a div with the class ngl-vt-grid on it.

00:05:32.056 --> 00:05:38.516 align:middle
And inside, a row and inside
of that, a bunch of empty divs.

00:05:39.116 --> 00:05:47.316 align:middle
If you ignore the clearfix elements, this
renders 1, 2, 3, 4 divs for our four items!

00:05:47.316 --> 00:05:50.216 align:middle
So the items are rendering...

00:05:50.276 --> 00:05:52.266 align:middle
they're just rendering empty.

00:05:52.776 --> 00:05:54.266 align:middle
And, that makes sense.

00:05:54.716 --> 00:05:59.206 align:middle
We haven't told layouts how recipe
items should be rendered yet.

00:05:59.576 --> 00:06:01.216 align:middle
More on that in a few minutes.

00:06:01.756 --> 00:06:06.776 align:middle
But before we get there, I want to
make our query type a tiny bit fancier.

00:06:07.696 --> 00:06:11.556 align:middle
On the first pass, we ignored
the buildParameters() method.

00:06:12.146 --> 00:06:17.616 align:middle
Whelp, it turns out that this is a
way for us to add extra form fields

00:06:17.856 --> 00:06:21.226 align:middle
so an admin user can pass options to the query.

00:06:21.256 --> 00:06:25.046 align:middle
For example, let's add an optional search term.

00:06:25.156 --> 00:06:31.726 align:middle
Say $builder-&gt;add() passing term - that will
be the internal name for this new parameter -

00:06:32.076 --> 00:06:36.246 align:middle
then TextType: the one from Netgen\Layouts.

00:06:36.796 --> 00:06:41.776 align:middle
There are a bunch of other field
types for URLs, dates and more.

00:06:42.846 --> 00:06:46.886 align:middle
With just this, when we refresh
the admin section...

00:06:47.056 --> 00:06:50.946 align:middle
and click down on the grid, there it is!

00:06:51.206 --> 00:06:53.296 align:middle
We've got a big new box!

00:06:53.646 --> 00:06:57.546 align:middle
Of course, if we type anything
inside, nothing happens...

00:06:57.846 --> 00:07:00.026 align:middle
and it also has a weird label.

00:07:00.956 --> 00:07:02.476 align:middle
Let's fix that label first.

00:07:03.016 --> 00:07:07.516 align:middle
Layouts defaults to this odd string,
but it's already running this

00:07:07.516 --> 00:07:11.646 align:middle
through the translator via
a domain called nglayouts.

00:07:11.856 --> 00:07:13.796 align:middle
So, in the translations/ directory,

00:07:14.046 --> 00:07:20.176 align:middle
create a file called nglayouts.en.yaml,
or use whatever format you want.

00:07:20.776 --> 00:07:23.226 align:middle
Paste the label and set it to "Search term".

00:07:24.536 --> 00:07:26.626 align:middle
Try the admin section now.

00:07:29.806 --> 00:07:30.986 align:middle
When we click...

00:07:31.546 --> 00:07:32.306 align:middle
much better!

00:07:32.886 --> 00:07:38.666 align:middle
If you still see the old label, try clearing
your cache: sometimes Symfony doesn't notice

00:07:38.666 --> 00:07:40.936 align:middle
when you add a new translation file.

00:07:41.986 --> 00:07:46.696 align:middle
Ok, to use the search term, head
over to our query type handler.

00:07:47.306 --> 00:07:52.646 align:middle
The Query object passed to getValues()
contains any parameters we added.

00:07:53.186 --> 00:07:58.926 align:middle
And, I already prepared the
createQueryBuilderOrderedByNewest() method

00:07:59.016 --> 00:08:01.316 align:middle
to accept an optional search term!

00:08:01.936 --> 00:08:08.896 align:middle
Pass this $query-&gt;getParameter(),
its name - term - then -&gt;getValue().

00:08:10.116 --> 00:08:14.436 align:middle
Copy that and repeat it down
here for the getCount() method.

00:08:16.276 --> 00:08:19.066 align:middle
Alrighty, let's take this
thing for a test drive!

00:08:19.816 --> 00:08:27.346 align:middle
Refresh the layouts area, go down
here and I think that worked!

00:08:28.006 --> 00:08:29.576 align:middle
It shows no items...

00:08:29.686 --> 00:08:32.276 align:middle
because I used a pretty silly search term.

00:08:32.706 --> 00:08:33.276 align:middle
Clear it out.

00:08:33.906 --> 00:08:35.006 align:middle
We get everything.

00:08:35.876 --> 00:08:37.716 align:middle
Now type just a few letters...

00:08:38.216 --> 00:08:40.556 align:middle
and watch as it changes below.

00:08:41.516 --> 00:08:47.036 align:middle
Next, let's teach layouts how to render
recipe items both on the frontend

00:08:47.316 --> 00:08:49.866 align:middle
as well as for the admin-area preview.

