# JBC Block Theme
Based on [_s theme](https://github.com/Automattic/_s) with Custom ACF Block MVC
## Theme Requirements
* PHP 8.2+
* [ACF Pro](https://www.advancedcustomfields.com/pro/)
* [ACF Extended](https://www.acf-extended.com/) (optional)
## Development Requirements ##
- [Node.js v12](https://nodejs.org/)
- [Composer](https://getcomposer.org/)
### Quick Start
Clone or download this repository, remname, and then you'll need to do a six-step find and replace on the name in all the templates.
1. Search for `'atlas'` (inside single quotations) to capture the text domain and replace with: `'theme-name'`.
2. Search for `atlas_` to capture all the functions names and replace with: `theme_name_`.
3. Search for `Text Domain: atlas-theme` in `style.css` and replace with: `Text Domain: theme-name`.
4. Search for <code> Atlas</code> (with a space before it) to capture DocBlocks and replace with: <code> Theme_Name</code>.
5. Search for `atlas-` to capture prefixed handles and replace with: `theme-name-`.
6. Search for `ATLAS_` (in uppercase) to capture constants and replace with: `THEME_NAME_`.
### Setup
To start using all the tools that come with `atlas` you need to install the necessary Node.js and Composer dependencies :
```sh
$ composer install
$ npm install
```
### Available CLI commands
- `composer lint:wpcs` : checks all PHP files against [PHP Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/).
- `composer lint:php` : checks all PHP files for syntax errors.
- `composer make-pot` : generates a .pot file in the `languages/` directory.
- `npm run compile:css` : compiles SASS files to css.
- `npm run compile:rtl` : generates an RTL stylesheet.
- `npm run watch` : watches all SASS files and recompiles them to css when they change.
- `npm run lint:scss` : checks all SASS files against [CSS Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/css/).
- `npm run lint:js` : checks all JavaScript files against [JavaScript Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/).
- `npm run bundle` : generates a .zip archive for distribution, excluding development and system files.
---
## Field Types
Follow this [LINK](https://hackmd.io/kRXbmm2IQ96Bo-kqCMSnqA#Basic) to find example implanmentations.
You can find a full reference of available settings on the [official ACF documentation](https://www.advancedcustomfields.com/resources/register-fields-via-php/#field-type%20settings).
### Blocks
***
#### File Structure
Create directory in the `blocks` directory found in the root directory and set up the below file Structure
```
WordPress/
├── wp-content/
│ ├── themes/
│ │ ├── theme-name/
│ │ │ └── blocks/
│ │ │ ├── example-block/
│ │ │ │ ├── view/
│ │ │ │ │ └── render.php
│ │ │ │ └── example-block.php
│ │ │ └── register-blocks.php
│ │ └── ...
│ └── ...
└── ...
```
- `render.php`: Contains the skeleton of the block view on the frontend
- `example-block.php`: This is the block class that contains the functionality and ACF Field of the Block
#### Register Block
To register a block add the class to `$classArray` in the `register-blocks.php` found in `theme-name/blocks`.
:::spoiler {state="open"} Code
```php=
<?php
$classArray = [
BlockNameBlock::class
];
```
:::
---
## Register ACF Fields to block
In the Block class with in the 'registerFields' function
### Simple Example
Below is a Simple implemention of use case
___
:::spoiler {state="open"} Code
```php=
<?php
$block = new FieldsBuilder('Block Name');
$block->setLocation('block', '==', 'acf/block-name-block');
$block->addGroup('block_name_block')
->addText('text_field')
->instructions('Instructions')
->addRepeater('repeater')
->addImage('image')
->endRepeater()
->endGroup();
return $block->build();
```
:::
:::spoiler {state="open"} Output
```php=
<?php
return = [
'key' => 'block_name',
'title' => 'Block Name',
'fields' => [
[
'key' => 'block_name_block',
'label' => 'Block Name Block',
'name' => 'block_name_block',
'type' => 'group',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'width' => '',
'class' => '',
'id' => '',
],
'layout' => 'block',
'sub_fields' => [
[
'key' => 'text_field',
'label' => 'Text Field',
'name' => 'text_field',
'type' => 'text',
'instructions' => 'Instructions',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'width' => '',
'class' => '',
'id' => '',
],
'default_value' => '',
'placeholder' => '',
'prepend' => '',
'append' => '',
'maxlength' => '',
],
[
'key' => 'repeater',
'label' => 'Repeater',
'name' => 'repeater',
'type' => 'repeater',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'width' => '',
'class' => '',
'id' => '',
],
'collapsed' => '',
'min' => 0,
'max' => 0,
'layout' => 'table',
'button_label' => '',
'sub_fields' => [
[
'key' => 'image',
'label' => 'Image',
'name' => 'image',
'type' => 'image',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'width' => '',
'class' => '',
'id' => '',
],
'return_format' => 'array',
'preview_size' => 'medium',
'library' => 'all',
'min_width' => '',
'min_height' => '',
'min_size' => '',
'max_width' => '',
'max_height' => '',
'max_size' => '',
'mime_types' => '',
],
],
],
],
],
],
'location' => [
[
[
'param' => 'post_type',
'operator' => '==',
'value' => 'post',
],
],
],
'menu_order' => 0,
'position' => 'normal',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'hide_on_screen' => '',
'active' => TRUE,
'description' => '',
'show_in_rest' => 0,
];
```
:::
---
#### Reusable Examples
Below is an example of the reusable case scenario
---
:::spoiler {state="open"} Code
```php=
<?php
$background = new FieldsBuilder('background');
$background
->addTab('Background')
->addImage('background_image')
->addTrueFalse('fixed')
->instructions("Check to add a parallax effect where the background image doesn't move when scrolling")
->addColorPicker('background_color');
$banner = new FieldsBuilder('banner');
$banner
->addTab('Content')
->addText('title')
->addWysiwyg('content')
->addFields($background)
->setLocation('post_type', '==', 'page');
$section = new FieldsBuilder('section');
$section
->addTab('Content')
->addText('section_title')
->addRepeater('columns', ['min' => 1, 'layout' => 'block'])
->addTab('Content')
->addText('title')
->addWysiwyg('content')
->addFields($background)
->endRepeater()
->addFields($background)
->setLocation('post_type', '==', 'page');
```
:::
---
## CPT (Custom Post Type)
### File Location
Create a class in the `cpt` directory found in the `include` directory.
```
WordPress/
├── wp-content/
│ ├── themes/
│ │ ├── include/
│ │ │ └── cpt/
│ │ │ └── example-cpt.php
│ │ └── ...
│ └── ...
└── ...
```
#### CPT Class Skeleton
In the CPT class file the below snippet is the basic structure
:::spoiler {state="open"} Code
```php=
<?php
namespace JBC\Include\CPT;
/**
* Functions which enhance the theme by adding Custom Post Type (CPT) into WordPress
*
* @package Example_Theme
*/
class ExampleCPT {
public function register() {
$this->example_cpt();
$this->example_cpt_categories();
$this->example_cpt_template();
$this->example_settings_menu();
acf_add_local_field_group( $this->example_settings_acf() );
acf_add_local_field_group( $this->example_categories_acf() );
}
public function example_cpt() {
...
}
private function example_cpt_categories() {
...
}
private function example_cpt_template() {
...
}
private function example_settings_menu() {
...
}
private function example_settings_acf() {
...
}
private function example_categories_acf() {
...
}
}
```
:::
#### example_cpt()
Code example:
:::spoiler {state="open"} Code
```php=
<?php
public function example_cpt() {
$supports = [
'title', // post title
'editor', // post content
'author', // post author
'thumbnail', // featured images
'excerpt', // post excerpt
'custom-fields', // custom fields
'comments', // post comments
'revisions', // post revisions
'post-formats', // post formats
];
$labels = [
'name' => _x( 'Examples', 'plural' ),
'singular_name' => _x( 'Example', 'singular' ),
'menu_name' => _x( 'Example', 'admin menu' ),
'name_admin_bar' => _x( 'Example', 'admin bar' ),
'add_new' => _x( 'Add New', 'add new' ),
'add_new_item' => __( 'Add New Example' ),
'new_item' => __( 'New Example' ),
'edit_item' => __( 'Edit Example' ),
'view_item' => __( 'View Example' ),
'all_items' => __( 'All Examples' ),
'search_items' => __( 'Search Examples' ),
'not_found' => __( 'No Example found.' ),
];
$args = [
'supports' => $supports,
'labels' => $labels,
'public' => TRUE,
'query_var' => TRUE,
'rewrite' => [ 'slug' => 'example' ],
'has_archive' => TRUE,
'hierarchical' => FALSE,
];
register_post_type( 'example', $args );
}
```
:::
#### example_cpt_categories()
Code example:
:::spoiler {state="open"} Code
```php=
<?php
private function example_cpt_categories() {
$labels = [
'name' => _x( 'Example Categories', 'taxonomy general name', 'example-theme' ),
'singular_name' => _x( 'Example Category', 'taxonomy singular name', 'example-theme' ),
'search_items' => __( 'Search Example Categories', 'example-theme' ),
'all_items' => __( 'All Example Categories', 'example-theme' ),
'parent_item' => __( 'Parent Example Category', 'example-theme' ),
'parent_item_colon' => __( 'Parent Example Category:', 'example-theme' ),
'edit_item' => __( 'Edit Example Category', 'example-theme' ),
'update_item' => __( 'Update Example Category', 'example-theme' ),
'add_new_item' => __( 'Add New Example Category', 'example-theme' ),
'new_item_name' => __( 'New Example Category Name', 'example-theme' ),
'menu_name' => __( 'Example Category', 'example-theme' ),
];
$args = [
'hierarchical' => TRUE,
'labels' => $labels,
'show_ui' => TRUE,
'show_admin_column' => TRUE,
'query_var' => TRUE,
'show_in_rest' => TRUE,
];
register_taxonomy( 'examples_categories', [ 'examples' ], $args );
}
```
:::
#### example_cpt_template()
Code example:
:::spoiler {state="open"} Code
```php=12
<?php
private function example_cpt_template() {
$page_type_object = get_post_type_object( 'examples' );
$page_type_object->template = [
[
'acf/examples-block',
[
'data' => [
'examples-block_title' => 'Related examples',
'"_examples-block_title' => 'field_examples_block_examples-block_title',
'examples-block_sub_title' => '',
'_examples-block_sub_title' => 'field_examples_block_examples-block_sub_title',
'examples-block_link' => [
"title" => 'All examples',
"url" => '/examples',
"target" => '',
],
'_examples-block_link' => 'field_examples_block_examples-block_link',
'examples-block' => '',
'_examples-block' => 'field_examples_block_examples-block',
],
],
],
];
}
```
:::
#### example_settings_menu()
Code example:
:::spoiler {state="open"} Code
```php=
<?php
private function example_settings_menu() {
if ( function_exists( 'acf_add_options_page' ) ) {
acf_add_options_page( [
'page_title' => 'Examples Settings',
'menu_slug' => 'examples-settings',
'menu_title' => 'Setting',
'parent_slug' => 'edit.php?post_type=examples',
] );
}
}
```
:::
#### example_settings_acf()
Set up the same as the [Register ACF Fields to block](#Register-ACF-Fields-to-block) changing the location to the setting page.
Code example:
:::spoiler {state="open"} Code
```php=
<?php
private function example_settings_acf() {
...
$block->setLocation( 'options_page', '==', 'examples-settings' );
...
}
```
#### example_categories_acf()
Set up the same as the [Register ACF Fields to block](#Register-ACF-Fields-to-block) changing the location to the categories.
Code example:
:::spoiler {state="open"} Code
```php=
<?php
private function example_categories_acf() {
...
$block ->setLocation( 'taxonomy', '==', 'examples_categories' );
...
}
```
#### Register CPT
To register a CPT add the class to `getCptList()` return array in the `register-cpt.php` found in `theme-name/include`.
:::spoiler {state="open"} Code
```php=
<?php
function getCptList(): array {
return [
ExampleCPT::class
];
}
```
:::
---