-
Posts
32 -
Joined
-
Last visited
Everything posted by thei
-
Fieldtype module, field names cannot be used in selectors
thei replied to thei's topic in Module/Plugin Development
next effect: mapping of fieldnames to true columns names has also be handled by overloading getMatchQuerySort() of FieldType private function mapColumnNames($fieldName) { $subfieldMap = [ 'date_from'=>'data', 'date_to'=>'to']; $col = array_key_exists($fieldName, $subfieldMap) ? $subfieldMap[$fieldName] : $fieldName; return $col; } /** * adapts the getMatchQuery() of the parent by mapping the column names */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { return parent::getMatchQuery($query, $table, $this->mapColumnNames($subfield), $operator, $value); } /** * adapts the getMatchQuerySort() of the parent by mapping the column names */ public function getMatchQuerySort(Field $field, $query, $table, $subfield, $desc) { $col = $this->mapColumnNames($subfield); // return PW internal sort query string return "_sort_{$field->name}_{$subfield}.{$col} " . ($desc ? 'DESC' : 'ASC'); } Welcome to the PW reverse engineering club - Happy hour wednesday 17-18 pm -
Fieldtype module, field names cannot be used in selectors
thei replied to thei's topic in Module/Plugin Development
i did the following public function getMatchQuery($query, $table, $subfield, $operator, $value) { $subfieldMap = [ 'date_from'=>'data', 'date_to'=>'to']; $sub = $subfieldMap[$subfield]; return parent::getMatchQuery($query, $table, $sub, $operator, $value); } overwriting the basic function getMatchQuery() defined in core/FieldType.php this helps. Is this the regular way to implement Fieldtypes? -
i wrote a simple fieldtype which implements a date_from and date_to sub field. When defining a field "interval" and and then using it in a template "sampletemplate" everything works. In "sampletemplate.php" fields can be accessed by $page->interval->date_from and $page->interval->date_to - everything fine! When using in a selector these fieldnames are invalid. e.g. $pages->find("template=sampletemplate, interval.date_to > today") throws an exception, no column with that name... the schema definition for the fields is public function getDatabaseSchema(Field $field) : array { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'date default null'; // date_from $schema['to'] = 'date default null'; // date_to $workspace->url = 'KEY data (`data`, `to`)'; $schema['keys']['to'] = 'KEY `to` (`to`)'; return $schema; } i.e. date_from is mapped on standard column 'data' and date_to is mapped on 'to'. When i use the real database column name $pages->find("template=sampletemplate, interval.to > today") it works. How is it possible to "correct" the names so that the name to access the field (e.g. $page->interval->data_from) and the name to be used in a selector is the same? and: Is there a description of the PW database schema definition to be used in a getDatabaseSchema() ?
-
For any reason it is not possible to use a simple dumb array as an internal data object. i refactored the module and implemented an data object derived from WireData. Now it works. It seems to be important processInput directly manipulates the given attribute $value (first line of procedure) public function ___processInput(WireInputData $input) { $value = $this->attr('value'); $value->date_from = $input->get('date_from'); $value->date_to = $input->get('date_to'); return $this; }
-
Dear Ryan, thank you for your help. But after i uninstalled and purged the old stuff and installed your refactored code the behaviour is similar. The db table is ok and looks like defined in getDatabaseSchema(). processInput receives the input values and $this->val($value); // should be ... $this->attr('value', $value); holds the right data. After that no further data processing. Even in FieldType.php no savePageField() (which was called when using my posted old code but does not detect a change and therefore returned if(!$page->isChanged($field->name)) return true;
-
Hello, i am trying to develop a simple Fieldtype module and followed Tutorial on developing a Fieldtype module. I installed the module (Fieldtype and Inputfield) created a field of that type, add such a field to a template and edited a page. After entering values and click save button the method processInput() is invoked but then nothing happens, sleepValue() and wakeupValue() are never invoked, the resulting editor page ends up showing empty entry fields. What i'm getting wrong? <?php namespace ProcessWire; class InputfieldTimeframe extends Inputfield { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them. * Information about your module and some settings and definition of module dependcies * @return array */ public static function getModuleinfo() { return [ 'title' => __('Inputfield Timeframe', __FILE__), 'summary' => __('Inputfield for start date and end date', __FILE__), 'author' => 'T. Heinlein', 'version' => "0.1.2", 'icon' => 'calendar', 'permanent' => false, 'requires' => [ 'PHP>=8.0.0', 'ProcessWire>=3.0.244', 'FieldtypeTimeframe' ], ]; } public function __construct() { parent::__construct(); } public function ___render() { $value = $this->attr('value'); $field = $this->field; $html = "<div class='interval'>" . "<input type='date' name='datefrom'>" . "<input type='date' name='dateto'>" . "</div>"; return "<div style='border:solid 1px red;'>$html</div>"; } public function renderValue() { $value = $this->attr('value'); return "$value"; // TODO: locale format } public function ___processInput(WireInputData $input) :self { $value = $this->attr('value'); $input->datefrom = trim($input->datefrom); $input->dateto = trim($input->dateto); $value['datefrom'] = $input->datefrom; $value['dateto'] = $input->dateto; bd($value, "processInput"); return $this; } } <?php namespace ProcessWire; class FieldtypeTimeframe extends Fieldtype { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them. * Information about your module and some settings and definition of module dependcies * @return array */ public static function getModuleinfo() { return [ 'title' => __('Timeframe', __FILE__), 'summary' => __('Fieldtype representing a timeframe with start and end date', __FILE__), 'author' => 'T. Heinlein', 'version' => "0.1.1", 'icon' => 'calendar', 'installs' => 'InputfieldTimeframe', 'requires' => [ 'PHP>=8.0.0', 'ProcessWire>=3.0.244' ] ]; } public function __construct() { parent::__construct(); } /** * define the table columns needed for that field type AND an additional column 'data' for a primary key. * (this is low level input for the database) * *** this method MUST be implemented when writing a custom fieldtype */ public function getDatabaseSchema(Field $field) : array { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'varchar(64) NOT NULL'; /* data is required field for system purposes */ $schema['datefrom'] = 'date'; $schema['dateto'] = 'date'; return $schema; } public function ___sleepValue(Page $page, Field $field, $value) { bd($value, "sleepValue"); $df = $value['datefrom']; $dt = $value['dateto']; $res = ['datefrom'=> $df, 'dateto' => $dt]; return $res; } public function wakeupValue(Page $page, Field $field, $value) { bd($value, "wakeupValue"); return $value; } public function getBlankValue(Page $page, Field $field) { return array(); } public function getInputfield(Page $page, Field $field) { $inputfield = $this->wire('modules')->get('InputfieldTimeframe'); $inputfield->class = $this->className(); /* adds the class name as a css class to the wrapper element */ $pageValue = $page->get($field->name); $inputfield->setAttribute('value', $pageValue); return $inputfield; } /** * @param Page $page * @param Field $field * @param int|object|WireArray|string $value * @return int|null|object|OpeningHours|WireArray|string * will be called on before sleepValue and after wakeupValue */ public function sanitizeValue(Page $page, Field $field, $value) { if (! is_array($value) ) { $value = $this->getBlankValue($page, $field); } return $value; } }
-
Hi Brodie, Do you know their problems in more detail? Have you some knowledge how they want to maintain their sites? i would prefer to implement sufficient different roles to enable people to maintain their own scope of interest/responsibility. So that everybody who has a natural interest to keep it up to date can do it by himself. Which features and media will you support for a rotary? May be It would be good to create a typical "rotary basic structure" for the content? Will they accept your product? 🙂
-
The problem could be solved by modifying ProcessController->isAjax() i.e. in wire/core/ProcessController.php In case of Htmx as well in case of ajax this method should return true. More flexible: when ProcessController would delegate this to its Process object (and the parent Process class would provide such a method by default) than a module could override the default implementation isAjax(). And ... since this is no longer related to ajax only but decides between a direct output of content or embedding the output in a admin page the method should be named to needsDirectOutpput() or similar. context: $config ->ajax is set in the ProcessWire instance by evaluating the http header HTTP_X_REQUESTED_WITH. It is used by admin.php to control an output buffer (ob_...) inbetween $ajax is overwritten by ProcessController->isAjax(). If true the output returned by the processes execute() is not embbedded in an admin page but output directly. In the current implementation ProcessController->isAjax() checks the http header in the same way like the ProcessWire instance. So this has no added value in the moment. It seems this is some sort of preparation for future plans. e.g. this works: in ProcessController.php (delegation to Process) public function isAjax() { return $this->process->isAjax(); } in Process.php (default implementation) public function isAjax() { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); } now this can be overridden by the modules class to enable htmx output Discussion: this is a "quick" solution. May be there is some kind of architectural snag: the decision whether something is to be embedded in an outer frame / page would belong to the view part e.g. in a "viewFile" instead of the modules main object - which acts as a controller in this context. If - alternatively - we want to handle the mode of output controlled purely on a protocol basis we should have something like $input->asynchProtocolType() which should return true in case of ajax, htmx and so on and should be hookable. So this is a question to the ProcessWire Masters here... 🙂
-
Hi, thanks for your response! But: meanwhile i found out... it's not possible ajax handling is processed in admin.php and only there the ProcessController instance is created. This controller has its own method isAjax() and gets the result directly from the http header. But there's no way to hook in here. so one has to modify the admin.php - which i want to avoid. i simply manipulated the $ajax with the debugger and it works except that tracy debugger pollutes the response with its own stuff.
-
When writing a process module the execute<name> methods output ... either is handled as the part of content embedded in a admin page ... or as output for an appropriate ajax-request (if ajax is detected while processing the incoming request by ProcessWire). When integrating HTMX this leads to an architectural problem: output of HTMX cannot be handled inside the modules process object (which can be seen as "controller") because it is handled as content and will be embedded in an admin page layout. One has to introduce a second route and a has to create a second Controller-Object to handle especially HTMX-responses. put on the wishlist: Processing the execute<name> methods should have something like an option to control processing the output (direct output and appropriate HTTP-headers) - not only ajax.
-
Thanks you very much for your help! i did some tests and found out: the only bad guy is 'template_id' (or 'template_ids'). Now i eliminated only this parameter and ran the whole bunch (including the "unnecessary parameters"). AND it works fine. I copied the code from the RockMirations Code field. i expected this generated code from RockMigration would be the correct code for setting up the fields. The generated code includes template_id => 0 and sometimes template_id => '' both cause a corrupted database.
-
The SQL error mentioned above is not related to RM. but here some testing that show the strange behavior when updating / creating repeater fields - may be this helps A) Situation: repeater field 'contact' exists, all caches cleared MariaDB [PW]> select id,name,data from templates where id = 51; | id | name | data | 51 | repeater_contact | {"noChildren":1, ... ,"modified":1727544508} | 1) running Config Migrations v6.7 with a ...fields/contact.php: | id | name | data | | 51 | repeater_contact | {"noChildren":1, ... ,"modified":1737381992,"_lazy":1,"_rockmigrations_log":"--- 2025-01-20 15:06:31 ---\n"} | | 68 | repeater_contact1 | {"noChildren":1, ... ,"modified":1737382002} | 2) remove contact.php and refresh: no changes 3) delete from templates where id=68; and then refresh: a new contact repeater field without Config Migrations having a contact.php | 51 | repeater_contact | {"noChildren":1, ... ,"modified":1737381992,"_lazy":1,"_rockmigrations_log":"--- 2025-01-20 15:06:31 ---\n"} | | 69 | repeater_contact2 | {"noChildren":1, ... ,"modified":1737382565} | 4) delete from templates where id=69; disable Config Migrations in Module Settings and then refresh: a new contact repeater field without Config Migrations active: | 51 | repeater_contact | {"noChildren":1, ... ,"modified":1737381992,"_lazy":1,"_rockmigrations_log":"--- 2025-01-20 15:06:31 ---\n"} | | 70 | repeater_contact3 | {"noChildren":1, ... ,"modified":1737382877} | ================================================================ B) Situation: repeater field 'contact' DOES NOT exists, all caches cleared 1) running Config Migrations v6.7 with a ...fields/contact.php: | 68 | repeater_contact | {"noChildren":1, ... ,"modified":1737383770,"_rockmigrations_log":"--- 2025-01-20 15:36:10 ---\n"} | | 69 | repeater_contact1 | {"noChildren":1, ... ,"modified":1737383771,"_rockmigrations_log":"--- 2025-01-20 15:36:11 ---\n"} | | 70 | repeater_contact2 | {"noChildren":1, ... ,"modified":1737383771} | Rock-Migrations Log entries: Trigger RockMigrations::migrationsDone --- config migration hook: afterData (0 files) --- /site/RockMigrations/fields/contact.php --- second run: migrate data --- --- config migration hook: beforeData (0 files) --- --- config migration hook: afterAssets (0 files) --- Tag: Name: contact --- first run: create assets --- --- config migration hook: beforeAssets (0 files) --- --- create PHP constant traits --- ### Running Config Migrations ### ### Migrate items with priority #1000 ### Running migrations from watchfiles ... Detected change in /site/RockMigrations/fields/contact.php -------------------------------------
-
above i wrote "Even when i removed the Config Migrations files for these repeater fields these repeater fields have "doubles" in the template fields" i.e. i couldn't reproduce this effect (new repeater-field rows when repeater-field Config-Migrations file is moved away. But: a repeater field Config - Migrations file produces to an improper database when refreshing modules is clicked
-
<?php /* repeater field contact */ return [ 'accordionMode' => 0, 'allowContexts' => [ 0 => 'repeaterTitle', ], 'collapsed' => 0, 'columnWidth' => 100, 'familyFriendly' => 0, 'fields' => [ 'desc' => [ 'rows' => 1, ], 'role' => [ 'columnWidth' => 25, ], 'email' => [ 'columnWidth' => 25, ], 'phone' => [ 'columnWidth' => 25,], 'mobile' => ['columnWidth' => 25, ], ], 'flags' => 0, 'icon' => 'user-md', 'label' => 'Contact', 'lazyParents' => '', 'loudControls' => 0, 'parent_id' => 0, 'rememberOpen' => 0, 'repeaterAddLabel' => '', 'repeaterCollapse' => 3, 'repeaterDepth' => '', 'repeaterLoading' => 1, 'repeaterMaxItems' => '', 'repeaterMinItems' => '', 'repeaterTitle' => '', 'required' => '', 'requiredIf' => '', 'showIf' => '', 'tags' => 'address organisation', 'template_id' => 0, 'themeBorder' => '', 'themeColor' => '', 'themeOffset' => '', 'type' => 'FieldtypeRepeater', ]; <?php /* field 'desc' */ return [ 'collapsed' => 0, 'columnWidth' => 100, 'contentType' => 0, 'flags' => 0, 'htmlOptions' => '', 'icon' => 'sticky-note-o', 'inputfieldClass' => 'InputfieldTextarea', 'label' => 'Description', 'maxlength' => 0,'minlength' => 0, 'placeholder' => '', 'required' => '', 'requiredAttr' => '', 'requiredIf' => '', 'rows' => 5, 'showCount' => 0, 'showIf' => '', 'stripTags' => '', 'tags' => 'text', 'textformatters' => [ 0 => 'TextformatterEntities', ], 'themeBlank' => '', 'themeBorder' => '', 'themeColor' => '', 'themeInputSize' => '', 'themeOffset' => '', 'type' => 'FieldtypeTextarea', ]; <?php /* field 'role' */ return [ 'collapsed' => 0, 'columnWidth' => 100, 'flags' => 0, 'icon' => 'black-tie','inputfieldClass' => '','label' => 'Role', 'maxlength' => 2048, 'minlength' => 0, 'pattern' => '', 'placeholder' => '', 'required' => '', 'requiredAttr' => '', 'requiredIf' => '', 'showCount' => 0, 'showIf' => '', 'size' => 0, 'stripTags' => '', 'tags' => 'organisation', 'textformatters' => [ 0 => 'TextformatterEntities', ], 'themeBlank' => '', 'themeBorder' => '', 'themeColor' => '', 'themeInputSize' => '', 'themeInputWidth' => '','themeOffset' => '', 'type' => 'FieldtypeText', ]; <?php /* email */ return [ 'collapsed' => 0, 'columnWidth' => 100, 'flags' => 9, 'icon' => 'at', 'inputfieldClass' => '', 'label' => 'E-Mail Address', 'maxlength' => 255, 'minlength' => 0, 'pattern' => '', 'placeholder' => '', 'required' => '', 'requiredAttr' => '', 'requiredIf' => '', 'showCount' => 0, 'showIf' => '', 'size' => 0, 'stripTags' => 1, 'tags' => 'communication', 'textformatters' => [ 0 => 'TextformatterEntities', ], 'themeBlank' => '', 'themeBorder' => '', 'themeColor' => '', 'themeInputSize' => '', 'themeInputWidth' => '', 'themeOffset' => '', 'type' => 'FieldtypeText', ]; ~ <?php /* phone */ return [ 'collapsed' => 0, 'columnWidth' => 100, 'flags' => 0, 'icon' => 'phone', 'inputfieldClass' => '', 'label' => 'Phone', 'maxlength' => 2048, 'minlength' => 0, 'pattern' => '', 'placeholder' => '', 'required' => '', 'requiredAttr' => '', 'requiredIf' => '', 'showCount' => 0, 'showIf' => '', 'size' => 0, 'stripTags' => '', 'tags' => 'communication', 'textformatters' => [ 0 => 'TextformatterEntities', ], 'themeBlank' => '', 'themeBorder' => '', 'themeColor' => '', 'themeInputSize' => '', 'themeInputWidth' => '', 'themeOffset' => '', 'type' => 'FieldtypeText', ]; <?php /* mobile */ return [ 'collapsed' => 0, 'columnWidth' => 100, 'flags' => 0, 'icon' => 'mobile', 'inputfieldClass' => '', 'label' => 'Mobile', 'maxlength' => 2048, 'minlength' => 0, 'pattern' => '', 'placeholder' => '', 'required' => '', 'requiredAttr' => '', 'requiredIf' => '', 'showCount' => 0, 'showIf' => '', 'size' => 0, 'stripTags' => '', 'tags' => 'communication', 'textformatters' => [ 0 => 'TextformatterEntities', ], 'themeBlank' => '', 'themeBorder' => '', 'themeColor' => '', 'themeInputSize' => '', 'themeInputWidth' => '', 'themeOffset' => '', 'type' => 'FieldtypeText', ]; ~ when i put the files shown above in RockMigrations/fields and click module refresh... | 51 | repeater_contact | {"noChildren":1,"noParents":1,"slashUrls":1,"pageClass":"RepeaterPage","noGlobal":1,"compile":3,"modified":1736510539,"_lazy":1,"_rockmigrations_log":"--- 2025-01-10 13:02:19 ---\n"} | | 77 | repeater_contact7 | {"noChildren":1,"noParents":1,"slashUrls":1,"pageClass":"RepeaterPage","noGlobal":1,"compile":3,"modified":1736933902,"_lazy":1,"_rockmigrations_log":"--- 2025-01-15 10:38:22 ---\n"} | | 79 | repeater_contact8 | {"noChildren":1,"noParents":1,"slashUrls":1,"noGlobal":1,"compile":3,"modified":1736933910,"_rockmigrations_log":"--- 2025-01-15 10:38:30 ---\n"} | | 80 | repeater_contact9 | {"noChildren":1,"noParents":1,"slashUrls":1,"pageClass":"RepeaterPage","noGlobal":1,"compile":3,"modified":1736933916} |
-
Thanx for your quick reaction. Hm, "steps" i installed 6.5.0 (previously a had an old version (5.2..?) installed) i copied the code from the RockMigrations code field to set up the files using fieldname as filename in RockMigrations/fields. e.g. 'label.php' below <?php return [ 'collapsed' => 0, 'columnWidth' => 100, 'flags' => 0, 'icon' => 'bookmark-o', 'inputfieldClass' => '', 'label' => 'Label', 'maxlength' => 2048, 'minlength' => 0, 'pattern' => '', 'placeholder' => '', 'required' => '', 'requiredAttr' => '', 'requiredIf' => '', 'showCount' => 0, 'showIf' => '', 'size' => 0, 'stripTags' => '', 'tags' => 'text', 'textformatters' => [ 0 => 'TextformatterEntities', ], 'themeBlank' => '', 'themeBorder' => '', 'themeColor' => '', 'themeInputSize' => '', 'themeInputWidth' => '', 'themeOffset' => '', 'type' => 'FieldtypeText', ]; i remember i did some changes via GUI and did not update the appropriate field file. At the moment i have no idea how/where to start with debugging RockMigrations
-
i started to use Config Migrations in an existing processwire application. In directory site/RockMigrations/fields i created all the files defining the fields. Up to now i created only two templates (i.e. 2 of 15 but this shouldn't matter) in the appropriate templates folder - so far. yesterday my database crashed after clicking the admin modules page - a PDO Error 1062 / duplicates... so i did some research in the database. i found the templates table contains something like duplicated repeater field rows - the field 'contact' has some duplicates with same name followed by numbers 'contact8' 'contact9' etc. I could repair the database by deleting these strange rows. When i click on module refresh, some new repeater field rows will be created. The crash seems to be a rare case. Such rows can exist and the application still works in some way but some content of repeater fields is missing on pages. The problem occurs with repeater fields only. Even when i removed the Config Migrations files for these repeater fields these repeater fields have "doubles" in the template fields. When i switch of RockMigrations this behavior disappears. RockMigrations 6.5.0 / PW 3.0.227
-
Hi, i'am new with FrontendForms. I use v2.2.16. When i set custom classes it is ignored. When i define a framework as described i can select it in the settings and save the config. But nothing happens. The class names i see in the generated html are always the same generic names e.g. fieldwrapper etc. So, i can write by my own a class "fieldwrapper" and it works - sure. But this is - as far as i understood the documentation - not the intention of framework files: to be without effect. Where do i get it wrong? Or is there a bug? Thank you for your help