|
| 1 | +--- |
| 2 | +title: Advanced Blueprint Features |
| 3 | +taxonomy: |
| 4 | + category: docs |
| 5 | +--- |
| 6 | + |
| 7 | +!!!! **BETA REQUIRED** - These advanced features are only available in **Grav 1.1.0-beta.1+**. They will not work in Grav 1.0.x |
| 8 | + |
| 9 | +There are some advanced features in the blueprints which allow you to extend them and to have dynamic fields. |
| 10 | + |
| 11 | +## Defining Validation Rules |
| 12 | + |
| 13 | +If you need the same validation rules multiple times, you can create your own custom rule for it. |
| 14 | + |
| 15 | +``` yaml |
| 16 | +rules: |
| 17 | + slug: |
| 18 | + pattern: "[a-z][a-z0-9_\-]+" |
| 19 | + min: 2 |
| 20 | + max: 80 |
| 21 | +form: |
| 22 | + fields: |
| 23 | + folder: |
| 24 | + type: text |
| 25 | + label: Folder Name |
| 26 | + validate: |
| 27 | + type: slug |
| 28 | +``` |
| 29 | +Above example creates rule `slug`, which is then used in the folder field of the form. |
| 30 | + |
| 31 | +## Extending Base Type (extends@) |
| 32 | + |
| 33 | +You can extend existing blueprints. Basically extending allows you to add new fields as well as modify existing ones from the base blueprint. |
| 34 | + |
| 35 | +``` yaml |
| 36 | +extends@: default |
| 37 | +``` |
| 38 | + |
| 39 | +In long format you can specify lookup context for your base file: |
| 40 | + |
| 41 | +``` yaml |
| 42 | +extends@: |
| 43 | + type: default |
| 44 | + context: blueprints://pages |
| 45 | +``` |
| 46 | + |
| 47 | +You can also extend the blueprint itself, if there are multiple versions of the same blueprint. |
| 48 | + |
| 49 | +``` yaml |
| 50 | +extends@: parent@ |
| 51 | +``` |
| 52 | + |
| 53 | +There is no limit on how many blueprints you an extend. Fields defined in the first blueprint will be replaced by any later blueprints in the list. |
| 54 | + |
| 55 | +``` yaml |
| 56 | +extends@: |
| 57 | + - parent@ |
| 58 | + - type: default |
| 59 | + context: blueprints://pages |
| 60 | +``` |
| 61 | + |
| 62 | +## Embedding Form (import@) |
| 63 | + |
| 64 | +Sometimes you may want to share some fields or sub-forms between multiple forms. |
| 65 | + |
| 66 | +Lets create `blueprints://partials/gallery.yaml` which we want to embed to our form: |
| 67 | + |
| 68 | +``` yaml |
| 69 | +form: |
| 70 | + fields: |
| 71 | + gallery.images: |
| 72 | + type: list |
| 73 | + label: Images |
| 74 | + fields: |
| 75 | + .src: |
| 76 | + type: text |
| 77 | + label: Image |
| 78 | +``` |
| 79 | + |
| 80 | +Our form then has a section where we would like to embed the gallery images: |
| 81 | + |
| 82 | +``` yaml |
| 83 | +form: |
| 84 | + fields: |
| 85 | + images: |
| 86 | + type: section |
| 87 | + title: Images |
| 88 | + underline: true |
| 89 | + import@: |
| 90 | + type: partials/gallery |
| 91 | + context: blueprints:// |
| 92 | +``` |
| 93 | + |
| 94 | +## Removing Fields / Properties (unset-*@) |
| 95 | + |
| 96 | +If you want to remove a field, you can add `unset@: true` inside of it. |
| 97 | +If you want to remove a property of field, you just append property name, eg: `unset-options@` removes all options. |
| 98 | + |
| 99 | +## Replacing Fields / Properties (replace-*@) |
| 100 | + |
| 101 | +By default blueprints use deep merging of its properties. Sometimes instead of merging the content of the field, you want to start from a clean table. |
| 102 | +If you want to replace the whole field, your new field needs to start with `replace@`: |
| 103 | + |
| 104 | +``` yaml |
| 105 | +author.name: |
| 106 | + replace@: true |
| 107 | + type: text |
| 108 | + label: Author name |
| 109 | +``` |
| 110 | + |
| 111 | +As the result `author.name` will have only two properties: `type` and `label` regardless of what the form had before. |
| 112 | +You can do the same for individual properties: |
| 113 | + |
| 114 | +``` yaml |
| 115 | +summary.enabled: |
| 116 | + replace-options@: true |
| 117 | + options: |
| 118 | + 0: Yeah |
| 119 | + 1: Nope |
| 120 | + 2: Do not care |
| 121 | +``` |
| 122 | + |
| 123 | +Note: `replace-*@` is alias for `unset-*@`. |
| 124 | + |
| 125 | +## Using Configuration (config-*@) |
| 126 | + |
| 127 | +There are times when you might want to get default value from Grav configuration. For example you may want to have author field to default to author of the site: |
| 128 | + |
| 129 | +``` yaml |
| 130 | +form: |
| 131 | + fields: |
| 132 | + author: |
| 133 | + type: text |
| 134 | + label: Author |
| 135 | + config-default@: site.author.name |
| 136 | +``` |
| 137 | + |
| 138 | +If your site author name is `John Doe`, the form is equilevant to: |
| 139 | + |
| 140 | +``` yaml |
| 141 | +form: |
| 142 | + fields: |
| 143 | + author: |
| 144 | + type: text |
| 145 | + label: Author |
| 146 | + default: "John Doe" |
| 147 | +``` |
| 148 | + |
| 149 | +You can use `config-*@` for any field; for example if you want to change the field `type`, you can just have `config-type@: site.forms.author.type` to allow you to change the input field type from your configuration. |
| 150 | + |
| 151 | +## Using Function Calls (data-*@) |
| 152 | + |
| 153 | +You can make function calls with parameters from your blueprints to dynamically fetch a value for any property in your field. You can do this by using `data-*@:` notation as the key, where `*` is the field name you want to fill with the result of the function call. |
| 154 | + |
| 155 | +As an example we are editing a page and we want to have a field that allows us to change its parent or in another words move page into another location. For that we need default value that points to the current location as well as a list of options which consists of all possible locations. For that we need a way to ask Grav |
| 156 | + |
| 157 | +``` yaml |
| 158 | +form: |
| 159 | + fields: |
| 160 | + route: |
| 161 | + type: select |
| 162 | + label: Parent |
| 163 | + classes: fancy |
| 164 | + data-default@: '\Grav\Plugin\Admin::route' |
| 165 | + data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes' |
| 166 | + options: |
| 167 | + '/': '- Root -' |
| 168 | +``` |
| 169 | + |
| 170 | +If you were editing team member page, resulting form would look something like this: |
| 171 | + |
| 172 | +``` yaml |
| 173 | +form: |
| 174 | + fields: |
| 175 | + route: |
| 176 | + type: select |
| 177 | + label: Parent |
| 178 | + classes: fancy |
| 179 | + default: /team |
| 180 | + options: |
| 181 | + '/': '- Root -' |
| 182 | + '/home': 'Home' |
| 183 | + '/team': 'Team' |
| 184 | + '/team/ceo': ' Meet Our CEO' |
| 185 | + ... |
| 186 | +``` |
| 187 | + |
| 188 | +While `data-default@:` and `data-options@:` are likely the most used dynamic field properties, you are not limited to those. There are no limits on which properties you can fetch, including `type`, `label`, `validation` and even `fields` under the current field. |
| 189 | + |
| 190 | +Additionally you can pass parameters to the function call just by using array where the first value is the function name and parameters follow: |
| 191 | + |
| 192 | +``` yaml |
| 193 | + data-default@: ['\Grav\Theme\ImaginaryClass::getMyDefault', 'default', false] |
| 194 | +``` |
| 195 | + |
| 196 | +## Changing field ordering |
| 197 | + |
| 198 | +When you extend a blueprint or import a file, by default the new fields are added to the end of the list. Sometimes this is not what you want to do, you may want to add item as the first or after some existing field. |
| 199 | + |
| 200 | +For this every field can set `ordering@` property, which is either field name or integer (0 = first item). |
| 201 | + |
| 202 | +# Creating new form field type |
| 203 | + |
| 204 | +If you create a special form field type, which needs a special handling in blueprints, there is a plugin function that you can use. |
| 205 | + |
| 206 | +``` php |
| 207 | + /** |
| 208 | + * Get list of form field types specified in this plugin. Only special types needs to be listed. |
| 209 | + * |
| 210 | + * @return array |
| 211 | + */ |
| 212 | + public function getFormFieldTypes() |
| 213 | + { |
| 214 | + return [ |
| 215 | + 'display' => [ |
| 216 | + 'input@' => false |
| 217 | + ], |
| 218 | + 'spacer' => [ |
| 219 | + 'input@' => false |
| 220 | + ] |
| 221 | + ]; |
| 222 | + } |
| 223 | +``` |
| 224 | + |
| 225 | +You do not need to register this function as its not really an event, but gets fired when plugin object gets constructed. |
| 226 | +The purpose of this function is to give extra instructions how to handle the field, for example above code makes display and spacer types to be virtual, meaning that they won't exist in real data. |
| 227 | + |
| 228 | +You can add any `key: value` pairs including dynamic properties like `data-options@` which will automatically get appended to the fields. |
| 229 | + |
| 230 | +## onBlueprintCreated or accessing blueprint data |
| 231 | + |
| 232 | +Because of blueprints consist of fields with dots, getting nested field from blueprint uses `/` notation instead of `.` notation. |
| 233 | + |
| 234 | +``` php |
| 235 | +$tabs = $blueprint->get('form/fields/tabs'); |
| 236 | +``` |
| 237 | + |
| 238 | +This makes it possible to access special data fields, like: |
| 239 | + |
| 240 | +``` php |
| 241 | +$name = $blueprint->get('form/fields/content.name'); |
| 242 | +$name = $blueprint->get('form/fields/content/fields/.name'); |
| 243 | +``` |
| 244 | + |
| 245 | +For backwards compatibility, you can specify divider in the last (3rd) parameter of `set()` and `get()` |
| 246 | + |
| 247 | +``` php |
| 248 | +$tabs = $blueprint->get('form/fields/tabs', null, '/'); |
| 249 | +``` |
0 commit comments