craftcms / cms

Build bespoke content experiences with Craft.
https://craftcms.com
Other
3.22k stars 627 forks source link

Allow asset queries to parse subdirectory contents as children #7085

Closed furioursus closed 3 years ago

furioursus commented 3 years ago

Description

Is there a decent method for outputting a volume or directory's assets in a tree-like structure?

Use case: one of our departments is using box.com but is no longer allowed to use an outside service to display their reports. Now we have to do it within Craft. We could easily add a directory for them to upload to, but providing a method on the frontend for visitors to view that content seems to not be something that's been discussed much from what I can tell.

Basically I'd like to just create a simple tree structure that a user can twirl open like a regular os directory. But it doesn't seem I can get children of an asset volume.

Directories don't show up as assets in an asset query so there is no kind to query either.

Take for instance this query:


{% set assets = myAssetQuery %}

{% for asset in assets %}
  {{ asset.folderPath }} | {{ asset.url }}<br>
{% endfor %}

Here's a quick sample result:

| https://s3.us-west-2.amazonaws.com/cao-94612/galleries/oak029249.jpg
| https://s3.us-west-2.amazonaws.com/cao-94612/galleries/oak029248.jpg
macarthur-boulevard-gateway-arches-dedication/ | https://s3.us-west-2.amazonaws.com/cao-94612/galleries/macarthur-boulevard-gateway-arches-dedication/Vice-Mayor-Larry-Reid-delivers-remarks-at-the-MacArhtur-Blvd-Gateway-Arches-Dedication-Event-on-November-8-2019-with-the-Oakland-arch-in-the-background.jpg
macarthur-boulevard-gateway-arches-dedication/ | https://s3.us-west-2.amazonaws.com/cao-94612/galleries/macarthur-boulevard-gateway-arches-dedication/East-Bay-artist-Eric-Powell-creator-of-the-MacArthur-Blvd-Gateway-Arches-speaks-during-the-dedication-event-on-November-8-2019.jpg
macarthur-boulevard-gateway-arches-dedication/ | https://s3.us-west-2.amazonaws.com/cao-94612/galleries/macarthur-boulevard-gateway-arches-dedication/City-Councilmember-Taylor-delivers-remarks-at-the-MacArhtur-Blvd-Gateway-Arches-Dedication-Event-November-8-2019-with-the-Oakland-arch-in-the-background.jpg
| https://s3.us-west-2.amazonaws.com/cao-94612/galleries/HT-Ballpark-Aerial-Rendering.jpg
| https://s3.us-west-2.amazonaws.com/cao-94612/galleries/Ballpark-District-Site-Plan-MTR-scenario.jpg

Now I can see that there is a folderPath on each item, but I can’t actually query the folder itself and do anything on its subsequent children. I can only evaluate if the current folderPath is set. It would seem to be poorly performant to try to query each asset and see whether its current folderPath is set, plus it gets extremely unwieldy to evaluate where I am versus several directories deep.

TL;DR: I would like to easily iterate over the contents AND subdirectories of an asset volume so that I can build out my own frontend directory structure that mimics something like in an OS Finder: image

andris-sevcenko commented 3 years ago

Off the top of my head you could do something like this:

{% set assets = yourAssetQuery
    .andWhere(['like', 'folderPath', subPath ~ '%', false])
    .orderBy('folderPath')
    .all() %}

This would return all Assets that are in the subPath/* folder. You could infer the folder structure from the folderPaths after that.

brandonkelly commented 3 years ago

You could do something like this:

{# fetch all the folder paths in the volume #}
{% set volume = craft.app.volumes.getVolumeByHandle('myVolumeHandle') %}
{% set paths = craft.app.assets.findFolders({
  volumeId: volume.id,
})|map(f => f.path) %}

{# fetch all the assets in the volume, grouped by folder path #}
{% set assets = craft.assets()
  .volumeId(volume.id)
  .orderBy('filename ASC')
  .all()|group(a => a.folderPath) %}

{# output all folders and the assets inside them #}
{% for path in paths %}
  <h3>/{{ path }}</h3>
  <ul>
    {% for asset in assets[path] %}
      <li>{{ asset.filename }}</li>
    {% endfor %}
  </ul>
{% endfor %}

Adding hierarchy to it would be quite a bit more involved though, and more of a job for PHP than Twig.