getgrav / grav

Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS powered by PHP, Markdown, Twig, and Symfony
https://getgrav.org
MIT License
14.59k stars 1.41k forks source link

1:M relationships and searching collections by custom header field #2052

Closed pierluigi closed 3 years ago

pierluigi commented 6 years ago

I am trying to create a 1:M relationship ("An release belongs to 1 artist, and an artist can have M releases") in Grav by using a custom field (uuid) defined via a blueprint, like so:

Release:

# blueprints/release.yaml
title: Release Entry
extends@: default

form:
  fields:
    tabs:
      type: tabs
      active: 1

      fields:
        advanced:
          fields:
            overrides:
              fields:
                header.uuid:
                  type: text
                  label: 'Unique ID - do not modify'
                  data-default@: '\Grav\Theme\Upitup::uuid'
                  readonly: true
        content:
          fields:
            header.artist:
              type: select
              label: Select artist
              data-options@: '\Grav\Theme\Upitup::getArtists'

Artist:

#blueprints/artist.yaml
title: Artist Entry
extends@: default

form:
  fields:
    tabs:
      type: tabs
      active: 1

      fields:
        advanced:
          fields:
            overrides:
              fields:
                header.uuid:
                  type: text
                  label: 'Unique ID - do not modify'
                  data-default@: '\Grav\Theme\Upitup::uuid'
                  readonly: true

My theme functions are defined as follows:

class Upitup extends Theme
{

    public static function uuid()
    {
        return uniqid(null, true);
    }

    public static function getArtists() 
    {
        $page = Grav::instance()['page'];
        $collection = $page->evaluate(['@page.children' => '/artists']);

        $options = [];
        foreach ($collection as $artist) {
            $options[$artist->header()->uuid] = $artist->title();
        };

        return $options;
    }

My goal is to show the Artist of a a specific Release. All I could figure out so far is to get all Pages of type "Artist" and checking one by one for the specific uuid I"m looking for, which is obviously suboptimal. Like so:


{% set artists = page.evaluate([{'@page':'/artists'}]) %}
{% for artist in artists %}
    {% if artist.header.uuid == page.header.artist %}
       Release by {{ artist.title }}
    {% endif %}
{% endfor %}

I read the manual twice but I couldn't figure out what's the best way to achieve this simple scenario. Is there a recommended way in Grav to filter collections by a custom value?

Many thanks!

mahagr commented 6 years ago

Right now you need to do something like this -- create a collection of pages and find the one(s) you want to display. You can probably use some caching, but this isn't something that's supported out of the box (look into how taxonomies work for example).

What you described is something I'm working on when I have some spare time and some of the base classes are already in Grav 1.4 and there are multiple improvements in Grav 1.5 to start supporting customizable pages/objects with relations between them. My goal is to have all of this fully integrated with Grav 2.0.

pierluigi commented 6 years ago

Thanks @mahagr and looking forward to seeing relationship support in Grav 2.

One last question: would it be a terrible idea to use creatively taxonomy tags to automatically store primary IDs (e.g. in my example above a creation-time uuid) upon creation of pages (Artists and Releases in my case) to be used later on for taxonomy filtering?