@valantic/vue-pimcore-generator

Generate Pimcore Twig templates based on your Vue components.

Usage no npm install needed!

<script type="module">
  import valanticVuePimcoreGenerator from 'https://cdn.skypack.dev/@valantic/vue-pimcore-generator';
</script>

README

Vue-Pimcore Generator

npm version

node node_modules/.bin/vue-pimcore-generator --help

Elevator Pitch

Pimcore uses a WYSIWYG editor to edit documents which is incompatible with Vue components. To alleviate that, this generator opens a web page in the frontend, parses its contents, and creates Pimcore Areabricks. This is done using data attributes in Vue. Creating these Areabricks allows an editor to drag&drop components on a page and being able to see a preview within Pimcore which closely resembles the Vue frontend.

generator-definitions

This file defines which pages should be parsed by the Pimcore generator. To see the available options and their use, please see the generator-definitions.sample.js file.

Supported data-pimcore-* attributes

  • data-pimcore-areabrick The name that should be used for the area brick in Pimcore. Only [a-z] are allowed.
  • data-pimcore-arg Optional arguments for the Twig tag, e.g. see https://pimcore.com/docs/6.x/Development_Documentation/Documents/Editables/Relation_(Many-To-One).html#page_Using-Restriction
  • data-pimcore-identifier The field name of the Twig tag (e.g. thisisthename -> pimcore_input('thisisthename')) and entry in the data object. Only [a-z_] are allowed
  • data-pimcore-disable-editmode Recursively adds the disabled attribute <a> tags when in Pimcore's edit mode. Was added to prevent navigation usage in Pimcore's edit mode.
  • data-pimcore-type Type of the CMS editor input type and Twig tag (e.g. input -> pimcore_input('name')), see list of editables
  • data-pimcore-document defines the path to the twig file after the build (e.g. snippets/my_snippet) AND source url of the data for GraphQL.
  • data-pimcore-snippet="<name>" Defines the root element of a snippet template.

Examples

Snippet (complete example)

c-header.vue

This is the only file you write by hand.

<!-- A layout -->
<b-jumbotron
    :class="b()"
    :header-level="5"
    lead-tag="div"
    data-pimcore-type="snippet"
    data-pimcore-identifier="header"
    data-pimcore-snippet="header"
    :data-pimcore-document="path">
    <template v-slot:header>
        <div v-if="pimcoreDocumentReady"
             v-html="elements.headline.text"
             :class="b('header')"
             data-pimcore-identifier="headline"
             data-pimcore-type="input"></div>
    </template>
    <template v-slot:lead>
        <div v-if="pimcoreDocumentReady"
             v-html="elements.content.text"
             :class="b('lead')"
             data-pimcore-identifier="content"
             data-pimcore-type="wysiwyg"></div>
    </template>
  </b-jumbotron>

Rendered HTML

Generated by the browser.

<div 
    data-pimcore-type="snippet" 
    data-pimcore-identifier="header" 
    data-pimcore-snippet="header" 
    data-pimcore-document="/snippets/header_shared" 
    class="jumbotron c-header">
    <h1 class="display-5">
        <div
            data-pimcore-identifier="headline"
            data-pimcore-type="input" 
            class="c-header__header">
            Pimcore-CaaS-Vue-SPA-PWA-PoC!
        </div>
    </h1>
    <div class="lead">
        <div 
            data-pimcore-identifier="content" 
            data-pimcore-type="wysiwyg" 
            class="c-header__lead">
            <p>
                Viel Spass!
            </p>
        </div>
    </div>
</div>

Main Twig template

<div
    data-pimcore-document="/snippets/header_shared"
    class="jumbotron c-header">
    {{ pimcore_snippet('header') }}
</div>

Snippet Twig template at snippets/header_shared.html.twig

(Actually, three files are generated to have different view for editmode and viewmode.)

<h1 class="display-5">
    <div class="c-header__header">{{ pimcore_input('headline') }} </div>
</h1>
<div class="lead">
    <div class="c-header__lead">{{ pimcore_wysiwyg('content') }} </div>
</div>

Pimcore Twig Tags

This Vue code:

<div data-pimcore-identifier="headline" data-pimcore-type="input"></div>

becomes this Twig code

<div>{{ pimcore_input('headline') }}</div>

Please see the Pimcore docs for which data-pimcore-types are supported. The identifier can be any string and generally, snake_case or camelCase are used. kebab-case might lead to troubles dealing with GraphQL responses. And spaces are evil, weallknowthat.

Additionally, a (second) argument can be passed to the Twig tag using data-pimcore-arg such as this example where a relation is restricted to Product objects:

<div data-pimcore-type="relations"
     data-pimcore-identifier="products"
     data-pimcore-arg='{
         "types": ["object"],
         "subtypes": {
             "object": ["object"],
         },
         "classes": ["Product"]
     }'
     :class="b('productgrid')">
</div>

Produce this Twig template:

<div class="c-areablock__productgrid">
    {{ pimcore_relations('products', {
            "types": ["object"],
            "subtypes": {
                "object": ["object"],
            },
            "classes": ["Product"]
        })
    }}
</div>

Pimcore Areablocks and Bricks

Render mode

The only difference to normal Twig tags is: data-pimcore-areabrick on the parent.

<div v-for="block in elements"
    :key="block._tagName"
    :data-pimcore-areabrick="block._type">

    <div v-if="block._type === 'headline'">
        <e-heading tag-name="h2"
            :data-pimcore-type="block.content._tagType"
            data-pimcore-identifier="content">
            {{ block.content.text }}
        </e-heading>
    </div>
    
    <!-- Add more bricks -->

</div>

Adding-a-new-brick mode

Simply add the brick unconditionally and execute the generator.

<div v-for="block in elements"
    :key="block._tagName"
    :data-pimcore-areabrick="block._type">

    <c-new-component
        data-pimcore-type="brickname"
        data-pimcore-identifier="content">
        Lorem ipsum
    </c-new-component>
    
    <!-- Rest of the loop -->

</div>

Note: the name of the Vue component is irrelevant. (The parsing happens in a browser at which stage the Vue component names are no longer visible.)