├── resources
└── img
│ └── blocky.png
├── phpstan.neon
├── renovate.json
├── .github
├── linters
│ └── .markdown-lint.yml
├── workflows
│ ├── linter.yml
│ ├── create-release.yml
│ └── php-review.yml
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── config
└── blocks.php
├── phpcs.xml.dist
├── .gitignore
├── src
├── exceptions
│ ├── InvalidBlockException.php
│ └── BlockTransformerNotFoundException.php
├── BlockInterface.php
├── variables
│ └── BlockParserVariable.php
├── twig
│ ├── BlocksTwigExtension.php
│ ├── nodes
│ │ ├── BlocksLoopNode.php
│ │ └── BlocksNode.php
│ └── tokenparsers
│ │ └── BlocksTokenParser.php
├── Block.php
├── Blocky.php
├── BlockParser.php
└── icon.svg
├── LICENSE
├── composer.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
└── README.md
/resources/img/blocky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wrux/blocky/HEAD/resources/img/blocky.png
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | paths:
3 | - src
4 | bootstrapFiles:
5 | - vendor/craftcms/cms/bootstrap/console.php
6 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.github/linters/.markdown-lint.yml:
--------------------------------------------------------------------------------
1 | # Linter rules doc:
2 | # - https://github.com/DavidAnson/markdownlint
3 | #
4 |
5 | MD024: false # Changelog contains multiple Added and Changed headings.
6 |
--------------------------------------------------------------------------------
/config/blocks.php:
--------------------------------------------------------------------------------
1 | 'app\blocks\HeadingBlock',
13 | * 'wysiwyg' => 'app\blocks\TextBlock',
14 | * ];
15 | * ```
16 | *
17 | */
18 |
19 | return [];
20 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 | PSR2 with 2 spaces indentation.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # CRAFT ENVIRONMENT
2 | .env.php
3 | .env.sh
4 | .env
5 |
6 | # COMPOSER
7 | /vendor
8 |
9 | # BUILD FILES
10 | /bower_components/*
11 | /node_modules/*
12 | /build/*
13 | /yarn-error.log
14 |
15 | # MISC FILES
16 | .cache
17 | .DS_Store
18 | .idea
19 | .project
20 | .settings
21 | *.esproj
22 | *.sublime-workspace
23 | *.sublime-project
24 | *.tmproj
25 | *.tmproject
26 | .vscode/*
27 | !.vscode/settings.json
28 | !.vscode/tasks.json
29 | !.vscode/launch.json
30 | !.vscode/extensions.json
31 | config.codekit3
32 | prepros-6.config
33 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: Lint Code Base
2 |
3 | on:
4 | push:
5 | branches-ignore:
6 | - 'master'
7 | - 'main'
8 | jobs:
9 | build:
10 | name: Lint Code Base
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout Code
14 | uses: actions/checkout@v3
15 | - name: Lint Code Base
16 | uses: docker://github/super-linter:v4.10.1
17 | env:
18 | DEFAULT_BRANCH: 'main'
19 | VALIDATE_ALL_CODEBASE: false
20 | VALIDATE_ANSIBLE: false
21 | VALIDATE_MD: true
22 |
--------------------------------------------------------------------------------
/src/exceptions/InvalidBlockException.php:
--------------------------------------------------------------------------------
1 | parseBlocks($blocks);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/twig/BlocksTwigExtension.php:
--------------------------------------------------------------------------------
1 | true], $lineno, $tag);
31 | }
32 |
33 | /**
34 | * Compile the loop node context.
35 | *
36 | * @param Compiler $compiler
37 | */
38 | public function compile(Compiler $compiler): void
39 | {
40 | $compiler
41 | ->write("++\$context['loop']['index0'];\n")
42 | ->write("++\$context['loop']['index'];\n")
43 | ->write("\$context['loop']['first'] = false;\n")
44 | ->write("if (isset(\$context['loop']['length'])) {\n")
45 | ->indent()
46 | ->write("--\$context['loop']['revindex0'];\n")
47 | ->write("--\$context['loop']['revindex'];\n")
48 | ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n")
49 | ->outdent()
50 | ->write("}\n");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wrux/blocky",
3 | "description": "Map a matrix field into an array of blocks to render in twig.",
4 | "type": "craft-plugin",
5 | "version": "1.1.2",
6 | "keywords": ["craft", "cms", "craftcms", "craft-plugin", "block parser"],
7 | "support": {
8 | "docs": "https://github.com/wrux/blocky/blob/master/README.md",
9 | "issues": "https://github.com/wrux/blocky/issues"
10 | },
11 | "license": "MIT",
12 | "authors": [
13 | {
14 | "name": "Callum Bonnyman",
15 | "homepage": "https://bloke.blog"
16 | }
17 | ],
18 | "scripts": {
19 | "phpstan": "vendor/bin/phpstan analyse ./src --level=5"
20 | },
21 | "require": {
22 | "craftcms/cms": "^4.0.0"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "wrux\\blocky\\": "src/"
27 | }
28 | },
29 | "extra": {
30 | "name": "Blocky",
31 | "handle": "blocky",
32 | "developer": "Callum Bonnyman",
33 | "developerUrl": "https://bloke.blog",
34 | "documentationUrl": "https://github.com/wrux/blocky/blob/master/README.md",
35 | "changelogUrl": "https://raw.githubusercontent.com/wrux/blocky/master/CHANGELOG.md",
36 | "class": "wrux\\blocky\\Blocky"
37 | },
38 | "require-dev": {
39 | "phpstan/phpstan": "^1.0.0"
40 | },
41 | "config": {
42 | "platform": {
43 | "php": "8.0.2"
44 | },
45 | "allow-plugins": {
46 | "yiisoft/yii2-composer": true,
47 | "craftcms/plugin-installer": true
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Block.php:
--------------------------------------------------------------------------------
1 | block = $block;
44 | }
45 |
46 | // Public Methods
47 | // ===========================================================================
48 |
49 | /**
50 | * Returns the Block type handle.
51 | *
52 | * @return string
53 | * Name of the block as defined in Craft.
54 | */
55 | public function getType(): string
56 | {
57 | return $this->block->type->handle;
58 | }
59 |
60 | /**
61 | * Returns the Block template.
62 | *
63 | * @return string
64 | * Name of the block as defined in Craft.
65 | */
66 | public function getTemplate(): string
67 | {
68 | return $this->blockTemplate;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Blocky Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this
6 | project adheres to [Semantic Versioning](http://semver.org/).
7 |
8 | ## 1.0.1 - 2020-06-22
9 |
10 | ## Added
11 |
12 | - Superlinter with Markdown support
13 |
14 | ## Updated
15 |
16 | - Set minimum PHP version to 7.4.0.
17 |
18 | ## 1.0.0 - 2020-06-16
19 |
20 | ### Added
21 |
22 | - Skip empty blocks in the blocks tag using the `skip empty` keyword
23 | - Variables `context`, `template`, `type` are accessible inside the blocks tag
24 | - PHPCS PSR2 rules
25 | - PHPStan rules
26 | - Github actions to test PHPCS and PHPStan
27 |
28 | ## 0.1.2 - 2020-07-07
29 |
30 | ### Changed
31 |
32 | - Fixed `block.template` not accessible in the template because Twig was trying
33 | to access the protected `block.template` property instead of
34 | `block.getTemplate()`
35 | - Fixed wrong order in CHANGELOG.md
36 |
37 | ## 0.1.1 - 2020-07-07
38 |
39 | ### Changed
40 |
41 | - Typo in class name `InvalicBlockException`
42 | - The class Blocky is not correctly importing the exception classes
43 |
44 | ### Removed
45 |
46 | - Translation files, as these only contained exception translations
47 | - All instances of `Craft::t()` in exceptions
48 |
49 | ## 0.1.0 - 2020-06-05
50 |
51 | ### Added
52 |
53 | - Added `{% blocks %}` Twig tag to simplify templating
54 |
55 | ### Changed
56 |
57 | - Moved the main block parser functionality so that it is now available at
58 | `Blocky::$plugin->parseBlocks()`
59 | - This is consumed by the new `{% blocks %}` Twig tag and
60 | `{% craft.blocky.blockparser() %}`
61 |
62 | ## 0.0.2 - 2020-06-03
63 |
64 | ### Added
65 |
66 | - Cleaned up plugin code and fixed some code style issues
67 |
68 | ## 0.0.1 - 2020-06-03
69 |
70 | ### Added
71 |
72 | - Initial release
73 |
--------------------------------------------------------------------------------
/src/twig/tokenparsers/BlocksTokenParser.php:
--------------------------------------------------------------------------------
1 | getLine();
34 | $stream = $this->parser->getStream();
35 | $stream->expect(Token::OPERATOR_TYPE, 'in');
36 | $blocks = $this->parser->getExpressionParser()->parseExpression();
37 |
38 | $skip_empty = false;
39 | if ($stream->nextIf(Token::NAME_TYPE, 'skip')) {
40 | $stream->expect(Token::NAME_TYPE, 'empty');
41 | $skip_empty = true;
42 | }
43 |
44 | if ($stream->nextIf(Token::BLOCK_END_TYPE)) {
45 | $body = $this->parser->subparse([$this, 'decideBlocksEnd'], true);
46 | if ($token = $stream->nextIf(Token::NAME_TYPE)) {
47 | $value = $token->getValue();
48 | }
49 | } else {
50 | $body = new Node(
51 | [
52 | new PrintNode(
53 | $this->parser->getExpressionParser()->parseExpression(),
54 | $lineno
55 | ),
56 | ]
57 | );
58 | }
59 | $stream->expect(Token::BLOCK_END_TYPE);
60 |
61 | return new BlocksNode($blocks, $body, $skip_empty, $lineno, $this->getTag());
62 | }
63 |
64 | /**
65 | * @inheritDoc
66 | */
67 | public function getTag(): string
68 | {
69 | return 'blocks';
70 | }
71 |
72 | /**
73 | * Checks for the blocks closing tag.
74 | *
75 | * @param \Twig\Token $token
76 | * Twig token.
77 | *
78 | * @return bool
79 | * True if `{% endblocks %}` is found.
80 | */
81 | public function decideBlocksEnd(Token $token): bool
82 | {
83 | return $token->test('endblocks');
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Blocky.php:
--------------------------------------------------------------------------------
1 | request->getIsSiteRequest()) {
47 | return;
48 | }
49 |
50 | // Register the blocky variable.
51 | Event::on(
52 | CraftVariable::class,
53 | CraftVariable::EVENT_INIT,
54 | function (Event $event) {
55 | /** @var CraftVariable $variable */
56 | $variable = $event->sender;
57 | $variable->set('blocky', BlockParserVariable::class);
58 | }
59 | );
60 |
61 | // Add the `blocks` Twig tag.
62 | Craft::$app->view->registerTwigExtension(new BlocksTwigExtension());
63 | }
64 |
65 | /**
66 | * Parse the Matrix blocks array.
67 | *
68 | * @param array|MatrixBlockQuery $blocks
69 | *
70 | * @return mixed
71 | * Iterable block object.
72 | */
73 | public function parseBlocks($blocks)
74 | {
75 | // If the matrix block was not eagar loaded then execute the query.
76 | if ($blocks instanceof MatrixBlockQuery) {
77 | $blocks = $blocks->all();
78 | }
79 | if (!$blocks || count($blocks) === 0) {
80 | return [];
81 | }
82 | $block_parser = new BlockParser;
83 | foreach ($blocks as $block) {
84 | if (!$block instanceof Element) {
85 | continue;
86 | }
87 | try {
88 | $block_parser->addBlock($block);
89 | } catch (BlockTransformerNotFoundException $e) {
90 | // Block found, but with no corresponding parser class.
91 | Craft::warning(
92 | sprintf('Block not found: %s', $e->getMessage()),
93 | __METHOD__
94 | );
95 | continue;
96 | } catch (\Exception $e) {
97 | Craft::error(
98 | sprintf('Block parser error: %s', $e->getMessage()),
99 | __METHOD__
100 | );
101 | return [];
102 | }
103 | }
104 | return $block_parser;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at callum@banbury.town. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/src/twig/nodes/BlocksNode.php:
--------------------------------------------------------------------------------
1 | loop = new BlocksLoopNode($lineno, $tag);
44 | $body = new Node([$body, $this->loop]);
45 | $nodes = [
46 | 'body' => $body,
47 | 'blocks' => $blocks,
48 | ];
49 | parent::__construct($nodes, ['skip_empty' => $skip_empty], $lineno, $tag);
50 | }
51 |
52 | /**
53 | * Compile the node.
54 | *
55 | * @param \Twig\Compiler $compiler
56 | * Twig compiler object.
57 | */
58 | public function compile(Compiler $compiler): void
59 | {
60 | $compiler
61 | ->addDebugInfo($this)
62 | ->write("\$context['_parent'] = \$context;\n")
63 | ->write("\$context['_blocks'] = twig_ensure_traversable(")
64 | ->subcompile($this->getNode('blocks'))
65 | ->raw(");\n");
66 |
67 | $compiler
68 | ->write("\$context['parsed_blocks'] = \wrux\blocky\Blocky::\$plugin->parseBlocks(\$context['_blocks']); ");
69 |
70 | $compiler
71 | ->write("\$context['loop'] = [\n")
72 | ->write(" 'parent' => \$context['_parent'],\n")
73 | ->write(" 'index0' => 0,\n")
74 | ->write(" 'index' => 1,\n")
75 | ->write(" 'first' => true,\n")
76 | ->write("];\n");
77 |
78 | $compiler
79 | ->write("if (is_array(\$context['_blocks']) || ")
80 | ->write("(is_object(\$context['_blocks']) && \$context['_blocks'] ")
81 | ->write("instanceof \Countable)) {\n")
82 | ->indent()
83 | ->write("\$length = count(\$context['_blocks']);\n")
84 | ->write("\$context['loop']['revindex0'] = \$length - 1;\n")
85 | ->write("\$context['loop']['revindex'] = \$length;\n")
86 | ->write("\$context['loop']['length'] = \$length;\n")
87 | ->write("\$context['loop']['last'] = 1 === \$length;\n")
88 | ->outdent()
89 | ->write("}\n");
90 |
91 | $compiler
92 | ->write("foreach (\$context['parsed_blocks'] as \$block) {\n")
93 | ->write("\$block_context = \$block->getContext();\n")
94 | ->indent();
95 |
96 | // Skip blocks that return an empty context.
97 | if ($this->getAttribute('skip_empty')) {
98 | $compiler
99 | ->write("if (empty(\$block_context)) {\n")
100 | ->indent()
101 | ->write("continue;\n")
102 | ->outdent()
103 | ->write("}\n");
104 | }
105 |
106 | $compiler
107 | ->write("\$context['block'] = \$block;\n")
108 | ->write("\$context['type'] = \$block->getType();\n")
109 | ->write("\$context['template'] = \$block->getTemplate();\n")
110 | ->write("\$context['context'] = \$block_context;\n")
111 | ->subcompile($this->getNode('body'), false)
112 | ->outdent()
113 | ->write("}\n\n");
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/BlockParser.php:
--------------------------------------------------------------------------------
1 | config = Craft::$app->config->getConfigFromFile('blocks');
49 | }
50 |
51 | /**
52 | * Returns the instansiated blocks.
53 | *
54 | * When looping over the result of `craft.blocks.parseBlocks` this method will
55 | * get called and return the blocks.
56 | *
57 | * In the twig template you can use the following example:
58 | * ```
59 | * {% for block in blocks %}
60 | *
61 | * {% include template ignore missing with context only %}
62 | *
63 | * {% endfor %}
64 | * ```
65 | *
66 | * @return ArrayIterator
67 | */
68 | public function getIterator(): ArrayIterator
69 | {
70 | return new ArrayIterator($this->blocks);
71 | }
72 |
73 | /**
74 | * Adds a block to the parser.
75 | *
76 | * @param \craft\base\Element $block Matrix block.
77 | *
78 | * @throws \wrux\blocky\exceptions\BlockTransformerNotFoundException
79 | * If no corresponding block parser class is found.
80 | */
81 | public function addBlock(Element $block): void
82 | {
83 | $block_class = $this->getBlockClass($block->type->handle);
84 | if (!$block_class || !class_exists($block_class)) {
85 | throw new BlockTransformerNotFoundException(
86 | sprintf('The block %s could not be found', $block_class)
87 | );
88 | }
89 | $reflect = new \ReflectionClass($block_class);
90 | if (!$reflect->implementsInterface(BlockInterface::class)) {
91 | throw new InvalidBlockException(
92 | sprintf(
93 | 'The block class %s does not implement BlockInterface',
94 | $block_class
95 | )
96 | );
97 | }
98 | $this->blocks[] = new $block_class($block);
99 | }
100 |
101 | /**
102 | * Returns weather the block parser contains any blocks.
103 | *
104 | * @return bool
105 | */
106 | public function hasBlocks(): bool
107 | {
108 | return count($this->blocks) > 0;
109 | }
110 |
111 | // Private Methods
112 | // =========================================================================
113 |
114 | /**
115 | * Returns the block class reference from the configuration.
116 | *
117 | * @param string $handle The Matrix block handle.
118 | *
119 | * @throws \wrux\blocky\exceptions\BlockTransformerNotFoundException
120 | * If no corresponding block parser is found in the config
121 | * @return string
122 | * Block class namespace.
123 | */
124 | private function getBlockClass(string $handle): string
125 | {
126 | if (empty($this->config[$handle])) {
127 | throw new BlockTransformerNotFoundException(
128 | sprintf(
129 | 'The block handle %s could not be found in the config',
130 | $handle
131 | )
132 | );
133 | }
134 | return $this->config[$handle];
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Blocky Plugin for Craft CMS 3.x
4 |
5 | Utility plugin for Craft CMS to map Matrix fields.
6 |
7 | Blocky handles the logic of parsing your Matrix blocks so you can create cleaner
8 | Twig templates.
9 |
10 | ## Requirements
11 |
12 | This plugin requires Craft CMS 3.0.0 or later.
13 |
14 | ## Installation
15 |
16 | To install the plugin, follow these instructions.
17 |
18 | 1. Open your terminal and go to your Craft project:
19 |
20 | ```bash
21 | cd /path/to/project
22 | ```
23 |
24 | 2. Then tell Composer to load the plugin:
25 |
26 | ```bash
27 | composer require wrux/blocky
28 | ```
29 |
30 | 3. In the Control Panel, go to Settings → Plugins and click the “Install” button
31 | for Blocky.
32 |
33 | ## Configuring the Block Parser
34 |
35 | 1. Create `config/blocks.php` inside your Craft project:
36 |
37 | ```php
38 | 'app\blocks\TextBlock',
42 | ];
43 | ```
44 |
45 | 2. Somewhere in your project, create block classes for each Matrix block which
46 | extends `wrux\blocky\Block`
47 |
48 | Here's an example block:
49 |
50 | ```php
51 | !empty($this->block->contentHtml)
64 | ? $this->block->contentHtml->getParsedContent()
65 | : NULL,
66 | ];
67 | }
68 | }
69 | ```
70 |
71 | ## Templating
72 |
73 | Blocky is available at `craft.blocky` in the template or you can also use the
74 | `{% blocks ... %}` Twig tag.
75 |
76 | ### Twig Tag
77 |
78 | The `{% blocks %}` tag works similarly to a Twig for loop. It expects a Matrix
79 | field and it will handle the parsing and iteration.
80 |
81 | **Example:**
82 |
83 | ```twig
84 | {% blocks in entry.blockComponents %}
85 |
86 | {% include template with context only %}
87 |
88 | {% endblocks %}
89 | ```
90 |
91 | **Example with skipping empty blocks:**
92 |
93 | You can use `skip empty` in the opening tag. This will skip blocks that return
94 | an empty context.
95 |
96 | ```twig
97 | {% blocks in entry.blockComponents skip empty %}
98 |
99 | {% include template with context only %}
100 |
101 | {% endblocks %}
102 | ```
103 |
104 | ### Variables
105 |
106 | The following variables are available inside the `{% blocks %}` tag.
107 |
108 | | Variable | Value |
109 | | -------------- | ------------------------------------------------------------- |
110 | | block | The block object |
111 | | template | The data returned from the `getTemplate()` method |
112 | | type | The value returned from the `getType()` method |
113 | | context | The context returned from the `getContext()` method |
114 | | loop.index | The current iteration of the loop. (1 indexed) |
115 | | loop.index0 | The current iteration of the loop. (0 indexed) |
116 | | loop.revindex | The number of iterations from the end of the loop (1 indexed) |
117 | | loop.revindex0 | The number of iterations from the end of the loop (0 indexed) |
118 | | loop.first | True if first iteration |
119 | | loop.last | True if last iteration |
120 | | loop.length | The number of items in the sequence |
121 |
122 | ### Manually Parsing Blocks
123 |
124 | If you don't want to use the Twig tag, blocks can be parsed manually using
125 | `craft.blocky` service. Internally this consumes the same
126 | `Blocky::$plugin->parseBlocks()` method. This method allows you to check
127 | `blocks.hasBlocks` before the for loop.
128 |
129 | **Example:**
130 |
131 | ```twig
132 | {% set blocks = craft.blocky.parseBlocks(entry.blockComponents) %}
133 | {% if blocks.hasBlocks %}
134 |
135 | {% for block in blocks %}
136 |
137 | {% include block.template ignore missing with block.context only %}
138 |
139 | {% endfor %}
140 |
141 | {% endif %}
142 | ```
143 |
144 | ## Block Parser Roadmap
145 |
146 | Some things to do, and ideas for potential features:
147 |
148 | - Testing 🔥
149 | - Nested blocks with the release of CraftCMS 4.0.
150 |
151 | Brought to you by [Callum Bonnyman](https://bloke.blog)
152 |
--------------------------------------------------------------------------------
/src/icon.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------