Crafty Asset Management

I'm not sure that I've explicitly mentioned this yet, but I have officially migrated theAdhocracy to Craft CMS 💃🎉🕺 (read: paaaarteh!). There's a lot still being sorted out, on both the back-end and the front-end, but today I want to particularly focus on asset management[1].

Craft has some pretty nifty and clever tricks up its sleeve when it comes to image transforms, focus pulling, and even asset scope, but to me the most important element about a CMS is making sure that your files and images are easily navigable on the server. If something goes wrong or you want to migrate in the future, working out which images pair with which articles, pages etc. can be a nightmare. Personally, I prefer to follow a fairly strict and tightly coupled architecture, so that each piece of content has a bespoke folder on the server. Some people will likely think that this is overkill (which is probably fair) and, yes, if you make it too tight then it can force you to duplicate content, which we want to avoid (web storage ain't free, after all), but overall I find the benefits and peace of mind it gives me far outweigh any negatives.

A couple of quick notes: for my purposes I'll be using local storage – so not a CDN or Cloud account – for image and file hosting. If you're doing something different your mileage with this technique may vary, depending on how folder creation works with that third-party provider. I'm also going to refer to "assets" a whole lot; for the most part I assume these will be images, but personally I have use cases for allowing small video files or even PDFs and Word documents to be uploaded, so I prefer a more open-ended terminology. It also helps with clarity, considering that Craft also uses assets as a core term.

Asset Volumes: Broad is Best

In Craft, all assets need to be categorised within a specific volume, which is tethered to a local folder. Because we want each standalone piece of content (i.e. a blog post) to have a unique assets folder, that suits us perfectly. Now, you might be thinking the simplest solution would be to create a unique volume for each type of content that you have. For example, on this site I have Articles (like this one) and Reviews, so I could setup two asset volumes, one for each. We'll return to exactly why this isn't the right idea a little bit later, but suffice to say that if you start down this route it can get a little high maintenance in the future.

Instead, I'd recommend creating a single content volume. For a simple, personal website (like this one) I'd generally recommend having just two asset volumes: one for assets used in page templates, such as header images, icons and logos; and one for all content types. What you call these is up to you, but I tend to settle on "Website" and "Content"; clear, short and simple! With that done, we create a structure like this:

- Website
 ¬
- Content
 ¬

Creating Asset Volumes

I won't go into too much detail, because setting up new asset volumes in Craft is incredibly simple. Just go to Settings -> Assets and hit the +New Volume button, give it a name, set the slider option to "true"[2], and then sit back and contemplate the Base URL and File System Path – these are the tricky bits!

Base URL can be sticky if you expect users to ever browse your images, or even just snoop around in your assets folders. It's something I tend to completely overthink, but luckily theAdhocracy is served in a headless manner (see below) that makes neither reason particularly likely, so for this site the Base URL only has to service one person: me. As a result, I tend to mimic the underlying folder structure to keep parity between workflows, and go for something like:

An example setup:

Name: Content
Handle: content
Assets in this volume 
have public URLs: true
Base URL: https://[your website URL]/assets/content/
Volume Type: Local Folder
File System Path: /[your root server path]/assets/content

Environment Variables & Aliases

Whilst the above setup is perfectly viable, there are a few additional things you can do. The first is to consider using environment variables or aliases within your Base URL and File System Path, which can be useful for server migrations in the future, and for creating development/staging environments. I'll be doing a write-up in the near future on how to create these in Craft, but if you already have some setup then I'd definitely recommend making use of them here. Just be aware that a variable cannot be tweaked or added to (e.g. you can't put a File System Path of $ROOT_PATH/assets/content) but an alias can (e.g. @rootPath/assets/content), so they're probably more versatile for how we're doing things.

Multisite & Headless

Craft has the ability to serve information to multiple sites in two ways: you can configure multisite routing directly within the CMS, or you can use plugins like the Elements API or CraftQL to convert Craft into a headless solution. I personally use the latter, which enables my single Craft instance to push content out to a few separate websites and projects that I manage.

If you're doing either, then you'll probably want to factor that in when setting up asset folders. For example, you might have two kinds of blog posts, one for your personal site and another for a personal project's site; you probably don't want these intermixing within the assets/content folder, even if they have subfolders. If that's the case, I'd recommend adding one more level of hierarchy between assets and content, named after the website those assets are intended for. That's what I'm doing here, so my assets folder structure looks like:

- theAdhocracy
 - Website
 ¬
 - Content
 ¬
- Project_Name
 - Website
 ¬
 - Content
 ¬

Of course, there's no real reason you couldn't do the opposite, and have /theadhocracy/assets/content and /projectname/assets/content instead. No reason at all. You also don't need to separate asset volumes out by website, if most of those assets are just recycled, which would be the case if your multisite setup was enabling different language support. Do whatever makes most sense for your situation.

Although, I'm actually lying a bit right now. You see, I don't use the above structure for my folders, because now that I have content permanently nested within a unique folder itself, I can just abstract it out to be that unique folder. That means I actually end up with a folder structure like: /assets/theadhocracy/ for content and /assets/theadhocracy/website for the rest. You could just as easily do this even on a single site, so your asset volumes and folder structures aren't necessarily completely aligned. Again, whatever works for you.

The Crafty Bit: Field Setup

Right, now we get on to the reason why a single, broad content volume is both sufficient and more maintainable in the long run, but first let's create a new Field. Go to Settings -> Fields and press +New Field. We're going to call this field Assets, but if you are using your CMS for multiple websites it might be worth being a little more specific here (i.e. "Assets - site name").

Next, choose Assets as the Field Type and check "Restrict uploads to a single folder?" (i.e. set to true) – this is that crafty bit I've been referring to. Select your Content assets volume from the dropdown and then put in the following as the subfolder path[3]:

{owner.type.name}/{owner.slug}

Fancy, right! What that line is doing is saying that any asset added to this field will be uploaded to a new subfolder within your Content folder[4], which will be created from the section entry type and the entity slug. That means the first subfolder down will be the entry type (e.g. "Blog Post" or "Article"), the second will be the slug, which is the unique part of the permanent URL for that page on your website. Remember how I originally said you don't need a unique volume for each content type? That's because we can programmatically do that on the fly, giving us the following structure[5] with one volume and one reusable field:

> theAdhocracy
 - Website
 - Content
 ¬ Articles
 - grid-locked
 - finding-time
 - 100-challenge
 ...
 ¬ Reviews
 - jurassic-world-fallen-kingdom
 - detective-pikachu
 - thor-ragnarok
 ...
> Project_Name
 - Website
 - Content
 ¬ Articles
 - test-article
 ...
 ¬ Guides
 - how-to-test
 ...

The main benefit to this folder system is that everything is very easy to follow, mimics the live website structure, and you don't need any pesky date subfolders (because who, really, can recall the correct month or even year of every piece of content you've written in the past?) or unique IDs, instead being fully human readable[6]! That was the initial aim and we can now rest knowing we've achieved it fully.

But it also benefits long-term site maintenance, as I've mentioned before. How? Well, this way you only have one field to worry about in Craft. If I'd setup an asset volume for Articles and another for Reviews at the start, then I'd need two fields now. If I added a Notes content type in the future (which I plan to) and then a Recipes one (which I don't), I'd need to create two more asset volumes and two more fields. Now, let's say I've chosen to set them all up the same way and restrict the file types to only images, but then realise I'll want to allow PDFs further down the line. With this multi-field setup, that's four separate changes; with the setup outlined above, it's just one.

