Dynamically Output Projects

Now that we have our categories being output correctly, let’s output the projects that are attached to them. This is a bit more complex than outputting categories, because the HTML for this block type is pretty rigid. It needs 3 projects per category, which we aren’t currently set up to provide. We’ll have to work around this limitation with some creative problem solving.

First, before we get to worrying about this, let’s go in and remove our placeholder HTML that was setting the height. We won’t need it for long:

Delete this.

Next, let’s look at our projects HTML. The way this block is built it basically loops through all available Project Categories and builds a hidden tab for ech one of them. This tab gets a specific ID, and then three slots within each tab lay out each one of the three specific projects that might show up there.

See our portfolio_box class? That’s where each one of our projects gets displayed. So it seems clear that we’re going to need to loop through all the available options and then, for each one of those select categories, get a list of project pages that are attached to that category. We’ll cap that list at 3 projects per area, since that’s all this pretty fixed layout can support. So let’s add that here:

What’s happening here? We loop through the exact same list of options, and we turn these options into a label and an ID. We use the same camelcase() method to create the ID, so that we can be sure it conforms to the way we built the category links earlier. (Note: this isn’t the most efficient code; you could move some of these methods into the block controller for reuse, but in general it’s not the biggest deal if a block works in a slightly inefficient way, especially if it lends itself to great understandability or readability.)

Next, we instantiate a new Concrete\Core\Page\PageList class. This class is the class to use any time you need to get a list of pages in a Concrete CMS site that conform to a set of criteria. We filter that page by the selected option that we have, next we set it to sort by public date descending. This ensures that the most recent three projects of the specific category are displayed. Next, we filter by page type, ensuring that only project pages are returned. Finally, we instruct Concrete to only return three items, and we get the results.

That’s it -- the PageList class takes care of the rest, including checking to see whether the current user has the proper permissions to view the project pages. It also ensures that pages that are pending publish do not get returned in the results.

For each one of those options that we loop through, we build an outer tab container. Next, let’s look at how the inner three projects are displayed. For each one of those portfolio_box classes, this is how the inner Concrete code works.

We get the particular result that we want (whether it’s $results[0], $results[1], or $results[2]). Next, we retrieve the Project Thumbnail and, if it is a valid object (which will be of the type Concrete\Core\Entity\File\File, we use the getURL() method to get the URL of the selected approved file version object. We output that in the HTML. Next, we get the value of the Project Type attribute within the <p> tag, just like we did in our custom template earlier. Finally, we output the page name and the link to the page.

And what if the project doesn’t exist? Let’s add a new div with the class empty-project. Why? Because our HTML and CSS from this theme are pretty rigid. If a project doesn’t exist in this loop it totally breaks the layout. So let’s create a new CSS class in our site’s _project.scss file, named .empty-project, that sets a minimum height. This will keep our layout from breaking if we have fewer than 3 projects in any given tab.

This is what our SCSS file looks like with our new code. Then we run npm run prod within our root directory to rebuild our derived CSS files:

That’s it! We have a fully working view template for our block!