Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/02/2022 in all areas

  1. This week on the core dev branch we’ve got some major refactoring in the Page class. Unless I broke anything in the process, it should be more efficient and use less memory than before. But a few of useful new methods for getting page fields were added in the process. (A couple of these were also to answer feature request #453). Dot syntax You may have heard of dot-syntax for getting page fields, such as $pages->get('parent.title') where “parent” can be any field and “title” can be any field that has subfields. This is something that ProcessWire has supported for a long time, but it doesn’t get used much because it was disabled when output formatting was on. So it wasn’t something you could really count on always being there. Now you can — it is enabled all of the time. But it’s also been rewritten to be more powerful. When using dot syntax with a multi-value field (i.e. any kind of WireArray value) you can also specify field_name.first to get just the first value or field_name.last to get just the last value. i.e. $page->get('categories.first'); will take a value that was going to be a PageArray (‘categories’) and return just the first Page from it. Bracket syntax With bracket syntax you can call $page->get('field_name[]') with the (‘[]’ brackets at the end) and it will always return the appropriate array value for the type, whether a PageArray, WireArray, Pagefiles/Pageimages, or regular PHP array, etc. This is useful in cases where you know you’ll want a value you can foreach/iterate. Maybe you’ve got a Page field that set set to contain just 1 page, or maybe you’ve got a File/Image field set to contain just 1 file. But you want some way to treat all of your page or file/image fields the same, just append “[]” to the field name in your $page->get() call and you’ll always get an array-type value, regardless of the field settings. This bracket syntax can also be used for getting 1 value by index number. Let’s say you’ve got a page field named “categories” that contains multiple pages. If you want to get just the first, you can just call $page->get('categories[0]'); If you want to get the second, you can do $page->get('categories[1]'); This works whether the field is set to contain just one value or many values. Using the first index [0] is a good way to ensure you get 1 item when you may not know whether the field is a single-value or multi-value field. Another thing you can do with the bracket syntax is put a selector in it to filter a multi-value field right in the $page->get() call. Let’s say you want all categories that have the word “design” in the name. You can call $page->get('categories[title%=design]'); If you want just the first, then $page->get('categories[title%=design][0]'); What’s useful about using selectors in brackets is that this does a filter at the database-level rather than loading the entire ‘categories’ field in memory and then filtering it. Meaning it's a lot more memory efficient than doing a $page->get('categories')->find('title%=design'); In this way, it’s similar to the already-supported option to use a field name as a method call, for instance ProcessWire supports $page->field_name('selector'); to achieve a similar result. Dot syntax and bracket syntax together You can use all of these features together. Here’s a few examples from the updated $page->get() phpdocs: // get value guaranteed to be iterable (array, WireArray, or derived) $images = $page->get('image[]'); // Pageimages $categories = $page->get('category[]'); // PageArray // get item by position/index, returns 1 item whether field is single or multi value $file = $page->get('files[0]'); // get first file (or null if files is empty) $file = $page->get('files.first'); // same as above $file = $page->get('files.last'); // get last file $file = $page->get('files[1]'); // get 2nd file (or null if there isn't one) // get titles from Page reference field categories in an array $titles = $page->get('categories.title'); // array of titles $title = $page->get('categories[0].title'); // string of just first title // you can also use a selector in [brackets] for a filtered value // example: get categories with titles matching text 'design' $categories = $page->get('categories[title%=design]'); // PageArray $category = $page->get('categories[title%=design][0]'); // Page or null $titles = $page->get('categories[title%=design].title'); // array of strings $title = $page->get('categories[title%=design].title[0]'); // string or null // remember curly brackets? You can use dot syntax in there too… echo $page->get('Page “{title}” has {categories.count} total categories'); I’m not going to bump the version number this week because a lot of code was updated or added and I’d like to test it for another week before bumping the version number (since it triggers the upgrades module to notify people). But if you decide to upgrade now, please let me know how it works for you or if you run into any issues. Thanks for reading, have a great weekend!
    11 points
  2. I couldn't find anything in the documentation, but in the core file it seems that comments starting with <!--# don't get stripped out: https://github.com/processwire/processwire/blob/3acd7709c1cfc1817579db00c2f608235bdfb1e7/wire/core/WireMarkupRegions.php#L844 <!--# Comment --> I tried it and it seems to work. Regards, Andreas
    3 points
  3. Hello, where is this option, I somehow can't find it? Thank you. ? Forget it I found it, thx ?
    2 points
  4. What I would do in your scenario is: 1) Catch any \Exception and return a valid JSON with for example: $result = ['success' => false, 'error_msg' => 'Description of the error']; and log the exception original message in your logs 2) To "catch" a notice or warning, do that from a script that analyze a custom Apache ErrorLog and send you a mail on a pattern you would like to receive the mails. Just do not show "errors, mean warning, notice..." but log them into a custom Apache ErrorLog. 3) <- should be the first step, write Test Cases, test your code, and more over, do not upgrade a production code directly, as the most notice and warning you will get once your code is working is from deprecated code (PHP version, ProcessWire, Modules..). And do not forget that you can handle many scenario, eg., you can catch WireException, CustomException, \Exception. You could read some Ryan's code by opening files that are in the wire\core folder to get some example.
    2 points
  5. I will take the time to answer your question a bit later @bernhard with some real example I use at work. But to get a general idea, when you go on the AppStore or GoolePlay, every app you see need to "discuss" with an (generally "Rest") API. Obviously there are many constraints to take into account when choosing the backend that will provide the API. For example, at work, I have servers that have to support quite heavy loads and that are written in Pascal. You can take a look there: https://synopse.info/files/html/Synopse mORMot Framework SAD 1.18.html#SOURCE (⚠️ it can hurt your head for the day ?) and read the general purpose and concept. I also have three ProcessWire backends (that need to be merged) that serve more user-oriented needs, such as apps delivered to customers or our technical maintenance group.
    2 points
  6. 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.
    2 points
  7. Page Table was developed/intended as a part of the ProFields bundle, but later bundled with the core package, so "ProFields" in the name is kind of a nod towards that history ? There are many ways to handle rendering, but here's one solution that may help you: Create a shared root for your content elements — something like /blocks/ or /content-elements/, perhaps. Configure your Page Table field to save new items below aforementioned root path, and make sure that you only enable templates that you've created for this purpose. You don't want to allow a "regular basic-page" (or anything like that) in your Page Table field as a "content element". For each of your "content element templates", leave template file empty (so that these pages can't be reached with their actual URL). Now even though your content elements will be actual pages in the page tree, they won't be reachable by visitors. You can render them in any way you see fit, but here's one approach using ProcessWire's native field rendering feature that, in my opinion, works pretty well. Here I've assumed that the Page Table field is called "blocks", and it's used on template called "basic-page". /site/templates/basic-page.php <?php namespace ProcessWire; ?> <?= $page->render('blocks') ?> /site/templates/fields/blocks.php <?php namespace ProcessWire; ?> <?php foreach ($value as $item): ?> <div class="block"> <?= $item->render(__DIR__ . '/blocks/' . $item->template . '.php') ?> </div> <?php endforeach; ?> /site/templates/fields/blocks/block_template_name.php <?php namespace ProcessWire; ?> <h2> <?= $page->title ?> </h2> <?= $page->content ?> Anyway, as I mentioned that's just one approach ? I'm not entirely sure what you're referring to here, but if you mean the processwire.com website, then yes — that's technically proprietary. In my experience that's a common thing for open source projects. API reference, on the other hand, is automatically generated from ProcessWire's source code, so that is technically open source ?
    2 points
  8. @Roych You can clear the copy/paste memory in the select options, but yeah I think the memory could clear itself after for example loggin out. I think no one needs something forever in the memory. ?
    2 points
  9. I have just released version 2 of RockMigrations: GitHub: https://github.com/baumrock/RockMigrations Modules Directory: https://processwire.com/modules/rock-migrations/ Please star the module on GitHub if you like it ? Are you unsure if RockMigrations is the right tool for you? @Jonathan Lahijani in a very nice PM ? Read the full post here Read the full post here Read the full post here QuickStart The example code uses bd() calls for dumping data. You need TracyDebugger installed! Put this in your site/migrate.php /** @var RockMigrations $rm */ $rm = $modules->get("RockMigrations"); bd('Create field + template via RM'); $rm->createField('demo', 'text', [ 'label' => 'My demo field', 'tags' => 'RMDemo', ]); $rm->createTemplate('demo', [ 'fields' => [ 'title', 'demo', ], 'tags' => 'RMDemo', ]); Reload your site and you will see the new field and template in the backend and you'll see the message in the tracy debug bar.
    1 point
  10. What would really be great is a tutorial on how to set up a project in DDEV, something I'm trying to figure out right now ? Edit: Spoke too soon. This is so easy it sounds like a lie. `ddev config` - Choose PHP project `git clone https://github.com/processwire/processwire.git web` - Downloads PW into the 'web' directory `ddev launch` - Go through the config, enter db, db, db, db in the database step Done
    1 point
  11. Could any of the moderators please unpin this thread and also the captain hook one? The links are all 404 and I don't think that those projects still exist? At least I have not used them for years...
    1 point
  12. Hello AndZyk, thanks for stepping in on this, and going to start playing with MarkUpRegions with it
    1 point
  13. Hi @AndZyk, good to know, thanks for your help. I do not need the old log file, it was only important to me to have the log file in the future.
    1 point
  14. Hello @kaz, the errors.txt should be recreated on the next fatal error. As for restoring the previous errors.txt you should look if your webhoster or local machine has a backup for this file. I hope that helps you. Regards, Andreas
    1 point
  15. Hey @MarkE thx for your message! I'll answer in detail soon. Could you please describe exactly what you mean here? Best with a specific example from the very beginning until the very end. For someone that is not really familiar with your module please ? What do you do in code, what do you do in the GUI? Where? When? Why?... Thx
    1 point
  16. I am on mobile, so short answer. You can define your own error handler with `set_error_handler`. (Do not forget to restore the default handler, check the php doc.) Example: function send_email_on_notice() { … } set_error_handler("send_email_on_notice", E_NOTICE); […] restore_error_handler() Consider testing the behavior of it if you use it on a try/catch block.
    1 point
  17. I've just implemented a new way of how watching files work and bumped the version to 1.0.0 As from version 1.0.0 the module will not run ALL migrations any more but only the ones that either have the "force" property set or that have changed since the last migrations run. I'm working on a site with lots of RockMatrix blocks and all blocks have their own migrate() method. Using RockFrontend I get livereload as soon as I change a watched file, but then the reload took over 9 seconds because I had so many migrations going on. Now the new version does only migrate changed files and forced files, which brings down the duration for the reload to around 1 second ? When using migrate() from the CLI it will still trigger all migrations, even for unchanged files. That update will especially be interesting for you @dotnetic I guess? ? This is how the log looks like (HomePage was changed and RockMatrix is forced, so these two files are the only ones being triggered):
    1 point
  18. Hey @Zeka! I assume you're referring to "method aliases" from MethodPropsTrait? If so, this is a feature that, to be honest, I've not had much use for myself. As far as I can remember, the original intention was two-fold: 1) Whether to make code cleaner or to mitigate issues resulting from partial rewrites, one can create an alias or "virtual method" that calls another local method behind the scenes. Say, if a controller class had method called "words", but later it was decided that it should be "terms" instead (or for some reason front-end code requires it to be that) yet changing it now could potentially cause other issues, one could handle this via an alias: class DictonaryController extends \Wireframe\Controller { protected $method_aliases = [ 'terms' => [ 'callable' => ['self', 'words'], 'params' => [], ], ]; public function words(): array { return $this->pages->findRaw('parent=' . $this->page->id . ', template=Word', [ 'name', 'title', 'dictionary_category.title', ], [ 'objects' => true, 'entities' => true, ]); } } Here I'm defining the $method_aliases property directly on class declaration, but in some cases it could make more sense to define individual aliases via the API: $this->setAlias($alias, $callable, $params). 2) Another use case is referring to a method defined somewhere else — another controller, utility class, third party library, etc. Essentially this makes it easy to inject new methods or properties into classes implementing the MethodPropsTrait (such as controllers and components): class DictonaryController extends \Wireframe\Controller { public function init() { $this->setAlias('terms', '\Wireframe\Lib\Utils::getWords', ['page' => $this->page]); } } You can do the same with ProcessWire hooks or just have a method that calls another method, so this is largely a matter of preference ? --- As I mentioned above, I've not had much use for this feature myself. It's been there since the very beginning, and it's a concept that seemed potentially useful (not to mention fun) while I was working on the initial implementation of Wireframe, but real world use cases have been few and far between. If you can think of other use cases, let me know ?
    1 point
  19. Yes, in a file with namespace ProcessWire that will let you use it: $foo = new \stdClass(); You can also put “use \stdClass” at the top if you need it a lot. The same goes for all the other stuff outside PW’s namespace. Often you’ll need \DateTime or \IntlDateFormatter for example. Edit: the technical term is “The Global Space”: https://www.php.net/manual/en/language.namespaces.global.php
    1 point
  20. Hello, I found & fixed an issue wich gives you an illegal string offset error in the latest PHP version. (7.1). Around line 429 you declare $return = ''; and then fill it up as an array doing $return[$key] = $value; at line 435. All you got to do is declare $return = array(); instead of $return = ''; and it should be fixed. Filling up an empty string as an array caused the errors. Can you fix this and push it to github? Kind regards, Jonathan private function parseCustom($custom) { if(trim($custom) == '') return; $return = array(); $lines = explode(PHP_EOL, $custom); foreach($lines as $line) { list($key, $value) = explode(':=', $line); $key = preg_replace('%[^A-Za-z0-9\-\.\:\_]+%', '', str_replace(' ', '-', trim($key))); $value = trim(wire('sanitizer')->text(html_entity_decode($value))); $return[$key] = $value; } return $return; }
    1 point
  21. would be awesome if core natively supported dependent selects in repeaters!
    1 point
  22. 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). 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.
    0 points
×
×
  • Create New...