Jump to content

MarkE

Members
  • Posts

    931
  • Joined

  • Last visited

  • Days Won

    10

Posts posted by MarkE

  1. Having problems with laragon so decided to switch to ddev. Installed docker & ddev just fine. Then wanted to set it up with an existing project, rather than start from scratch.

    So what I did was:

    • change to the project directory
    • ddev config
    • ddev start
    • imported the database (ddev import-db ...) using a copy downloaded from the live site

    That all seemed to execute properly. Then I ran the url given by ddev and clearly the php was executed but the db could not be found:

    Quote

    Exception: SQLSTATE[HY000] [2002] No such file or directory (in wire/core/WireDatabasePDO.php line 509)

    #0 wire/core/WireDatabasePDO.php (509): PDO->__construct('mysql:dbname=hi...

    The dbname etc. were the details for the live site (although I was expecting it to look for the dev site details as the $config->paths->root had not changed) EDIT: under ddev it has changed, so I changed my test condition in config.php for the dev environment accordingly

    Unfortunately, since laragon is not working any more (not php8.0 compatible and unable to upgrade) I couldn't get into phpmyadmin and export the database there).

    Any ideas on how to hook everything back together, @bernhard, @heldercervantesor others?

    EDIT: I see ddev changes all the database details to 'db'. I changed all the name, users etc to match, but still no luck.

  2. 2 hours ago, bernhard said:

    I've built several apps based on Repeaters and I always got into trouble later. So I stick to the fundamentals, which are Pages rather than repeater items.

    That explains a lot! I have LOTS of repeaters in this new app as it seemed a really neat idea from the point of view of the admin gui. Then I ran into LOTS of problems with dependent page selectors, which I think I’ve now fixed, either by contributions to the core or by the new CustomDependSelects module. I have wondered whether it would have been wiser to use plain pages. RepeaterMatrix does seem to be very popular (and I can see why), particularly for a UI for web page blocks. Other contributions on the “repeaters or no” discussion would be helpful. 

    • Like 1
  3. 42 minutes ago, bernhard said:

    Where's that recommendation?

    Well, more of a suggestion than a recommendation 

    Quote

    Your custom classes can use PHP object inheritance, enabling you to maintain Page objects that build upon one another. For instance, your BlogPostPage class could extend your DefaultPage class rather than the Page class, and so on.

    in https://processwire.com/blog/posts/pw-3.0.152/#custom-page-classes-vs-hooks

    However, it struck me as a good idea since any custom methods for all pages can go in DefaultPage and will be available to any other custom page classes. If, for some reason, you don't want to include the method in a custom page class, you can either declare it in the subclass or (to exclude them all) extend Page. Much better than adding methods to Page via hooks, no? So my practice is always to include a DefaultPage class, even if I end up putting nothing in it.

    48 minutes ago, bernhard said:

    I'm not sure I understand the question. Do you have an example or use case?

    Absolutely, but it is a rather large app, so difficult to encapsulate. The context is the same as the worked example here (which I will repeat for ease of reading):

    Quote

    The context is my new web-based app for cider production and sales management "CiderMaster". Among other things, this has a template - 'Cider' - for recording all stages of production and use. The stages are recorded in a repeaterMatrix field called 'stage'. Two such stages are 'blend_from' and 'blend_to' to record the blending of (part of) one cider to another. 

    There is CiderPage.php containing: class CiderPage extends DefaultPage. This contains a lot of methods, but one simple one is:

    	/**
    	 * Get the stage sequence as it is actually stored in the page array - this may be different from the 'sort' property
    	 *
    	 * @param $stage
    	 * @return false|int|string
    	 */
    	public function getStageSequence($stage): bool|int|string {
    		$sort = array_search($stage->id, $this->stage->explode('id'));
    		return $sort;
    	}

    Ideally, I would have something like RepeaterStagePage class with method:

    	/**
    	 * Get the stage sequence as it is actually stored in the page array - this may be different from the 'sort' property
    	 *
    	 * @return false|int|string
    	 */
    	public function getStageSequence(): bool|int|string {
    		$sort = array_search($this->id, $this->getForPage()->stage->explode('id'));
    		return $sort;
    	}

    Admittedly, this is not any less code, but at least it would sit in the right place. For example, the first method would be less good if the stage field was used in a two different templates (which it is in fact, but the class of one -JuicePage - extends CiderPage, so it is not a problem in my case).

  4. That’s great @bernhard. I really think custom page hooks should be in the page class, not ready.php, which can get really messy. I have a couple of questions:

    1. why do you extend page rather than introduce an intermediate DefaultPage class as recommended by @ryan?
    2. how do you deal with pages that are repeaters (or repeaterMatrix)? Per a comment in the RepeaterMatrix forum, custom pages classes don’t work with these as they have their own classes which descend directly from Page and extending those classes is not advised. @ryansuggests using addHookMethod on RepeaterMatrixPage (or, presumably on RepeaterPage for plain repeaters), but that would place all the code in ready.php. The approach I have adopted is to put the methods in the page class of the getForPage with an argument for the repeater page object. Any suggestions?
  5. On 1/8/2022 at 4:11 PM, bernhard said:

    That makes it possible to attach all kinds of hooks that belong to the custom page class where they belong: Into the page classe's php file

    This looks really useful. For some reason I missed it at the time, but I thought I saw a tutorial covering it within the context of custom page classes generally - which I now can't find. Is there one @bernhard?

  6. On 9/8/2022 at 8:33 PM, Ivan Gretsky said:

    Could you please help me here and outline pros and cons of your module comparing to @kongondo's ?

    I have used both and now just use this one on new projects. They both work fine in normal circumstances (and are easy enough to just temporarily install and play with), but @Robin S's is simpler with less to go wrong - I had a few problems with @kongondo's in a multi-instance environment (which I think are fixed). On the other hand @kongondo's has a slightly more feature-rich GUI. You could always build your own simple module rather than just use hooks if you want more than the basic RuntimeOnly and want control over the code - I forked this module to include a custom Fieldtype in my ProcessDbMigrate module. As @bernhard says, it all depends what you are trying to do.

    • Like 3
  7. Hi @bernhard. Had a quick look at this. It's a bit of a puzzle to me. The problem occurs when clicking the "Get Module Info" button on the new modules tab.

    I tried on 3.0.200 and 3.0.203

    In 3.0.203, all I get is a compile error - see the title of this thread - so it's a bit difficult to diagnose as there is no obvious cause. This happens with RockCalculator and RockMigrations, but NOT RockFrontend. (Although I have not yet proceeded to install RockFrontend I have no reason to believe it won't install).

    In 3.0.200 I get the "ProcessWire: ProcessModule: No page specified" error. This is generated by ProcessPageEditImageSelect::init() (line 292 or 294). I have no idea why PW is even in that class. See the trace below

    1424604609_RockMigrationsdownloadprob.thumb.jpg.89db291d4ed1d440e2461ee0ca064c7d.jpg

    Again, this happens with RockCalculator and RockMigrations, but not RockFrontend.

    I inserted a debug_backtrace - see here (unfortunately lost indents on paste from Tracy), which explains more:

    3 => array
    'file' => '.../admin/wire/core/Modules.php'
    'line' => 1711
    'function' => 'get'
    'class' => 'ProcessWire\Modules'
    'object' => ProcessWire\Modules
    count: 131
    items: array
    'type' => '->'
    'args' => array
    0 => 'ProcessPageEditImageSelect'
    4 => array
    'file' => '.../admin/wire/core/WireArray.php'
    'line' => 1763
    'function' => 'find'
    'class' => 'ProcessWire\Modules'
    'object' => ProcessWire\Modules
    count: 131
    items: array
    'type' => '->'
    'args' => array
    0 => 'installs='
    5 => array
    'file' => '.../admin/wire/core/WireArray.php'
    'line' => 582
    'function' => 'findOne'
    'class' => 'ProcessWire\WireArray'
    'object' => ProcessWire\Modules
    count: 131
    items: array
    'type' => '->'
    'args' => array
    0 => 'installs='
    6 => array
    'file' => '.../admin/wire/core/Modules.php'
    'line' => 1911
    'function' => 'get'
    'class' => 'ProcessWire\WireArray'
    'object' => ProcessWire\Modules
    count: 131
    items: array
    'type' => '->'
    'args' => array
    0 => 'installs='
    7 => array
    'file' => '.../admin/wire/modules/Process/ProcessModule/ProcessModule.module'
    'line' => 1154
    'function' => 'isInstalled'
    'class' => 'ProcessWire\Modules'
    'object' => ProcessWire\Modules
    count: 131
    items: array
    'type' => '->'
    'args' => array
    0 => 'installs='
    8 => array
    'file' => '.../admin/wire/core/Wire.php'
    'line' => 420
    'function' => '___buildDownloadConfirmForm'
    'class' => 'ProcessWire\ProcessModule'
    'object' => ProcessWire\ProcessModule #359
    'type' => '->'
    'args' => array
    0 => array
    'status' => 'success'
    'id' => 2236
    'class_name' => 'RockMigrations'
    'name' => 'rock-migrations-1'
    'title' => 'RockMigrations'
    'summary' => 'The Ultimate Automation and Deployment-Tool for ProcessWire'
    'version' => 121
    'module_version' => '1.2.1'
    'created' => 1661782531
    'modified' => 1662318506
    'refreshed' => 1662616138
    'url' => 'https://processwire.com/modules/rock-migrations-1/'
    'details_url' => 'https://modules.processwire.com/export-json/rock-migrations-1'
    'requires' => array
    0 => ''
    1 => 'installs='
    'requires_versions' => array
    '' => array
    'installs=' => array
    'installs' => array
    0 => ''
    'author' => 'bernhard'
    'icon' => ''
    'likes' => 1
    'release_state' => array
    'id' => 1083
    'name' => 'stable'
    'title' => 'Stable'
    'url' => 'https://modules.processwire.com/release-states/stable/'
    'pw_versions' => array
    0 => array
    'categories' => array
    0 => array
    1 => array
    'authors' => array
    0 => array
    'authors_other' => ''
    'project_url' => 'https://github.com/baumrock/RockMigrations'
    'download_url' => 'https://github.com/baumrock/RockMigrations/archive/main.zip'
    'forum_url' => 'https://processwire.com/talk/topic/27504-rockmigrations-'
    'author_id' => 2865
    1 => false

     

    • Thanks 1
  8. Thanks @bernhard. That's helpful but doesn't work as $f->hasField is null because magnitude is a property of a measurement object field, not a field itself. So I tried the following code - creating a dummy field to hold the rockcalculator property: 

    			$f = $this->modules->get("InputfieldText");
    			if(wire()->modules->isInstalled('RockCalculator')) {
    				$f->attr("data-rockcalculator", 6); //6 digit precision
    				$f->notes = __("Numeric or math formula");
    				$dummyField = wire()->fields->makeItem(['name' => 'dummyName']);
    				$f->hasField = $dummyField;
    				$f->hasField->rockcalculator = true;
    				bd($f, '$f in rockcalc');
    			} else {
    				$f->notes = __("Numeric");
    				$f->precision = 6;
    				$f->attr('type', "number");
    				$f->attr('step', "any");
    			}

    That almost works, in the sense that isEnabled() is true and loadAssets() runs. However the dev tools do not show the js as loaded and the field doesn't work.

    I've got round the problem by including the following in the init() of my fieldtype module:

    		if(wire()->modules->isInstalled('RockCalculator')) {
    			$rc = $this->wire->modules->get('RockCalculator');
    			$this->wire->config->scripts->add($rc->m('lib/math.min.js'));
    			$this->wire->config->scripts->add($rc->m('lib/tooltip.js'));
    			$this->wire->config->scripts->add($rc->m($rc->className . '.js'));
    			$this->wire->config->styles->add($rc->m($rc->className . '.css'));
    		}

    Maybe there is a better way, but at least this seems to work.

    And your module certainly enhances mine ☺️

  9. This looks a handy little module. I'm hoping to add it as an enhancement to my FieldtypeMeasurement module, which it would complement very nicely. However, I'm scratching my head a bit as to the best way of incorporating it as I really need to embed it within my Inputfield::render() method  - i.e. after the hook in your module has fired (?), but I think that's too late.

    The problem is that my field is an object. The magnitude property (which is where I'd like the calculator) is rendered as a text inputfield. So what I was hoping to do was something like this (where $f will be the inputfield for the magnitude): 

    $f = $this->modules->get("InputfieldText");
    			if(wire()->modules->isInstalled('RockCalculator')) {
    				// set the rockcalculator here //
    				$f->attr("data-rockcalculator", 6); //6 digit precision
    				$f->notes = __("Numeric or math formula");
    			} else {
    				$f->notes = __("Numeric");
    				$f->precision = 6;
    				$f->attr('type', "number");
    				$f->attr('step', "any");
    			}

    However, that doesn't work because the assets aren't loaded. It works OK if I remove the line

    if(!$this->isEnabled($inputfield)) return;

    from loadAssets(), but obviously if the module is used elsewhere in the site, that is not a great idea and isEnabled() is not hookable. I could just load the assets separately, I guess. Any suggestions@bernhard

    • Like 1
  10. TLDR: Use custom selectors in page field selector: 

    - check_access=role1|role2 ... - to control who can see results
    - field=[item.id] - to select on id of repeater item containing the page field.

    ------------

    This module extends the capabilities of selectors specified in the 'input' tab of a page reference field, specifically when that field is part of a set of dependent selectors which may be inside a repeater item. The readme also attempts to bring together various existing documentation regarding the use of dependent page selectors and the enhancements that were provided by various PW versions.

    See https://github.com/MetaTunes/CustomDependSelects for full readme and to download. Please note that this is an initial alpha release. Please test in your own context thoroughly before using. Also note that PW3.0.200 is required and 3.0.203 is preferred.

    • Like 6
    • Thanks 1
  11. Thanks @AndZyk. I am using dependent selects. So it needs check_access=0 in the input tab as you suggest for InputfieldPage to get it. However for dependent selects, ProcessPageSearch is called to populate the options and strips out the 'check_access'. Then, when the options don't include the already-selected item provided by InputfieldPage, it is removed ?

    I think perhaps I should raise this as an issue or feature request.

    • Like 2
  12. Thanks @AndZyk. I had tried check_access, but it didn't work. I found out why - because ProcessPageSearch contains the following:

    			// don't allow setting of check_access property, except for superuser
    			if($lowerName == 'check_access' && !$superuser) continue; 

    So I sort-of hacked it with a hook:

    wire()->addHookAfter('ProcessPageSearch::findReady', function(HookEvent $event) {
    	$selector = $event->return;
    	$user = wire()->users->getCurrentUser();
    	if($user->hasRole('webmaster')) $selector = 'check_access=0, ' . $selector;
    	$event->return = $selector;
    });

    which works, but I'm not very happy with it!

    • Like 1
  13. 28 minutes ago, bernhard said:

    Still I don't get how you are actually working with the module. And that was what I was asking for. To understand and maybe find a way to get the best of both worlds.

    Thanks @bernhardfor your detailed response. The best of both worlds would be nice if it makes sense. First I'll do a few more improvements to my module and then try and respond to the request for a working example, before considering the way forward from there.

  14. I have a number of pages which host dependent select fields. The contents of these are drawn from various repeater fields (actually repeaterMatrix, but I don't think that's relevant to the problem) - i.e. they are provided by a selector which is something like "template=repeater_field_name, ...". They all work fine in superuser, but in any non-superuser role, they return an empty result, even if the role has the maximum permissions. It seems like every repeater template needs to specifically permit access to the required roles, because they are all children of 'admin'.

    Is this so? Is there a shortcut way of doing this (there are a lot of such fields)?

    Edit: I guess what I would like to do is to set the access to the repeater page to be the same its host page (getForPage) rather than admin. Any ideas?

  15. 8 hours ago, bernhard said:

    Could you please describe exactly what you mean here?

    In case you hadn't realised, there is extensive documentation of the module at https://metatunes.github.io/DbMigrate/help.html. So you don't need to install and use the module to get an understanding of what it does. Specifically as regards running code on migrations, see the following sections: https://metatunes.github.io/DbMigrate/help.html#snippets and  https://metatunes.github.io/DbMigrate/help.html#hooks.

    I'm happy to supply a full soup-to-nuts example if it helps, but that will take a little time. Perhaps reading the help file and reviewing this (completed) example might be explanantion enough. (Note that this example does not include ready.php code for uninstalling as I had fully tested everything and was confident I didn't need to write it. If for some reason it was needed, I could have modified ready.php to hook before and after uninstallMigration() in a similar manner).

    215086327_Migrationwithcode.thumb.jpg.854a5343be8064777edc1859580995c2.jpg

    Let me know if you want a fuller example; also if anything in the help file is not clear as I'll gladly improve it.

    • Confused 1
  16. The new version looks good @bernhard and I am very tempted to give it a proper try. However, I have been using my own ProcessDbMigrate module successfully on a number of projects and it is serving me pretty well (and the change in approach would be quite radical). Although the two are quite different in methodology, there are some similarities in that my json files have similarities to your yaml files (btw, not quite sure what advantages yaml has over json in this context - json has coped so far).

    My module is still very much alpha as something this complex does need extensive testing and bugs do crop up (as well as the need to extend to handle additional field types etc. - the recent version 0.1.5 has been updated to include FieldtypeRepeaterMatrix). Sometimes I think about re-engineering it to use RockMigrations methods rather than the current native ones, but that does lead to some head-scratching.

    For example, my module will handle uninstallation of migrations (provided they have not been conflicted by subsequent changes) - I don't see how RockMigrations does this (reverting the code would leave the database unchanged?).

    Also I can attach hooks to run on installation (and uninstallation) to handle any data changes related to the migrations (e.g. say field 'name' is split into 'first_name' and 'last_name', the code can populate the new fields when the installation is run). I assume that RockMigrations can handle that too (at least for the forward install), but not quite sure how.

    I know you have commented in the past that you don't like my approach (although I'm not sure of the precise reasons**) but I think it is a valid alternative and I would like to somehow get the best of both worlds.

    ** I can see that using Git to handle conflicts has some advantages. My module has to include its own conflict management which works but has not been tested in a multi-developer environment. Mind you, I can envisage semantic conflicts that might arise which Git would not spot - separate modules changing the same field for example.

    • Like 3
×
×
  • Create New...