diff --git a/docs/.gitbook/assets/image.png b/docs/.gitbook/assets/image.png new file mode 100644 index 00000000..9442ac55 Binary files /dev/null and b/docs/.gitbook/assets/image.png differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..5e84691b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,2 @@ +# About Sylius Stack + diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 00000000..c62ca519 --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,16 @@ +# Table of contents + +* [About Sylius Stack](README.md) + +## 🌱 Twig Hooks + +* [Getting started](twig-hooks/getting-started.md) +* [Passing data to your hookables](twig-hooks/passing-data-to-your-hookables.md) +* [Making your hookables configurable](twig-hooks/making-your-hookables-configurable.md) +* [Composable Layouts with a predictable structure](twig-hooks/composable-layouts-with-a-predictable-structure.md) +* [Advanced](twig-hooks/advanced/README.md) + * [Autoprefixing feature](twig-hooks/advanced/autoprefixing-feature.md) + * [Ergonomic work with hooks](twig-hooks/advanced/ergonomic-work-with-hooks.md) + * [Metadata objects](twig-hooks/advanced/metadata-objects.md) + * [Multiple hooks inside a single template](twig-hooks/advanced/multiple-hooks-inside-a-single-template.md) + * [Overriding hookables](twig-hooks/advanced/overriding-hookables.md) diff --git a/docs/twig-hooks/advanced/README.md b/docs/twig-hooks/advanced/README.md new file mode 100644 index 00000000..0fb91367 --- /dev/null +++ b/docs/twig-hooks/advanced/README.md @@ -0,0 +1,2 @@ +# Advanced + diff --git a/docs/twig-hooks/advanced/autoprefixing-feature.md b/docs/twig-hooks/advanced/autoprefixing-feature.md new file mode 100644 index 00000000..d713fdb4 --- /dev/null +++ b/docs/twig-hooks/advanced/autoprefixing-feature.md @@ -0,0 +1,109 @@ +# Autoprefixing feature + +{% hint style="warning" %} +`Autoprefixing` is turned off by default. If you want to use this feature you need to set the `enable_autoprefixing` setting to `true` in your `config/packages/twig_hooks.yaml` file: + +``` +twig_hooks: + # ... + enable_autoprefixing: true + # ... +``` +{% endhint %} + +When you are creating a bundle, or a bigger project like [Sylius](https://sylius.com), you might want to rely fully on Twig Hooks to provide easy and flexible way of modifying and extending your views. + +Enabling the autoprefixing feature might improve your developer experience. This feature is crucial for creating [composable-layouts-with-a-predictable-structure.md](../composable-layouts-with-a-predictable-structure.md "mention"). + +{% hint style="info" %} +If you did not read the [composable-layouts-with-a-predictable-structure.md](../composable-layouts-with-a-predictable-structure.md "mention")section we encourage you to do it before you read more about the autoprefixing feature. +{% endhint %} + +The mechanism of autoprefixing is pretty simple. We check if there are any prefixes, then we iterate over them and prepend the hook name with a given prefix. + +### Defining prefixes + +Prefixes by default are injected automatically, and they are the name of the hook where the hookable is rendered. + +> As a developer I define the **index.form** hook in my template +> +> And I define the **some\_field** hookable in it +> +> So when I check prefixes **inside** the **some\_field** hookable I should get `index.form` + +In case we deal with a complex hook: + +> As a developer I define the **index.form, common.form** hook in my template +> +> And I define the **some\_field** hookable in **index.form** +> +> So when I check prefixes **inside** the **some\_field** hookable I should get `index.form` and `common.form` + +If for some reason you want to take the control over the passed prefixes, you can override existing prefixes using the `_prefixes` magic variable when you are creating a hook inside a Twig template: + +{% code title="index.html.twig" lineNumbers="true" %} +```twig +{% hook 'index.form with { + _prefixes: ['my_custom_prefix'] +} %} +``` +{% endcode %} + +From now, only the value of `_prefixes` will be taken into account. + +### Example + +{% code title="index.html.twig" lineNumbers="true" %} +```twig +{% raw %} +{% hook 'app.index' %} +{% endraw %} + +{# + # index.html.twig is an entry template, so it is not an hookable + #} +``` +{% endcode %} + +{% code title="index/content.html.twig" lineNumbers="true" %} +```twig +{% raw %} +{% hook 'content' %} + +{# + # this template is an hookable, and is hooked into app.index + #} + +{# + # so {% hook 'content' %} this is a shorter form of {% hook 'app.index.content' %} +{% endraw %} + # when autoprefixing is turned on + #} +``` +{% endcode %} + +{% code title="index/content/button.html.twig" lineNumbers="true" %} +```twig + +``` +{% endcode %} + +The configuration for the hooks and hookables above is: + +{% code title="config/packages/twig_hooks.yaml" lineNumbers="true" %} +```yaml +twig_hooks: + hooks: + 'app.index': + content: + template: 'index/content.html.twig' + + 'app.index.content': + button: + template: 'index/content/button.html.twig +``` +{% endcode %} + +{% hint style="info" %} +The structure of directories above does not matter, all templates can be on the same level of nesting. However, in this example we are following creating [composable-layouts-with-a-predictable-structure.md](../composable-layouts-with-a-predictable-structure.md "mention") guide. +{% endhint %} diff --git a/docs/twig-hooks/advanced/ergonomic-work-with-hooks.md b/docs/twig-hooks/advanced/ergonomic-work-with-hooks.md new file mode 100644 index 00000000..5baf3d2e --- /dev/null +++ b/docs/twig-hooks/advanced/ergonomic-work-with-hooks.md @@ -0,0 +1,2 @@ +# Ergonomic work with hooks + diff --git a/docs/twig-hooks/advanced/metadata-objects.md b/docs/twig-hooks/advanced/metadata-objects.md new file mode 100644 index 00000000..e25eaa80 --- /dev/null +++ b/docs/twig-hooks/advanced/metadata-objects.md @@ -0,0 +1,2 @@ +# Metadata objects + diff --git a/docs/twig-hooks/advanced/multiple-hooks-inside-a-single-template.md b/docs/twig-hooks/advanced/multiple-hooks-inside-a-single-template.md new file mode 100644 index 00000000..1314d4fa --- /dev/null +++ b/docs/twig-hooks/advanced/multiple-hooks-inside-a-single-template.md @@ -0,0 +1,2 @@ +# Multiple hooks inside a single template + diff --git a/docs/twig-hooks/advanced/overriding-hookables.md b/docs/twig-hooks/advanced/overriding-hookables.md new file mode 100644 index 00000000..156921df --- /dev/null +++ b/docs/twig-hooks/advanced/overriding-hookables.md @@ -0,0 +1,2 @@ +# Overriding hookables + diff --git a/docs/twig-hooks/composable-layouts-with-a-predictable-structure.md b/docs/twig-hooks/composable-layouts-with-a-predictable-structure.md new file mode 100644 index 00000000..2362cf50 --- /dev/null +++ b/docs/twig-hooks/composable-layouts-with-a-predictable-structure.md @@ -0,0 +1,2 @@ +# Composable Layouts with a predictable structure + diff --git a/docs/twig-hooks/getting-started.md b/docs/twig-hooks/getting-started.md new file mode 100644 index 00000000..789dea6e --- /dev/null +++ b/docs/twig-hooks/getting-started.md @@ -0,0 +1,128 @@ +--- +description: >- + Twig Hooks are a robust and powerful alternative to the Sonata Block Events + and the old Sylius Template Events systems. +--- + +# Getting started + +### Main features + +* built-in support for _Twig templates_, _Twig Components_ and _Symfony Live Components_ +* adjustability +* autoprefixing hooks +* configurable hookables +* priority mechanism +* easy enable/disable mechanism per each hook + +### Installation + +Install the package using Composer and Symfony Flex: + +```bash +composer require sylius/twig-hooks +``` + +### Your first hook & hookable + +Once Twig Hooks installed, you can open **any** twig file and define your first hook. + +{% code title="some.html.twig" %} +```twig +{# ... #} + +{% raw %} +{% hook 'my_first_hook' %} +{% endraw %} + +{# ... #} +``` +{% endcode %} + +From now the `my_first_hook` is a unique name we will use to hook into that place. + +{% hint style="success" %} +The ideal hook name: + +* is consisted of small case letters +* has logical parts separated with using dots (`.`) +* has each part separated with underscore (`_`) if it is consisted of multiple words + + + +Recommended: + +* `index` +* `index.sidebar` +* `index.top_menu` + + + +Not recommended: + +* index +* indextopmenu +{% endhint %} + +#### Hooking into a hook + +For the purpose of this example, let's consider we want to render the `some_block.html.twig` template inside the `my_first_hook` hook. First step is to create a `twig_hooks.yaml` file (or any other format you use) under the `config/packages/` directory (of course, if you do not have already one). + +Now, we can define our first hookable with the following configuration: + +{% code title="config/packages/twig_hooks.yaml" lineNumbers="true" %} +```yaml +twig_hooks: + hooks: + 'my_first_hook': + some_block: + template: 'some_block.html.twig' +``` +{% endcode %} + +Decomposing the example above we can notice that: + +1. `twig_hooks` is just a main key for the Twig Hooks configuration +2. `hooks` is a configuration key for defining hookables per hook +3. `my_first_hook` is our hook name, defined on the Twig file level +4. `some_block` is a name of our hookable, it can be any string, but it should be unique for a given hook unless you want to override the existing hookable (if you want to read more about overriding hookables check the [overriding-hookables.md](advanced/overriding-hookables.md "mention")section) +5. finally we have a `template` key that defines what template should be rendered inside the `my_first_hook` hook + +#### Possible hookable configuration options + +Depending on the hookable template, we can pass different configuration options while defining hookables: + +{% tabs %} +{% tab title="Hookable Template" %} +{% code lineNumbers="true" %} +```yaml +twig_hooks: + hooks: + 'my_first_hook': + some_block: + template: 'some_block.html.twig' + enabled: true # whether the hookable is enabled + context: [] # key-value pair that will be passed to the context bag + configuration: [] # key-value pair that will be passed to the configuration bag + priority: 0 # priority, the higher number, the earlier hookable will be hooked +``` +{% endcode %} +{% endtab %} + +{% tab title="Hookable Component" %} +{% code title="" %} +```yaml +twig_hooks: + hooks: + 'my_first_hook': + some_block: + component: 'app:block' # component key + enabled: true # whether the hookable is enabled + context: [] # key-value pair that will be passed to the context bag + props: [] # key-value pair that will be passed to our component as props + configuration: [] # key-value pair that will be passed to the configuration bag + priority: 0 # priority, the higher number, the earlier hookable will be hooked +``` +{% endcode %} +{% endtab %} +{% endtabs %} diff --git a/docs/twig-hooks/making-your-hookables-configurable.md b/docs/twig-hooks/making-your-hookables-configurable.md new file mode 100644 index 00000000..8de2c387 --- /dev/null +++ b/docs/twig-hooks/making-your-hookables-configurable.md @@ -0,0 +1,51 @@ +# Making your hookables configurable + +Sometimes when you are creating a bundle or a reusable template for different hookables, you might want to provide a way to adjust it to a given context. Thanks to the configuration data bag, you are able to achieve it easily. + +Configuration can be defined only while defining a hookable, and is accessibly with using `hookable_metadata.configuration.` or `get_hookable_configuration().`. + +#### Example + +{% code title="index.html.twig" lineNumbers="true" %} +``` +{# + # we assume there is a `form` variable holding a `FormView` instance passed + # from the controller + #} + +
+ {{ form_start(form) }} + {{ form_errors(form) }} + {{ form_widget(form._token) }} + + {% raw %} +{% hook 'index.form' with { form } %} +{% endraw %} + {{ form_end(form, {render_rest: false}) }} +
+``` +{% endcode %} + +{% code title="generic_field.html.twig" lineNumbers="true" %} +``` +
+ {{ form_row( + hookable_metadata.context.form[hookable_metadata.configuration.field_name] + ) }} +
+``` +{% endcode %} + +{% code title="twig_hooks.yaml" lineNumbers="true" %} +``` +twig_hooks: + hooks: + 'index.form': + name: + template: 'generic_field.html.twig' + configuration: + field_name: 'name' + attr: + class: 'field special-field' +``` +{% endcode %} diff --git a/docs/twig-hooks/passing-data-to-your-hookables.md b/docs/twig-hooks/passing-data-to-your-hookables.md new file mode 100644 index 00000000..7d9730d5 --- /dev/null +++ b/docs/twig-hooks/passing-data-to-your-hookables.md @@ -0,0 +1,77 @@ +# Passing data to your hookables + +One of the most powerful aspects of hooks & hookables is an ability to pass their data down to the children. We can have two sources of the context data: + +* Hook-level defined data +* Hookable-level defined data + +The context data from these two sources are merged and passed with the metadata to the hookable template or component, so we can use them. + +
+ +
+ +
+ +### Example + +{% code title="index.html.twig" lineNumbers="true" fullWidth="false" %} +```twig +{# + # we assume there is a `form` variable holding a `FormView` instance passed + # from the controller + #} + +
+ {{ form_start(form) }} + {{ form_errors(form) }} + {{ form_widget(form._token) }} + + {% raw %} +{% hook 'index.form' with { form } %} +{% endraw %} + {{ form_end(form, {render_rest: false}) }} +
+``` +{% endcode %} + +So, as we see at `line 8` we define the `index.form` hook. But also, we pass the `form` with using the `with` keyword. Thanks to it, we are able to pass multiple data to hookables that will hook into the `index.form` hook. + +{% hint style="info" %} +`with { form }` is a short-hand for `with { form: form }`, so the key for our `FormView` in the context data bag will be `form.` +{% endhint %} + +Now let's create a Twig template rendering some field from our form, and let's make it a hookable. + +{% code title="index/some_field.html.twig" lineNumbers="true" %} +```twig +
+ {{ form_row(hookable_metadata.context.form.some_field) }} + + {# we can also write it like #} + + {% raw %} +{% set context = hookable_metadata.context %} + + {{ form_row(context.form.some_field) }} + + {# or #} + + {% set context = get_hookable_context() %} +{% endraw %} + + {{ form_row(context.form.some_field) }} +
+``` +{% endcode %} + +{% hint style="info" %} +You can access the context data in multiple ways, so you can pick the one you like the most. Available options are: + +* getting it directly from the `hookable_metadata` object like `hookable_metadata.context.` +* getting the context data bag via the Twig function like `get_hookable_context().` +{% endhint %} + +{% hint style="info" %} +Sometimes you might want to override some data that are defined at the hook-level. It is possible by defining the same context data key on the hookable level. If the same context data key is defined at both hook-level and hookable-level the hookable-level one is used. +{% endhint %}