ryan Posted August 30 Share Posted August 30 This week we introduce a new module named Custom Fields. This module provides a way to rapidly build out ProcessWire fields that contain any number of subfields/properties within them. No matter how simple or complex your needs are, Custom Fields makes your job faster and easier. Not only does this post introduce Custom Fields, but also documents how to use them and includes numerous examples— https://processwire.com/blog/posts/custom-fields-module/ 11 5 Link to comment Share on other sites More sharing options...
FireWire Posted August 30 Share Posted August 30 This looks fantastic. I'm at my computer right now working on a ProcessWire site, hop over to the forums, see this, immediately useful. Awesome work, thanks! 2 Link to comment Share on other sites More sharing options...
Jim Bailie Posted August 30 Share Posted August 30 Wow. This is really something. Thanks @ryan! I didn't read the the entire post, but are these or will these Custom Field "maps" be exportable from an existing install? 1 Link to comment Share on other sites More sharing options...
ryan Posted August 30 Author Share Posted August 30 @Jim Bailie They are defined just in a PHP (or JSON) file, so exporting (or importing) the definitions would be just a matter of copying the file from one system to another. 1 Link to comment Share on other sites More sharing options...
Jim Bailie Posted August 30 Share Posted August 30 @ryan Understood. I suppose I would rephrase the question with a scenario. Let's say I install the module, will it or could it auto-generate definitions of existing fields? I hope I'm not missing something obvious. And I will read the post in detail, but we have to head out for the evening 🙂 Link to comment Share on other sites More sharing options...
ryan Posted August 30 Author Share Posted August 30 @Jim Bailie If I understand the question correctly, you want to convert several regular ProcessWire fields into a single Custom Field? There isn't an automated way to do that. I suppose there could be though, as they are using all the same configuration properties. 1 Link to comment Share on other sites More sharing options...
Robin S Posted August 30 Share Posted August 30 @ryan, thanks for creating this cool module! A couple of questions: 1. Do the subfields have an ID of some sort separate to the name, such that a subfield can be renamed without losing the existing data stored for the subfield? 2. What happens to stored data if a subfield is removed from the defining file? Does the existing data for the subfield persist in the database, or does the fieldtype detect the removal and do some cleanup to remove the stored data? Also, I think there's a typo in the blog post. // multi-selection foreach($page->custom_field->countries as $value) { $label = $page->field->label('countries', $value); echo "<li>$value: $label</li>"; // i.e. "USA: United States" } ...should be... // multi-selection foreach($page->custom_field->countries as $value) { $label = $page->custom_field->label('countries', $value); echo "<li>$value: $label</li>"; // i.e. "USA: United States" } 6 Link to comment Share on other sites More sharing options...
ryan Posted September 1 Author Share Posted September 1 @Robin S These property/subfield definitions are in files rather than the database, so there are no database-style IDs. Or, you can think of the property names as the IDs. There is already is a to-do note in the module to add support for property aliases, so that you can rename properties without having to convert data. That's not in this v1 beta version, but likely will be in the next one. That will enable you to rename properties when/if the need arises. But you'll still have to update your own code that refers to any of those names, as would be the case with any other field. When it comes to deleting properties, the no-longer-needed data would be cleaned up whenever it is saved. This is like any other Fieldtype that encodes multiple properties/subfields together (Textareas is one example, Combo is another, depending on the chosen storage method). If you regularly need to rename and delete these kinds of things after development of a site, regular old ProcessWire fields (without subfields) are hard to beat. But either way, you still have to consider your own code that's referring to those fields. Thanks, I will correct the typo! 5 Link to comment Share on other sites More sharing options...
MrSnoozles Posted September 3 Share Posted September 3 (edited) @ryan The module sounds very cool. Does it support MariaDB too or only MySQL? Quote 1. Do the subfields have an ID of some sort separate to the name, such that a subfield can be renamed without losing the existing data stored for the subfield? A storage_name option could be useful to be able to rename the field in the api, but keep the values in the database. Edited September 3 by MrSnoozles Link to comment Share on other sites More sharing options...
diogo Posted September 3 Share Posted September 3 12 minutes ago, MrSnoozles said: @ryan The module sounds very cool. Does it support MariaDB too or only MySQL? MariaDB and MySQL are interchangeable 🙂 Link to comment Share on other sites More sharing options...
MrSnoozles Posted September 3 Share Posted September 3 3 minutes ago, diogo said: MariaDB and MySQL are interchangeable 🙂 Okay cool. I thought with JSON functionality they were starting to diverge. Link to comment Share on other sites More sharing options...
diogo Posted September 3 Share Posted September 3 @MrSnoozles sorry, I have to correct myself here. I should have written that MariaDB and MySQL are generally interchangeable. There are different features since the very beginning, but they are very specific. It seems like, though, MySQL 8.0 has some bigger differences. Concerning JSON functionality. For what I understand, the difference is how JSON is stored on each of them. MySQL stores JSON as binary objects, and MariaDB as strings, but the functionality is (again) generally similar. 1 Link to comment Share on other sites More sharing options...
Pete Posted September 5 Share Posted September 5 Sounds and looks fantastic. 1 Link to comment Share on other sites More sharing options...
ryan Posted September 6 Author Share Posted September 6 As far as JSON column types, the module lets you choose between JSON, TEXT, MEDIUMTEXT and LONGTEXT (or is it BIGTEXT, I can't remember). The main difference between them is how many kilobytes/megabytes/gigabytes you can hold. Functionally I can't tell any difference between them, and you can easily switch between them in the module settings. But as I understand it, JSON columns have the benefit of being more optimized for MySQL JSON-based queries, even if those queries still work on the text column types. I expect there may be a measurable difference at larger scale that isn't yet apparent at the scale I'm currently working at. The downside with the JSON column type is that you can't have a FULLTEXT index. So you can query individual subfields/properties, but can't perform a text search on all of them at once. As I understand it, JSON column types also have the benefit of being able to support MySQL 8 multivalue indexes. These enable you to pick and choose which individual fields within the JSON you want to index separately, or combine several of them in one index. I plan to support these with CustomFields in a future version. For now, I find MEDIUMTEXT to be a good fit for my project, as I do like to be able to perform text searches on the entire field at once, while also being able to query individual fields within it. Link to comment Share on other sites More sharing options...
FireWire Posted September 6 Share Posted September 6 @ryan Is there a way to make the custom field children iterable? I have a script that loops through a couple of fieldtypes on a page where I don't know the names of the custom field children at runtime. My solution requires a little digging to access the '_custom_property_name' by drilling down into $customFields->defs()->getInputfields()->children() then looping through the subfield defs and accessing the value using $page->{$customField->name}->{$subfieldDef->_custom_property_name} I might be missing a better way to do that. I'm coding so much that my brain is going to melt and might have missed the right way to do it. 1 Link to comment Share on other sites More sharing options...
ryan Posted September 6 Author Share Posted September 6 @FireWire You can just iterate $page->your_custom_field as if it were an array. There are a couple of foreach() examples in the blog post, in the section headlined "Outputting custom fields". Though let me know if I've misunderstood what you are looking for. 1 Link to comment Share on other sites More sharing options...
FireWire Posted September 6 Share Posted September 6 I had to review my code a bit to see what I meant. Bad job formulating that question... can confirm that my brain is soup. I needed to get the value of addClass. When I create the custom fields I'm setting a value for 'addClass' and while I can loop through the custom field to get names/values I couldn't access an underlying field object to get to the class string. It ended up being complex but I think that's because of how the data is stored for the child fields. Here's the loop I put together where the subfield had to be accessed separately to get the value set by 'addClass', using that chain I noted above was just because it was in this context. <?php foreach ($customField->defs()->getInputfields()->children() as $subField) { $subField->class; // Provides the class that I added when configuring the child field // Skip this field if it has X class assigned $subField->value; // Doesn't provide the value $page->{$customField->name}->{$subfieldDef->_custom_property_name}; // Used this to get the value because it was accessible here } // Looping this way looped over the 'data' property of the CustomField object foreach ($customField as $subfield) { $subfield; // Not a field object so no value or class property } So it was a workaround because I couldn't iterate over the child fields as if they were normal fields. 1 Link to comment Share on other sites More sharing options...
maetmar Posted September 11 Share Posted September 11 Just checked out the module and I really like it. Still need to wait for the next version as I need support for images. Link to comment Share on other sites More sharing options...
ryan Posted September 14 Author Share Posted September 14 @FireWire Apologies, I still don't completely follow what you are trying to do in the code above, but wanted to comment about a couple of things. Quote $subField->value; // Doesn't provide the value This is because at this point in your code, you've only dealt with the Field object (or in this case a CustomField object), and no $page has been involved. Since values are stored with pages, all you've got here is a set of blank Inputfields, which probably isn't useful for anything. Quote // Looping this way looped over the 'data' property of the CustomField object foreach ($customField as $subfield) { $subfield; // Not a field object so no value or class property } In this case you are iterating that Field object, which I don't think has any value. What you want to iterate is the value from the page. So if your CustomField is named "custom_field": foreach($page->custom_field as $property => $value) { echo "<li>$property: $value</li>"; } Quote When I create the custom fields I'm setting a value for 'addClass' and while I can loop through the custom field to get names/values I couldn't access an underlying field object to get to the class string. It ended up being complex but I think that's because of how the data is stored for the child fields. Are you setting an 'addClass' property to your Inputfield definitions in your /site/templates/custom-fields/field_name.php file? And you want to use the value of that property somehow on the front-end of your site? That property is for adding a class to the Inputfield in the admin, but if you want to have access to it on the front-end of your site, I suppose you could do this: $defs = $fields->get('custom_field')->defs(); /** @var CustomFieldDefs $defs */ foreach($page->custom_field as $property => $value) { $f = $defs->getPropertyInputfield($property); echo "<li>addClass for $property is: $f->addClass</li>"; } But you might also just consider going straight to the source, by including your field definitions php file directly: $defs = include('./custom-fields/field_name.php'); /** @var array $defs */ foreach($page->custom_field as $property => $value) { $def = $defs[$property]; if(isset($def['addClass'])) { echo "<li>addClass for $property is: $def[addClass]</li>"; } } Note this will only work if you don't have your properties nested within fieldsets. If they are nested in fieldsets, you can still do it, but you'd just need to account for that in the code. You wouldn't need to account for it in the example above this one. 1 Link to comment Share on other sites More sharing options...
FireWire Posted September 14 Share Posted September 14 6 minutes ago, ryan said: Are you setting an 'addClass' property to your Inputfield definitions in your /site/templates/custom-fields/field_name.php file? And you want to use the value of that property somehow on the front-end of your site? That property is for adding a class to the Inputfield in the admin, but if you want to have access to it on the front-end of your site, I suppose you could do this: So this is actually something I cooked up to "mark" certain subfields within the custom field and pretty much the entire cause of this approach I took. Your include tip is pretty ingenious, I never would have thought of that. These are good tips. As for the use case of addClass, it's definitely a hacky method of attaching some additional data to each subfield. I have a Pages::saveReady hook that loops through groups of fields on the page and creates a supplemental index field for the SearchEngine module. It's only necessary for fields that aren't compatible with the module out of the box. I needed a way to indicate which subfields of custom fields shouldn't be added to the search index so in the custom field config I added 'input:nosearchindex'. It's just a workaround to indicate which fields should be excluded at configuration but which wouldn't be known at runtime. Really appreciate the response, very helpful! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now