Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Adding More Customized Blocks

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

We're going to work more on this Recipe List Layout later. But, let's do one more things right now. Edit that layout. I want to give our admin users the flexibility to change this title. Cool! Let's add a new title block right above... and enter some text.

Hit "Publish and continue editing"... then go to the frontend. What I'm attempting to do is replicate this title, or "hero" area - so that we can remove it from our Twig template. But when we refresh, that doesn't look right yet.

Go over and look at that template. Ok: to replicate this, we need an h1 tag wrapped in a hero-wrapper div:

... lines 1 - 4
{% block body %}
<div class="hero-wrapper">
<h1>Doggone Good Recipes</h1>
<p>Recipes your pup will love!</p>
... lines 10 - 31
{% endblock %}

Right now, layouts is simply rendering an h1. And, by the way, you can, in the title block options, choose between h1, h2, or h3. h1 is what we need this time.

Adding a Wrapper Div Column

So: how can we wrap this in a div and give it a hero-wrapper class? The answer: add a nifty "column" block... then move the title into that column. Cool right? Finally, when you click on the column, you can add any class you want. Add hero-wrapper.

Let's try it! Hit "Publish and continue editing", refresh the frontend and... much better! What about that text? Copy it, add a new "text" block right below our "title" and... paste. Publish and continue editing again... try the frontend again and... look at that! A perfect replica!

To celebrate, over in the template, we can remove that section entirely:

... lines 1 - 5
<div class="hero-wrapper">
<h1>Doggone Good Recipes</h1>
<p>Recipes your pup will love!</p>
... lines 10 - 33

The end result is the same as before... except admin users now have the ability to change the text.

Custom CSS in Layouts or Pre-Made Custom Block Type?

Though, you probably noticed that this did require me to be a bit technical: I had to know the CSS class that the column needed. If the admin users designing your layouts are a bit technical, then this might be no problem. But if your editors are less technical, you could, instead, create a custom block type - like a hero block - where the user just types in the text and you render this whole thing for them. We're not going to create custom blocks in this tutorial... but that's mostly because, by the end of the tutorial, you'll know everything you need to follow the docs for that.

The Layouts Web Debug Toolbar

All right, back on the front end, layouts comes with its own web debug toolbar icon. And if you click this, it's pretty cool. We're going to use this a bunch of times. It shows you the layout that was resolved and even the reason why it was chosen.

But the really useful thing is the "Rendered blocks" section. This shows us all the layouts blocks that were rendered to build this page. You can see there's one called "Twig block" for the top nav, a "Column", then the "Title", "Text", "Full view" block and finally the last "Twig" block for the footer. This is a great way to see all the different blocks that are being rendered, as well as the template behind each one. Later, we're going to talk about overriding those templates, so we can customize how they look.

Linking to the Layouts Admin

Back in the Layouts admin, publish the layout to get back to the main page. If you go to /admin, you'll find that our app already has EasyAdmin installed. Let's add a link from the menu here to Layouts to make life easier. Open src/Controller/Admin/DashboardController.php... and find configureMenuItems(). Add another with yield MenuItem::linkToUrl(), call it "Layouts" and give it some icons: fas fa-list. For the url, use this->generateUrl() and pass in the route name, which happens to be nglayouts_admin_layouts_index:

... lines 1 - 12
class DashboardController extends AbstractDashboardController
... lines 15 - 34
public function configureMenuItems(): iterable
... lines 37 - 38
yield MenuItem::linkToUrl('Layouts', 'fas fa-list', $this->generateUrl('nglayouts_admin_layouts_index'));

Perfect! That's a small detail, but now when we're on /admin, we can click "Layouts" to jump right there.

Okay, status check! We can render Twig blocks and mix in title, text, HTML, Google Maps and other blocks wherever we want. The more Twig blocks we have in the template, the more flexibility we have here.

But what about being able to render a collection of recipes from our database, like the "Latest Recipes" we have on the homepage? That's a big piece of layouts: so let's start diving into it next.

Leave a comment!

Login or Register to join the conversation

Many thanks for these tutorials! It makes my life easier. Can't wait to watch the next videos :)

Jesse-Rushlow Avatar
Jesse-Rushlow Avatar Jesse-Rushlow | SFCASTS | Lubna | posted 10 months ago

HI! We LOVE reading comments like this and thank you! Most of the folks at SymfonyCasts have been at SymfonyCon in Paris last week, so hang tight, more videos are on there way shortly...


Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.7", // v3.7.0
        "doctrine/doctrine-bundle": "^2.7", // 2.7.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.13", // 2.13.3
        "easycorp/easyadmin-bundle": "^4.4", // v4.4.1
        "netgen/layouts-contentful": "^1.3", // 1.3.2
        "netgen/layouts-standard": "^1.3", // 1.3.1
        "pagerfanta/doctrine-orm-adapter": "^3.6",
        "sensio/framework-extra-bundle": "^6.2", // v6.2.8
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
        "symfony/console": "5.4.*", // v5.4.14
        "symfony/dotenv": "5.4.*", // v5.4.5
        "symfony/flex": "^1.17|^2", // v2.2.3
        "symfony/framework-bundle": "5.4.*", // v5.4.14
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "5.4.*", // v5.4.6
        "symfony/runtime": "5.4.*", // v5.4.11
        "symfony/security-bundle": "5.4.*", // v5.4.11
        "symfony/twig-bundle": "5.4.*", // v5.4.8
        "symfony/ux-live-component": "^2.x-dev", // 2.x-dev
        "symfony/ux-twig-component": "^2.x-dev", // 2.x-dev
        "symfony/validator": "5.4.*", // v5.4.14
        "symfony/webpack-encore-bundle": "^1.15", // v1.16.0
        "symfony/yaml": "5.4.*", // v5.4.14
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.3
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "5.4.*", // v5.4.11
        "symfony/maker-bundle": "^1.47", // v1.47.0
        "symfony/stopwatch": "5.4.*", // v5.4.13
        "symfony/web-profiler-bundle": "5.4.*", // v5.4.14
        "zenstruck/foundry": "^1.22" // v1.22.1