Of course, you might want that level of granular control, in which case this may not be the right architecture for your asset management, but it could still help. Let's say you only want Recipes to have PDF uploads, but definitely not Articles. Well, in that case, you create two fields: Assets+PDF and Assets. Et voila, your needs are fully catered for and you're still only using one asset volume and two fields, compared to four of each. Now imagine you're actually maintaining a site with dozens of content types... can you see how broader asset volumes suddenly become a much neater solution?

Actually, y'know, Managing Assets

Okay, the last thing we need to do is add that Assets field to the relevant content (aka entry (aka section))[7] types, so that you can actually (y'know) manage your assets from within the CMS. As with most things Craft, once you've done the leg work and really considered the setup to begin with, it becomes super easy to implement. Just go to Settings -> Sections and then pick the section you want to be able to add assets to. Finally, drag your Assets field into the field layout wherever you want, save it and fire up a new Entry of the relevant type. You'll now be able to upload whatever assets you need to that entry, and as soon as you hit Save the relevant subfolder structure will be automatically generated.

Warning! Mutating Slugs

Because an entry cannot be saved without a title, and because the slug is autogenerated from the title, there's never any worry about blank folder names or illegal characters. That said, it is worth remembering that slugs are not immutable, so users can cause a little mischief. If someone was to change the slug on a preexisting entry and then upload a new asset, things could go a little weird. In general, Craft is fairly good at managing these kinds of things and I've had more trouble from trying to change folders around on the server then I ever have when editing in Craft itself, but there are a couple of things you can do to mitigate issues.

The first is to simply add some notes to the relevant field to warn users that live slugs should never be edited, and that it could mean images suddenly break or files disappear. That should hopefully dissuade most people, or at least give you a heads up if someone absolutely needs to edit a slug for some reason.

The second is to teach specific workflows, chiefly that new entries are only ever saved as a draft until the slug is finalised, and then it is never amended. In many ways that's an ideal solution, as it prevents SEO and cross-site linking errors; slugs should be regarded as pretty much set in stone anyway. Craft doesn't create the asset folders until an entry is actively published, so you can work away at a draft without worrying; particularly useful if you're using a placeholder title and therefore unwanted slug. Of course, Craft's default behaviour (to auto-enable and auto-publish) isn't ideal, but hopefully a fix will come in the future (and in the meantime you could always create your own plugin to amend that default behaviour wink wink)[8].

Either way, slug mutations should be actively discouraged, but if the worst does happen then you can always give Craft's inbuilt Asset Indexes utility a whirl. I've never tested this step myself, but in theory running that from the Admin panel on the content volume should fix any orphaned folder issues. If it doesn't, then I guess the final step is to hop onto the server and sort it out manually. Far from ideal, but hopefully a rare-to-never scenario in the first place.

---

At any rate, hopefully that leaves you with a good overview of my strategy to managing content assets using Craft. I'm sure this technique can be improved upon, but for now it suits me just fine.

Footnotes