froot Posted August 8, 2022 Share Posted August 8, 2022 I'm working on a module, having troubles getting ___install() and ___uninstall() to work properly. First, how do I check if the fields exist? Because in order to get things right I need to install and uninstall back and forth a couple of times. But any time I install or uninstall it, there's another problem, so it just does half the job which messes up the next attempt, and so on. So from my first uninstall test, some fields weren't uninstalled, leaving a half installed module installed. So instead of uninstalling the fields manually, I want to add a condition that checks if the fields are already installed. Makes sense? Long story short, this doesn't work: $fields = $this->wire('fields'); // neither does $fields = wire('fields'); if(!$fields->get('my_field')) : $my_field = new Field(); // int or text $my_field->type = $this->modules->get("FieldtypeInteger"); $my_field->title = 'My Field'; $my_field->name = wire('sanitizer')->pageName($my_field->title, true); $my_field->label = 'My Field'; $my_field->tags = 'my_module'; $my_field->save(); endif; Why, I know not. Thoughts? EDIT: (SOLVED) OK my bad, this actually works, I'm just tired I guess. Link to comment Share on other sites More sharing options...
froot Posted August 8, 2022 Author Share Posted August 8, 2022 However, second question… I read somewhere in the forum that you do NOT need fieldgroups when installing/constructing a module. However, when I create some fields and templates I get Template 'my_template' cannot be saved because it has no fieldgroup assigned I have not clue what that means. Link to comment Share on other sites More sharing options...
froot Posted August 8, 2022 Author Share Posted August 8, 2022 why do I need fieldgroups?! I checked so many other modules where there's no mention of fieldgroups. But if I uncomment all lines that handle fieldgroups in my code it throws that error that I need a fieldgroup. Why is that? I never needed fieldsgroups, cannot make sense of fieldgroups and it's not well documented. Link to comment Share on other sites More sharing options...
bernhard Posted August 9, 2022 Share Posted August 9, 2022 A template has an assigned fieldgroup and a fieldgroup holds all the fields. In the PW GUI it looks like you are attaching fields to the template, but you are actually attaching them to the fieldgroup. You could simply use RockMigrations for that tasks... The main reason why I made it public is so that others can use it to easily build modules with it using the far easier syntax compared to doing those things with the default API (as you have realised). 1 Link to comment Share on other sites More sharing options...
froot Posted August 9, 2022 Author Share Posted August 9, 2022 No offense but RockMigrations sounds like yet another API to learn to make things "easier". I still wonder why do I even need fieldgroups when almost all other modules don't use them. Anyways, I will look into it, but I still want to understand this fieldgroups stuff. As usual, I can find zero documentation on the subject, I can only reach out to the forum, if I google the error message all I get is the PW github repo where the error message is manifested in the first place. So do I attach the fieldgroup to the template or field? Do I first create the fieldgroup and then attach fields to it and then the template? Or what's the proper order? And what about uninstalling, what do I need to do first? Link to comment Share on other sites More sharing options...
froot Posted August 9, 2022 Author Share Posted August 9, 2022 found in RockMigrations: You can edit your field or template and copy the code from there (I recommend to only copy the settings you need to make your migration files more readable): I don't get this sentence. What code of my fields and templates? copy from where? Are you implying that fields and templates are created with the PW API? So if I copy over the code that creates the fields and templates and put in the site/migrate.php file, my module's relevant code and files would be all over the place wouldn't it? I'm sure that can't be what this sentence means but then again no clue how else to read it. And then, again, what about fieldgroups, does RockMigrations take care of that "in the background"? Thanks for help! Link to comment Share on other sites More sharing options...
bernhard Posted August 9, 2022 Share Posted August 9, 2022 1 hour ago, fruid said: As usual, I can find zero documentation on the subject, I can only reach out to the forum You can also look into the code. Many areas of ProcessWire have very nice helping comments in the top comments of the related file or in the php docblocks above the methods. Of course it would be nice to have resources that explain everything in an easy to understand way - but don't forget that we are talking about an open source and free product... 12 minutes ago, fruid said: I don't get this sentence. What code of my fields and templates? copy from where? Are you implying that fields and templates are created with the PW API? So if I copy over the code that creates the fields and templates and put in the site/migrate.php file, my module's relevant code and files would be all over the place wouldn't it? I'm sure that can't be what this sentence means but then again no clue how else to read it. And then, again, what about fieldgroups, does RockMigrations take care of that "in the background"? The docs of RockMigrations unfortunately are at the moment a bit... weak ? The sentence means that you can lookup all the properties that you need in the "RockMigrations Code" field in your GUI once you have created your field/template. RockMigrations offers an as-easy-as-it-can-get API for all the necessary steps that you need when building reusable modules. That's usually creating templates and fields and maybe also permissions. For all those things you can use simple methods like $rm->addFieldToTemplate("fieldname", "templatename") that behind the scene do all those things that you need some deeper understanding of PW's inner workings (like handling fields in fieldgroups). The RockMigrations API is built in a way and wording that sticks to the way you work in the GUI. createField(), createTemplate(), addFieldToTemplate() etc. The latter one for example does this, which shows how you can use fieldgroups from the PW API: https://github.com/baumrock/RockMigrations/blob/4de5edf7d037935af756243f8b055e91a142563f/RockMigrations.module.php#L395-L424 Link to comment Share on other sites More sharing options...
bernhard Posted August 9, 2022 Share Posted August 9, 2022 1 hour ago, fruid said: As usual, I can find zero documentation on the subject, I can only reach out to the forum, if I google the error message all I get is the PW github repo where the error message is manifested in the first place. I've worked with a commercial product lately and all I could do was writing an e-mail to the support-team and then wait over a week to get a response. The PW-forum is not the worst option if one is needing help imho ? 1 Link to comment Share on other sites More sharing options...
froot Posted August 9, 2022 Author Share Posted August 9, 2022 8 minutes ago, bernhard said: don't forget that we are talking about an open source and free product fair enough, but I'm sure PW has some sort of revenue that would benefit from a clearer documentation. Templates, fields and many more things are explained throroughly but fieldsgroups and many other things completely fall through. I know I'm not in the position to complain but it's frustrating when anything I develop requires me to start countless forum threads. I'm sure most developers here had to go through the same process of countless trial and errors and now they just know it. 14 minutes ago, bernhard said: Many areas of ProcessWire have very nice helping comments in the top comments of the related file I disagree but maybe I'm just not experienced enough. And then, what's the relevant file regarding fieldgroups? I still don't get it. Do I absolutely need a third party module to get my first module to work? Link to comment Share on other sites More sharing options...
bernhard Posted August 9, 2022 Share Posted August 9, 2022 47 minutes ago, fruid said: I still don't get it. Do I absolutely need a third party module to get my first module to work? I don't know. You didn't write what you are trying to do, so how should anybody be able to show how it can be done? RockMigrations was just an offer to make it easier (whatever you are trying to do). You don't have to use it of course and there might be a much simpler solution... Of course you don't need to use fieldgroups at all when creating a module. See the hello world module. 14 hours ago, fruid said: However, second question… I read somewhere in the forum that you do NOT need fieldgroups when installing/constructing a module. However, when I create some fields and templates I get Template 'my_template' cannot be saved because it has no fieldgroup assigned I have not clue what that means. And I guess no one here has a clue what you actually did and where the error occurs ? How did you create your fields? Via GUI? Via code? What code did you use... etc etc... 52 minutes ago, fruid said: fair enough, but I'm sure PW has some sort of revenue that would benefit from a clearer documentation. As far as I know: Not really... That was discussed very often and has pros and cons. Link to comment Share on other sites More sharing options...
froot Posted August 9, 2022 Author Share Posted August 9, 2022 5 minutes ago, bernhard said: And I guess no one here has a clue what you actually did and where the error occurs ? How did you create your fields? Via GUI? Via code? What code did you use... etc etc... 58 minutes ago, fruid said: For my module I need a couple of templates and fields. These templates don't use a corresponding php template file because they don't need to be rendered in the frontend. The pages using these templates and fields are admin area only. I have another module which extends the Process-class that would list the contents of these pages in a table, but that's not relevant here I guess. Here's the full code so far… class Kiosk extends WireData implements Module, ConfigurableModule { public static function getModuleinfo() { return [ 'title' => 'Kiosk', 'summary' => 'Checkout System', 'author' => 'FRUID', 'version' => 1, ]; } public function ___install() { $fields = wire('fields'); $templates = wire('templates'); $fieldgroups = wire('fieldgroups'); // FIELDS if(!$fields->get('custom_order_id')) : $custom_order_id = new Field(); // int or text $custom_order_id->type = $this->modules->get("FieldtypeInteger"); $custom_order_id->title = 'Custom Order ID'; $custom_order_id->name = wire('sanitizer')->pageName($custom_order_id->title, true); $custom_order_id->label = 'Order ID'; $custom_order_id->tags = 'kiosk'; $custom_order_id->save(); endif; if(!$fields->get('custom_order_products')) : $custom_order_products = new Field(); $custom_order_products->type = $this->modules->get("FieldtypeTextarea"); $custom_order_products->title = 'Custom Order Products'; $custom_order_products->name = wire('sanitizer')->pageName($custom_order_products->title, true); $custom_order_products->label = 'Products'; // $custom_order_products->derefAsPage = 1; // $custom_order_products->parent_id = $this->user->parent->id; // $custom_order_products->labelFieldName = 'custom_order_products'; // $custom_order_products->inputfield = 'InputfieldSelect'; // $custom_order_products->required = 1; $custom_order_products->tags = 'kiosk'; $custom_order_products->save(); endif; if(!$fields->get('custom_order_customer_name')) : $custom_order_customer_name = new Field(); // text $custom_order_customer_name->type = $this->modules->get("FieldtypeText"); $custom_order_customer_name->title = 'Custom Order Customer Name'; $custom_order_customer_name->name = wire('sanitizer')->pageName($custom_order_customer_name->title, true); $custom_order_customer_name->label = 'Customer Name'; $custom_order_customer_name->tags = 'kiosk'; $custom_order_customer_name->save(); endif; if(!$fields->get('custom_order_customer_address')) : $custom_order_customer_address = new Field(); // text $custom_order_customer_address->type = $this->modules->get("FieldtypeTextarea"); $custom_order_customer_address->title = 'Custom Order Customer Address'; $custom_order_customer_address->name = wire('sanitizer')->pageName($custom_order_customer_address->title, true); $custom_order_customer_address->label = 'Customer Address'; $custom_order_customer_address->tags = 'kiosk'; $custom_order_customer_address->save(); endif; if(!$fields->get('custom_order_customer_emailaddress')) : $custom_order_customer_emailaddress = new Field(); // email $custom_order_customer_emailaddress->type = $this->modules->get("FieldtypeEmail"); $custom_order_customer_emailaddress->title = 'Custom Order Customer Emailaddress'; $custom_order_customer_emailaddress->name = wire('sanitizer')->pageName($custom_order_customer_emailaddress->title, true); $custom_order_customer_emailaddress->label = 'Customer Email Address'; $custom_order_customer_emailaddress->tags = 'kiosk'; $custom_order_customer_emailaddress->save(); endif; if(!$fields->get('custom_order_shipping')) : $custom_order_shipping = new Field(); // shipping $custom_order_shipping->type = $this->modules->get("FieldtypeText"); $custom_order_shipping->title = 'Custom Order Shipping'; $custom_order_shipping->name = wire('sanitizer')->pageName($custom_order_shipping->title, true); $custom_order_shipping->label = 'Shipping'; $custom_order_shipping->tags = 'kiosk'; $custom_order_shipping->save(); endif; if(!$fields->get('custom_order_total_taxes')) : $custom_order_totalTaxes = new Field(); // totaltaxes $custom_order_totalTaxes->type = $this->modules->get("FieldtypeText"); $custom_order_totalTaxes->title = 'Custom Order Total Taxes'; $custom_order_totalTaxes->name = wire('sanitizer')->pageName($custom_order_totalTaxes->title, true); $custom_order_totalTaxes->label = 'Total Taxes'; $custom_order_totalTaxes->tags = 'kiosk'; $custom_order_totalTaxes->save(); endif; if(!$fields->get('custom_order_total_and_shipping')) : $custom_order_totalAndShipping = new Field(); // Total and Shipping $custom_order_totalAndShipping->type = $this->modules->get("FieldtypeText"); $custom_order_totalAndShipping->title = 'Custom Order Total and Shipping'; $custom_order_totalAndShipping->name = wire('sanitizer')->pageName($custom_order_totalAndShipping->title, true); $custom_order_totalAndShipping->label = 'Total and Shipping'; $custom_order_totalAndShipping->tags = 'kiosk'; $custom_order_totalAndShipping->save(); endif; if(!$fields->get('custom_order_status')) : $custom_order_status = new Field(); // order status $custom_order_status->type = $this->modules->get("FieldtypeText"); $custom_order_status->title = 'Custom Order Status'; $custom_order_status->name = wire('sanitizer')->pageName($custom_order_status->title, true); $custom_order_status->label = 'Order Status'; $custom_order_status->tags = 'kiosk'; $custom_order_status->save(); endif; // FIELDGROUPS if(!$fieldgroups->get('fieldgroup_custom_orders')) : $fieldgroup_custom_orders = new Fieldgroup(); $fieldgroup_custom_orders->name = 'fieldgroup_custom_orders'; $fieldgroup_custom_orders->add($this->fields->get('title')); $fieldgroup_custom_orders->save(); $this->message('creating fieldgroup fieldgroup_custom_orders'); endif; if(!$fieldgroups->get('fieldgroup_custom_order')) : $fieldgroup_custom_order = new Fieldgroup(); $fieldgroup_custom_order->name = 'fieldgroup_custom_order'; $fieldgroup_custom_order->add($this->fields->get('title')); $fieldgroup_custom_order->add($custom_order_id); $fieldgroup_custom_order->add($custom_order_products); $fieldgroup_custom_order->add($custom_order_customer_name); $fieldgroup_custom_order->add($custom_order_customer_address); $fieldgroup_custom_order->add($custom_order_customer_emailaddress); $fieldgroup_custom_order->add($custom_order_shipping); $fieldgroup_custom_order->add($custom_order_totalTaxes); $fieldgroup_custom_order->add($custom_order_totalAndShipping); $fieldgroup_custom_order->add($custom_order_status); $fieldgroup_custom_order->save(); endif; // TEMPLATES if(!$templates->get('template_custom_orders')) : $template_custom_orders = new Template(); $template_custom_orders->name = 'template_custom_orders'; $template_custom_orders->fieldgroup = $fieldgroup_custom_orders; $template_custom_orders->pageLabelField = 'title'; $template_custom_orders->tags = 'kiosk'; $template_custom_orders->save(); endif; if(!$templates->get('template_custom_order')) : $template_custom_order = new Template(); $template_custom_order->name = 'template_custom_order'; $template_custom_order->fieldgroup = $fieldgroup_custom_order; $template_custom_order->pageLabelField = 'title'; $template_custom_order->parentTemplates = array($template_custom_orders->id); $template_custom_order->noChildren = 1; $template_custom_order->tags = 'kiosk'; $template_custom_order->save(); endif; $template_custom_orders->childrenTemplates = array($template_custom_order->id); $template_custom_orders->save(); $parent = wire('pages')->get('/admin/page/'); if ($parent->hasChildren('name=custom_orders') == false) : $c = new Page(); $c->parent = $parent; $template = templates()->get('template_custom_orders'); $c->template = $template; $c->title = 'Custom Orders'; $c->name = 'custom_orders'; $c->save(); endif; } /** * Optional method called when the module is uninstalled * * This method undoes anything that the install() method did. * For instance, remove installed DB tables, files, etc. * */ public function createCustomOrder() { // TODO } public function ___uninstall() { $fields = wire('fields'); $fieldgroups = wire('fieldgroups'); $templates = wire('templates'); $custom_order_pages = wire('pages')->find('template=template_custom_orders|template_custom_order')->count(); // if ($custom_order_pages > 0) // throw new WireException("There are pages using custom-order-templates. Remove those first before uninstall"); foreach($custom_order_pages as $custom_order_page){ $custom_order_page->delete(); } if($fields->get('custom_order_id')) : foreach ($fields->get('custom_order_id')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_products')) : foreach ($fields->get('custom_order_products')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_customer_name')) : foreach ($fields->get('custom_order_customer_name')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_customer_address')) : foreach ($fields->get('custom_order_customer_address')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_customer_emailaddress')) : foreach ($fields->get('custom_order_customer_emailaddress')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_id')) $fields->delete($fields->get('custom_order_id')); if($fields->get('custom_order_products')) $fields->delete($fields->get('custom_order_products')); if($fields->get('custom_order_customer_name')) $fields->delete($fields->get('custom_order_customer_name')); if($fields->get('custom_order_customer_address')) $fields->delete($fields->get('custom_order_customer_address')); if($fields->get('custom_order_customer_emailaddress')) $fields->delete($fields->get('custom_order_customer_emailaddress')); if($fields->get('custom_order_shipping')) $fields->delete($fields->get('custom_order_shipping')); if($fields->get('custom_order_total_taxes')) $fields->delete($fields->get('custom_order_total_taxes')); if($fields->get('custom_order_status')) $fields->delete($fields->get('custom_order_status')); if($templates->get('template_custom_orders')) $templates->delete($templates->get('template_custom_orders')); if($templates->get('template_custom_order')) $templates->delete($templates->get('template_custom_order')); } } 1 Link to comment Share on other sites More sharing options...
teppo Posted August 9, 2022 Share Posted August 9, 2022 3 hours ago, fruid said: And then, what's the relevant file regarding fieldgroups? I still don't get it. You've stumbled upon a concept that is, admittedly, a little obscure. The Fieldgroup class comments explain what Fieldgroups are all about, and it boils down to this: Quote A group of fields that is ultimately attached to a Template. Fieldgroups are something that most users don't need to concern themselves with, but technically there's always a fieldgroup behind a template — fields don't belong directly to template, but rather to a fieldgroup, which in turn may be used by one or more templates. Multiple templates using one fieldgroup is, though, pretty rare — I don't think I've ever had that need myself ? All the examples I've seen so far have created a Fieldgroup and then applied it to a Template. If there's a way to create template with fields without creating a fieldgroup to hold said fields, I'm not aware of it (or can't remember it right now — to be honest creating templates via API is not something I do often). 3 hours ago, fruid said: Do I absolutely need a third party module to get my first module to work? No. RockMigrations can make things easier and provide you with many useful tools, but it's not required by any means. If you just need to create a few templates and fields in a module, I personally feel that it's probably overkill to add a dependency to a third party module for that task alone. (Though it could be quite helpful in the future in case you also want to keep those templates/fields in sync with new versions of your module.) 3 hours ago, fruid said: Templates, fields and many more things are explained throroughly but fieldsgroups and many other things completely fall through. I would recommend opening a request for (additional) documentation at https://github.com/processwire/processwire-requests. In my opinion you have a valid point when it comes to fieldgroups — creating templates with fields (and fieldgroups) via API could be more clearly documented. As for "many other things", I don't know what those things would be, but if you can think of some then by all means open a request for those as well. 5 hours ago, fruid said: So do I attach the fieldgroup to the template or field? Do I first create the fieldgroup and then attach fields to it and then the template? Or what's the proper order? And what about uninstalling, what do I need to do first? Based on your latest message it seems that you've already solved this — nice! ? 3 Link to comment Share on other sites More sharing options...
froot Posted August 9, 2022 Author Share Posted August 9, 2022 haha OK I see what you did there ? Just cause I succeeded to the point where I uninstall it doesn’t mean the installation went smoothly. Whichever order I create fields, fieldgroups and templates, or no fieldgroups at all, I always get some error mentioning fieldgroups, different maybe but never without. So I couldn’t care less about field groups but PW seems to insist on it despite of what everyone says. Either that or something else is fishy. Run my code if you want and tell me you don’t get an error like that. Link to comment Share on other sites More sharing options...
froot Posted August 10, 2022 Author Share Posted August 10, 2022 I just removed all mentions of fieldgroups in my code, uninstalled all fields, templates and pages in the context of my module, emptied the trash, updated the PW version and then tried to reinstall my module and guess what… Template 'template_custom_orders' cannot be saved because it has no fieldgroup assigned So if you don't need fieldgroups to get started on modules, how can that be? So tired of this ? Link to comment Share on other sites More sharing options...
bernhard Posted August 10, 2022 Share Posted August 10, 2022 50 minutes ago, fruid said: So if you don't need fieldgroups to get started on modules, how can that be? <?php namespace ProcessWire; class Hello extends WireData implements Module { public static function getModuleInfo() { return [ 'title' => 'Hello', 'version' => '0.0.1', 'summary' => 'Your module description', 'autoload' => true, 'singular' => false, 'icon' => 'smile-o', 'requires' => [], 'installs' => [], ]; } } Voila - that's a working module without fieldgroups! Here an example without using RockMigrations (I'm so happy I've not had to do this for a very long time...): <?php namespace ProcessWire; class Hello extends WireData implements Module { const name = 'hello'; public static function getModuleInfo() { return [ 'title' => 'Hello', 'version' => '0.0.1', 'summary' => 'Your module description', 'autoload' => false, 'singular' => true, 'icon' => 'smile-o', 'requires' => [], 'installs' => [], ]; } public function ___install() { // create new fieldgroup $fg = $this->wire(new Fieldgroup()); $fg->name = self::name; $fg->save(); // create new template $t = $this->wire(new Template()); $t->name = self::name; $t->fieldgroup = $fg; $t->save(); // create field $f = $this->wire(new Field()); $f->type = 'text'; $f->name = self::name; $f->save(); // add field to template $fg->add($f); $fg->save(); } public function ___uninstall() { // delete template and fieldgroup $tpl = $this->wire->templates->get(self::name); if($tpl) { $this->wire->templates->delete($tpl); $this->wire->fieldgroups->delete($tpl->fieldgroup); } // delete field $this->wire->fields->delete($this->wire->fields->get(self::name)); } } And using RockMigrations: <?php namespace ProcessWire; class Hello extends WireData implements Module { const name = 'hello'; public static function getModuleInfo() { return [ 'title' => 'Hello', 'version' => '0.0.1', 'summary' => 'Your module description', 'autoload' => false, 'singular' => true, 'icon' => 'smile-o', 'requires' => [ 'RockMigrations>=0.13.3', ], 'installs' => [], ]; } public function init() { /** @var RockMigrations $rm */ $rm = $this->wire->modules->get('RockMigrations'); $rm->watch($this); } public function migrate() { /** @var RockMigrations $rm */ $rm = $this->wire->modules->get('RockMigrations'); $rm->createField(self::name, 'text', ['label' => 'Your field label']); $rm->createTemplate(self::name); $rm->addFieldToTemplate('title', self::name); $rm->addFieldToTemplate(self::name, self::name); } public function ___install() { $this->migrate(); } public function ___uninstall() { /** @var RockMigrations $rm */ $rm = $this->wire->modules->get('RockMigrations'); $rm->deleteTemplate(self::name); $rm->deleteField(self::name); } } When not using RockMigrations you need to be very careful with everything you do. It throws errors all the time if something does not exist what you are trying to remove/delete or it throws errors if somethings exists that you are trying to create... It throws errors if the execution order is not correct. It throws errors if you try to delete a template that is used by at least one page, etc etc... RockMigrations takes care of all those things so that you can focus on what you want to do. And you don't need to always install/uninstall your module while developing. Refreshing your browser will fire the new migrations. RockFrontend can even take care of that as well... 50 minutes ago, fruid said: So tired of this ? ?♂️ You're welcome: https://github.com/sponsors/baumrock 1 Link to comment Share on other sites More sharing options...
froot Posted August 10, 2022 Author Share Posted August 10, 2022 7 minutes ago, bernhard said: <?php namespace ProcessWire; class Hello extends WireData implements Module { public static function getModuleInfo() { return [ 'title' => 'Hello', 'version' => '0.0.1', 'summary' => 'Your module description', 'autoload' => true, 'singular' => false, 'icon' => 'smile-o', 'requires' => [], 'installs' => [], ]; } } Voila - that's a working module without fieldgroups! Admitted, it's a working module without fieldgroups, but also without fields…… Link to comment Share on other sites More sharing options...
teppo Posted August 11, 2022 Share Posted August 11, 2022 On 8/9/2022 at 10:15 PM, fruid said: Just cause I succeeded to the point where I uninstall it doesn’t mean the installation went smoothly. Whichever order I create fields, fieldgroups and templates, or no fieldgroups at all, I always get some error mentioning fieldgroups, different maybe but never without. Looking at your code, and specifically your ___uninstall() method, I can spot at least a few potential issues. $custom_order_pages = wire('pages')->find('template=template_custom_orders|template_custom_order')->count(); // if ($custom_order_pages > 0) // throw new WireException("There are pages using custom-order-templates. Remove those first before uninstall"); foreach($custom_order_pages as $custom_order_page){ $custom_order_page->delete(); } The foreach loop above doesn't make any sense. You're essentially trying to iterate over the count() value, which is an integer. If you remove the "->count()" part, it's more likely to work — unless your pages are hidden/unpublished, in which case you should also add "include=all" to the selector string. if($fields->get('custom_order_id')) : foreach ($fields->get('custom_order_id')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_products')) : foreach ($fields->get('custom_order_products')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_customer_name')) : foreach ($fields->get('custom_order_customer_name')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_customer_address')) : foreach ($fields->get('custom_order_customer_address')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; if($fields->get('custom_order_customer_emailaddress')) : foreach ($fields->get('custom_order_customer_emailaddress')->getFieldgroups() as $fieldgroup) { $fieldgroups->delete($fieldgroup); } endif; The part above looks fine, but keep in mind that none of these fields were added to the "fieldgroup_custom_orders" fieldgroup, so that won't get removed. You should probably add something like this right after the lines above: $fieldgroups->delete($fieldgroups->get('fieldgroup_custom_orders')); Also this is likely unrelated to your bigger issue(s) here, but note that this part doesn't seem quite right: $template_custom_orders->childrenTemplates = array($template_custom_order->id); There's no "childrenTemplates" property on Templates. What you probably meant was childTemplates. Finally, your ___install() method is rather error prone. In case any of the fields already exist, you will run into problems. For an example, look at these lines here: if(!$fields->get('custom_order_id')) : $custom_order_id = new Field(); // int or text $custom_order_id->type = $this->modules->get("FieldtypeInteger"); $custom_order_id->title = 'Custom Order ID'; $custom_order_id->name = wire('sanitizer')->pageName($custom_order_id->title, true); $custom_order_id->label = 'Order ID'; $custom_order_id->tags = 'kiosk'; $custom_order_id->save(); endif; What happens here is that a) if custom_order_id field is not found, it will be created and stored in $custom_order_id, but b) if it doesn't exist, then it won't be created and it also won't be stored in $custom_order_id. Basically if install/uninstall has been only partially completed, even once, then you're likely going to run into issues. Here's one approach that should work a bit more reliably: $custom_order_id = $fields->get('custom_order_id'); if(!$custom_order_id) : $custom_order_id = new Field(); // int or text $custom_order_id->type = $this->modules->get("FieldtypeInteger"); $custom_order_id->title = 'Custom Order ID'; $custom_order_id->name = wire('sanitizer')->pageName($custom_order_id->title, true); $custom_order_id->label = 'Order ID'; $custom_order_id->tags = 'kiosk'; $custom_order_id->save(); endif; 19 hours ago, fruid said: I just removed all mentions of fieldgroups in my code, uninstalled all fields, templates and pages in the context of my module, emptied the trash, updated the PW version and then tried to reinstall my module and guess what… Template 'template_custom_orders' cannot be saved because it has no fieldgroup assigned It's quite likely that this error is linked to the issue I mentioned above about your install/uninstall method being error prone: if the process has failed even once, this part will not work as expected: if(!$fieldgroups->get('fieldgroup_custom_orders')) : $fieldgroup_custom_orders = new Fieldgroup(); $fieldgroup_custom_orders->name = 'fieldgroup_custom_orders'; $fieldgroup_custom_orders->add($this->fields->get('title')); $fieldgroup_custom_orders->save(); $this->message('creating fieldgroup fieldgroup_custom_orders'); endif; This — and every other code snippet that uses similar approach — should be replaced with something like this instead: $fieldgroup_custom_orders = $fieldgroups->get('fieldgroup_custom_orders'); if(!$fieldgroup_custom_orders) : $fieldgroup_custom_orders = new Fieldgroup(); $fieldgroup_custom_orders->name = 'fieldgroup_custom_orders'; $fieldgroup_custom_orders->add($this->fields->get('title')); $fieldgroup_custom_orders->save(); $this->message('creating fieldgroup fieldgroup_custom_orders'); endif; 19 hours ago, fruid said: So if you don't need fieldgroups to get started on modules, how can that be? If your module needs to create templates via the API and add fields to said templates, you do need fieldgroups. There's no way around that. 2 Link to comment Share on other sites More sharing options...
bernhard Posted August 11, 2022 Share Posted August 11, 2022 On 8/9/2022 at 11:34 AM, fruid said: No offense but RockMigrations sounds like yet another API to learn to make things "easier". I still wonder why do I even need fieldgroups when almost all other modules don't use them. As soon as you want to add fields to templates you need to work with fieldgroups, as fieldgroups are the thing that connect templates with fields. I think the fact that many modules don't use fieldgroups is that - as you have realised - it is not that easy to create fields/templates/fieldgroups via API as there is a lot of things you need to take care of and that are not obvious and make things tedious. 7 hours ago, teppo said: Basically if install/uninstall has been only partially completed, even once, then you're likely going to run into issues. That's only one example of what I said above. But there are modules that create fieldgroups, of course. For example here: https://github.com/kongondo/Blog/blob/master/BlogInstallWizard.php The frustration you are going through is exactly the reason why I built RockMigrations... 1 Link to comment Share on other sites More sharing options...
froot Posted August 11, 2022 Author Share Posted August 11, 2022 thanks @teppo for the input! I actually managed to get it to work the other day, some of what you mention here is what made the difference. - yes, the loop with ->count is of course nonsense, just a typo from copy-pasting - yes, I delete the fieldgroup somewhere later in the code - yes, it most likely needs to be ->childTemplates, ->childrenTemplates doesn't cause any issue, but not sure either if it actually works, so I'll change that - and lastly, thanks for the last tip, I was actually running into issues, something with "null page" cannot be deleted or whatever, so I did something with if($mypage->id>0) which is not so elegant and probably your suggestion will fix that. So, resuming, I can tell that this order works for me (not sure if others work but this one does)… ___install: 1. check if field exists and if not create field 2. check it template exists and if not create the fieldgroup, 3. add name to fieldgroup 4. add title-field to fieldgroup 5. add custom fields to fieldgroup 6. save the fieldgroup 7. create template 8. set the fieldgroup of the template 9. save template ___uninstall: 1. delete template(s) 2. delete fieldgroup(s) 3. delete field(s) Link to comment Share on other sites More sharing options...
froot Posted August 11, 2022 Author Share Posted August 11, 2022 another question, I'm saving some values in a field of type FieldtypeTextarea that lives on a custom page I create. Then I want to return that value in a table in a Process-module. I want line breaks for that value, so when I add the value I simply put <br> inbetween the lines but the Process-module doesn't know HTML so it just spits it out as is. So how do I do that? Is there a way to just tell the Process-module that this is HTML? Should I use a sanitizer? Or should I create a different type of field to begin with? If so, which type? Basically my question is, how to add HTML markup in a Process-module table? Link to comment Share on other sites More sharing options...
Jan Romero Posted August 11, 2022 Share Posted August 11, 2022 You mean MarkupAdminDataTable? https://processwire.com/api/ref/markup-admin-data-table/ Have you tried setting encodeEntities to false? You’ll have to escape the contents yourself, but you’ll be able to use HTML tags. 1 Link to comment Share on other sites More sharing options...
froot Posted August 11, 2022 Author Share Posted August 11, 2022 @Jan Romero thanks, exactly what I meant! Link to comment Share on other sites More sharing options...
froot Posted August 11, 2022 Author Share Posted August 11, 2022 can you return an admin page within a process module? Seems like that page is not accessible, any other page works fine with the same code but not a page with admin template under /admin/page/ If not, how can I do that? Link to comment Share on other sites More sharing options...
froot Posted August 13, 2022 Author Share Posted August 13, 2022 I spoke too soon, installing and uninstalling doesn't (yet) properly work. I created a module that extends class Process to create a little dashboard. I uninstalled the module for some reason and somehow it does half the job. I still have the dashboard's title in the above menu but it's an unknown path. Trying to delete it returns an error saying: Page (0) cannot be deleted: it is a NullPage Here's my code: public function ___uninstall() { // ... $dashboard = wire('pages')->get('/admin/kiosk_dashboard/'); if($dashboard){ $dashboard->delete(); } // ... } On a positive note, the fieldgroups don't seem to cause any issues anymore ^^ Link to comment Share on other sites More sharing options...
teppo Posted August 13, 2022 Share Posted August 13, 2022 3 hours ago, fruid said: Trying to delete it returns an error saying: Page (0) cannot be deleted: it is a NullPage The problem here is that $pages->get() can return either a Page or a NullPage. NullPage is a null object that (in terms of API usage) behaves like a Page, but doesn't have any content, and doesn't actually match any page on the system. Thus you can't delete a NullPage. NullPage doesn't have an ID, so here's one way you can solve this, checking for an object with an ID: public function ___uninstall() { // ... $dashboard = wire('pages')->get('/admin/kiosk_dashboard/'); if($dashboard->id){ $dashboard->delete(); } // ... } Edit: just for the record, admin pages can be renamed or moved, so keep in mind that at least technically your uninstall routine may leave "orphaned" pages behind. In case your dashboard page uses your Process module as it's process, here's what you could do instead of relying on the path/name always being /admin/kiosk_dashboard/: public function ___uninstall() { // ... $module_id = wire('modules')->getModuleID($this); $dashboard = wire('pages')->get('template=admin, process=' . $module_id); if($dashboard->id){ $dashboard->delete(); } // ... } 2 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