Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 02/01/2025 in all areas

  1. Thank you @Juergen You accidentaly placed the old Hooking example 2 on the docs which now the font awesome icon displayed always even if there are no errors. Replace it with the above hook which works properly.
    2 points
  2. I have updated FrontendForms to support hooks to customize the markup of certain elements. As written in a the blog post above there was a problem of using hooks, because they never fired. This should be solved now. You will find a working example on this website. If you press submit without filling out the form, all error messages will get an exclamation sign in front of the error message. This is done via the following hook inside the site/init.php: $wire->addHookAfter('Errormessage::render', function(HookEvent $event) { $msg = $event->object; $errorText = $msg->getText(); if($errorText){ $fontAwesome = '⚠ '; $event->return = $fontAwesome.$errorText; } }); This is for demonstration puropose that it works now! In addition I have added some other hook examples inside the Hooking section of the docs. This bug fix includes the change of really a lot of files of FrontendForms, so please that care that everything works after the update as expected. Make a backup before you update the module!!! Best regards
    2 points
  3. I have to leave town later today for for my daughters gymnastics meet, so I'm getting the weekly post out early this week. I bumped the dev branch core version to 3.0.245 as well. While there isn't anything major in this particular core version, it does contain some updates to the ProcessPageList module that were needed to make in order to make it possible to build the new PageListFilter module (the one I mentioned last week). These updates focus on making some parts of the PageList more hookable. To go along with those core updates, I've released the PageListFilter module that I mentioned last week. This is an admin helper that enables you to filter pages in the page list with a click, such as first letter (A-Z, etc.). I've already found it quite handy on some of the ProcessWire installations I work with, I hope some of you find it useful too, please let me know what you think. Thanks and have a great rest of the week and weekend!
    2 points
  4. View website: ID Studio Web Agency We have been working on the ID Studio website for quiet some time using ProcessWire extensively for ourseleves and 90% of all our clients. This post will highlight some features we have implmented and also show off some of the hidden functionality. A quick overview is as follows: Custom web design of course 🙂 Front-end uses Canvas and Three.JS The core objective for us is to get users engaged, reviewing the showcase and services, then getting in touch The showcase and blog have alot of content We hide the ID Lab and About section in the footer but there if folks want to dive in and have the time Development features include: We use the form builder system with some custom modifications 3D tools and management Linking 3D elements to HTML elements Repeater matrix for content panels and lots more, best way is to see it on the video overview below ID-Overview.mp4
    1 point
  5. A general approach: The client doesn't do any optimising but just inserts images into TinyMCE. If there are different styles/positioning of images they apply these via classes on the image. You use a textformatter module that finds the images within the field, and if the image corresponds to a Pageimage (as opposed to some external image that you don't have control over) you do whatever resizing and optimising is needed and replace the original <img> markup with the markup you want (srcset or whatever). You can check the class on the original image and output different markup accordingly. A proof of concept module that could be a starting point: https://processwire.com/modules/textformatter-process-images/
    1 point
  6. Hello @Andy I have added your rule as the first community provided validation rule to FrontendForms. Take a look here. This validation rule is included in the next update, but if you do not want to wait until the next update, you only have to replace the code of the CustomRules.php from Github with yours and you are done.
    1 point
  7. Hello @Andy Glad that it works like you want!! For this check, it is not really necessary to write a custom rule. You can also use the regex validation rule and add the regex there, but it is not wrong if you need this validation rule often.😉
    1 point
  8. Thank you @PWaddict I have corrected in inside the docs.
    1 point
  9. Hi @Juergen Good news everybody. Custom validation rules work. I'll add one more proven validation rule to the general collection. Check to enter a Cyrillic first, last or middle name. In ready.php // // Cyrillic name may contain lowercase and uppercase а-я and hyphen // \Valitron\Validator::addRule('cyrillicName', function ($field, $value) { $regex = '/[^а-яё\-]/iu'; if (!preg_match($regex, $value)) { return true; } return false; },'contains not allowed characters. Cyrillic name may contain lowercase and uppercase а-я and hyphen (no whitespaces).');
    1 point
  10. Hello @Andy I will write a new section in the docs in the next time about this possibility. Please note: If you want to use API variables inside your custom validation rule I guess you need to add this rule to the site/ready.php instead of the site/init.php. I have not tested it, but I guess you need to do it this way, but you can try it out. That is a good idea and if a validation rule seems to be useful for others I will add it to the FrontendForms validation rules file, so everyone can use it. I guess there are around 60 validation rules available at the moment, but more is more in this case. 😉
    1 point
  11. Hi @Juergen I continue to admire you. Yes, that's exactly what I meant. That's what I was thinking about writing a new validation rule, just haven't decided how to do it yet. I was going to use hooks. You've shown a logical solution. Thank you. I think FrontendForms users can share their useful validation rules in this thread.
    1 point
  12. Good news @Andy I have taken a look at the Valitron library, which I use to validate form fields and this library offers an easy to use solution to add custom rules. Take a look here. To add a custom rule to ProcessWire you need to add it to the site/init.php. For demonstration puroses I use the example from the Valitron docs. So please add this code (which always returns false) to your site/init.php \Valitron\Validator::addRule('alwaysFail', function($field, $value, array $params, array $fields) { // here you can write your validation code - this should return true or false return false; }, 'Everything you enter is not correct. You fail.'); Now you have added a new custom rule, which you can use sitewide like all other validation rules. Here is a form example containing this rule: $form = new \FrontendForms\Form('test'); $inputText = new \FrontendForms\InputText('text'); $inputText->setLabel('Input Text'); $inputText->setRule('required'); $inputText->setRule('alwaysFail'); // here is the new validation rule $form->add($inputText); $button = new \FrontendForms\Button('submit'); $button->setAttribute('value', 'Send'); $form->add($button); if ($form->isValid()) { } echo $form->render(); If you validate the form, you will always get the error message "Everything you enter is not correct. You fail." because this rule always returns false. You can take this example as a starting point for your own validation rule. The validation code must return a boolean value (true or false). Hope this helps
    1 point
  13. Hello @Andy I will take your issue as an idea for a new feature and I will take a look if I could find a good working solution. At the moment I will wait if there are some unwanted negative side effects concerning the last update. If everything works as expected and no issues occur/will be reported I will go on to the next step and try to find a solution to your issue. Best regards
    1 point
  14. The form will be always submitted after pressing the send button 😉 You have the choice, what should be done inside the isValid() function. This method returns always true if the "standard validation does not fail". It is up to you to make another validation inside the isValid() method, but there is no possibility to make isValid to false manually. As I explained in the example some post before, you can do addtional checks inside the isValid() method. Depending on the results of your custom checks you can send fe an email or not. The only problem could be that the form will not be displayed after isValid() is true. The code is not designed to handle such special cases, sorry!
    1 point
  15. I hope you all have had a great week. Last week was the blog post for our newest main/master version 3.0.244. This week I've been catching up with some other projects, so no new core updates to report. But one thing I've been working on (and am still working on) is a module that lets you provide filters in the admin page list. In my case, a client wants to be able to filter by the first letter of page titles, so they can quickly jump to all pages that start with the letter "C", for example. It figures out all the starting first-characters for page titles and builds a kind of pagination-style list for it, like seen in the screenshot below. Clicking any of the single character filters to just those pages by sending an Ajax request to the server, grabbing just the relevant pages and listing them. I think it's pretty useful in many cases. And I think there's potential for predefined filters to go beyond just letters. There's more to work out with this, but I hope to release it in the near future. Thanks for reading and have a great weekend!
    1 point
  16. I didn't know where the right place to share this was on the forums, so I'll post it here since it may be helpful to those getting started with ProcessWire hooks, or some experienced ProcessWire developers who might find them useful. Either way, dear reader, If someone already wrote it, why write it again? If you're someone with experience, feedback is welcome! If there are better or more efficient ways to do something, I would love being a student. Some of these may either address challenges that others have experienced as well or were inspired by the awesome community sharing their solutions. Kudos to all of the people out there helping all of us. If someone sees something that was solved elsewhere please share it in the comments to give credit. I have to make a disclaimer- these have worked for me and while most of them are ready to copy/paste, a few of them are going to need customization or tweaking to make them work for your use case. I don't currently have the resources (time) to provide a lot of support. Some of these were slightly rewritten or adapted for the examples. If you run into issues, the best thing to do is research the solution so that you know exactly what is happening in your application. If you adapt something, fix a bug, or address an edge case, it would be great if you can come back and share that. Be smart, if you're going to run hooks that modify or create data, run a DB backup first. This is the part where I say "I'm not responsible if your site blows up". I don't think that's possible, but do the right thing. There are dozens of hooks in the project I am sharing these from, and to manage that I created a file structure to handle this because there were far too many to put in one file and keeping the init.php and ready.php files clean really makes a huge difference in maintainability. Being able to jump between files by filename is a supremely efficient way to work as well. The filenames don't matter, they're there to identify the files and make it easy to locate/switch between them. Here's my approach to directory organization: /site - hooks -- HookUtils -- init -- lazy_cron -- ready - init.php - ready.php The ready.php file contents: <?php namespace ProcessWire; if(!defined("PROCESSWIRE")) die(); /** @var ProcessWire $wire */ // Import all ready hooks foreach (glob(__DIR__ . '/hooks/ready/*.php') as $hook) { require_once $hook; } The init.php file contents: <?php namespace ProcessWire; if(!defined("PROCESSWIRE")) die(); /** @var ProcessWire $wire */ // Import all init hooks foreach (glob(__DIR__ . '/hooks/init/*.php') as $hook) { require_once $hook; } // Import all LazyCron hooks, import after init hooks as there may be dependencies foreach (glob(__DIR__ . '/hooks/lazy_cron/*.php') as $hook) { require_once $hook; } Operational Hooks Here are some favorites. Sort items in a Repeater matrix when a page is saved This one helped sort RM items by a date subfield to help the user experience when editing pages. This implementation is configured to only fire on a specific template but can be modified to fire everywhere if modified to check that a field exists on the page being saved first. This was adapted from an answer here in the PW forum but can't find the original post, so I'm going to include it. If you're having issues getting items to sort the way you want, check out this post about natural sorting, which also works elsewhere in ProcessWire. Github Gist Automatically add a new child page when a page with a specific template is created This automatically creates a new child page and saves it when a page having a specific template is created. This also has the ability to show a message to the user in the admin when new page(s) have been created by this hook. It is also error safe by catching any potential exceptions which will show an informative error to the admin user and log the exception message. The messaging/logging operation is abstracted to a separate object to allow reuse if creating multiple pages. Github Gist Conditionally show the user a message while editing a page This one shows a message on a page with a specific template under specific conditions. May be page status, field value, type of user, etc. Visual feedback when editing complex pages can be very helpful, especially when an operation may or may not take place depending on factors like the values of multiple fields. This can reduce the amount of explanations needed on each field or training required for users to use a ProcessWire application. In my case, a message is shown if the state of a page indicates that another operation that is triggered by other hooks will or will not run, which is something that the user doesn't directly trigger or may not be aware of. Github Gist Show the user a message when viewing the page tree This is intended to display a message, warning, or error when the page tree is viewed, such as on login, but in this case executes any time the main page tree is viewed to provide consistent communication and awareness. In my case it displays if there is an activity page located under an "Uncategorized" page for an event. This is something that may be buried in the page hierarchy and not noticeable, but if an activity isn't categorized, then is isn't visible on the website, and if it's not visible on the website, people aren't seeing it or buying tickets. So having a persistent message can bring visibility to important but otherwise potentially unnoticed issues. Or you can just say hi and something nice. Github Gist Hook Enhancement - Fast user switching Hooks can run on triggers that vary widely. Some can and should be identified as those that are triggered by the current user, others may be more autonomous like executing via cron. There may be other hooks that are executed by a user that isn't logged in. Depending on the type of action and your need to identify or track it, switching from the current user to another user created specifically to handle certain tasks can be very helpful. ProcessWire tracks a number of things that are attributed to users- log entries note the user, the user that creates pages is stored, the user that last updated the page is stored, etc. You may want to know who did what when, or only take action if the last user that touched something was X and not Y. I created a separate user that has been provided only the specific permissions it needs to complete jobs that are triggered by hooks or crons. Creating a user with less permissions may also help prevent accidental behaviors, or at least help you be very intentional in determining what actions are delegated. Creating custom permissions is also useful. With a dedicated user I can see explicitly that the last update on some pages were made by an autonomous script that syncs information between the ProcessWire application and a third party platform. Github Gist - Fast user switcher Github Gist - Example of switching users in a hook Fast, powerful, and very (very) easy custom admin buttons I needed a way to add custom interactive buttons that had some specific requirements. Needs to be a button that can be clicked by the user and does something Can be conditionally shown to the user with an alternate message if that action is not available Needs to do something on the server and interact with ProcessWire Here's what that looked like for my application. The green "Refresh Activity" button in the top right. That's a custom button and you don't have to author an Inputfield module to get it. When a user clicks that button, it sends a request to the server with GET variables that are recognized in a hook, actions are taken, then a nice message indicating success or failure is shown to the user. To do this you'll need to install FieldtypeRuntimeOnly and create a new field. Following the documentation for that field, create a button with a URL to the current page with GET variables appended. Then create a hook that watches for the specific GET variable that executes if it's present. Shoutout to @Robin S for helping make short work of a potentially complex task. Note that the field code contains JS that handles the URL on page load. Since the hook is looking for a GET variable in the URL, using the back button or refreshing the page will cause the action to run twice. The JS in that example removes the entry from the browser history and also removes the GET parameter after the page loads if it's present. Github Gist - An example gist for the hook that handles the action Github Gist - An example of the FieldtypeRuntimeOnly code that is displayed and interacted with by the user. Automatically convert logged object or array data to JSON If you're using the outstanding Logs JSON Viewer (yet another great one by @Robin S module, then this hook makes for a thoroughly enjoyable logging experience. Using array or stdClass data when logging your values helps store additional information in an organized way Github Gist <?php $log->save('log_name_here', 'Regular string message'); // Remains a string $log->save('log_name_here', ['gets' => 'converted', 'to' => 'json']); $log->save('log_name_here', (object) ['is' => 'stdClass', 'object' => 'friendly']); Use a separate field to store address data for a FieldtypeMapMarker field This one is really simple, more just sharing an implementation and idea, but proved valuable for reducing data redundancy. I have a FieldtypeMapMarker field but the way that I needed to store address data was much better suited to using multiple fields for things like street, city, state, and zip code. I wanted those fields to be the "controlling" fields for the map marker field to prevent needing to edit 2 fields to keep updated, or accidental content diversion between them. On page save the value from the address fields are pulled and converted into a single string that is added to the FieldtypeMapMarker field's "address" property. I used a Custom Field (ProFields) for my address fields but this can be modified to suit your use case very easily. Github Gist You might also consider hiding the address input on the FieldtypeMapMarker field itself to reduce confusion since the values will be updated automatically anyway. You'll need to have this in a file that is appended to the Admin styles /* You can find the appropriate class for the template you are applying this to in the <body> element when editing a page You can omit that if you want to apply this everywhere */ .ProcessPageEdit-template-your_template_name .InputfieldMapMarker.Inputfield_activity_location .InputfieldMapMarkerAddress, .ProcessPageEdit-template-your_template_name .InputfieldMapMarker.Inputfield_activity_location .InputfieldMapMarkerToggle, .ProcessPageEdit-template-your_template_name .InputfieldMapMarker.Inputfield_activity_location .InputfieldMapMarkerLat, .ProcessPageEdit-template-your_template_name .InputfieldMapMarker.Inputfield_activity_location .InputfieldMapMarkerLng { display: none !important; } <?php // Add this to your ready.php file or ready-firing hook to insert the file containing that CSS to your admin. $config->styles->add("/path/to/your/custom/admin/css/file.css"); Not-A-Hook Bonus - Here's code for an interactive Google Map Renders a Google Map using a FieldtypeMapMarker field, a separate address field, Alpine.js, and Tailwind. You'll need a Google Maps API key, a styled map ID from your Google Developer account, and the aforementioned fields. I wrote it using the latest Google Maps API. Saved you some time. You'll probably need to tweak it. I adapted this so if you find a bug please let me know and I'll update the gist. Note- this makes use of the AlpineJS Intersect plugin to improve performance by only loading/initializing the map when a user scrolls close enough to it. If you don't want that, remove the x-intersect directive. If you want to see it in action, you can check it out here. Github Gist Hook Support Class - A static method class to translate a field into all languages automatically If you use the Fluency translation module, this is a class that will help out with translating a field into all languages programmatically. Sharing this here because the next hook uses this as a dependency. I keep this in the HookUtils directory noted in the file structure above. Usage is demonstrated in the next hook. Github Gist Translate all translatable fields using Fluency on page save whether from UI or API. This is useful for instances where you want a page translated automatically and especially helpful when you are creating pages programmatically. This requires the above hook support class, as well as Fluency connected to an API account. Here are things that must be kept in mind. Please read them, the code for the hook, and the code for the support class to ensure that it works to your needs. You should modify Fluency before using this, really. Change the value of CACHE_EXPIRY on line 19 in the TranslationCache file to WireCache::expireNever. Do this to prevent chewing through your API usage from month to month on repeat translations. This will become standard in the next release of Fluency. This is an expensive operation in terms of API usage, which is why you very much should modify the caching behavior. This hook does not make an effort to determine which fields have changed before translating because it doesn't really matter if the translation is already cached. First time translations of pages with a significant amount of fields/content may be slow, like noticeably slower first time page save because this operation is only as fast as the speed of the request/response loop between ProcessWire and the translation API. Later page saves will be much faster thanks to cached translations. This will not attempt to translate empty fields, so those won't cause any delays. This works with multi-language text/textarea/TinyMCE/CKEditor fields, RepeaterMatrix fields, and the newer Custom Fields (ProFields). Other fields haven't been tested, but it's definitely possible to adapt this to those needs. I prefer to target specific templates with hooks, you can add multiple but be mindful of your use case. Consider adding excluded fields to the array in the hook if it makes sense Consider adding a field to enable/disable translations from the UI, a checkbox field or something This hook is probably one of the uglier ones, sorry. If you run out of API usage on your account, you're going to see a big ugly exception error on screen. This is due to Fluency not handling an account overage error properly because the return type was not as expected. Will be fixed in the next version of the module This is one that may be tailored to my PW application, I think it's general enough to use as-is for your project, but testing is definitely required. Read all the code please. Github Gist ProcessWire Object Method & Property Hooks The following are custom methods that add functionality to native ProcessWire objects. Add a getMatrixChildren() method to RepeaterMatrixPage objects RepeaterMatrix fields represent nesting depth as an integer on each RepeaterMatrixPage item. So top level is 0, first nested level is 1, second 2, etc. When looping through RM items, determining nesting requires working with that integer. It works, but adding adding some functionality helps out. This is infinitely nestable, so accessing children, children of children, children of children of children, and so on works. Fun for the whole family. This was inspired by a forum post, another one I can't find... Github Gist <?php // Access nested RepeaterMatrix items as child PageArray objects $page->repeater_matrix_field->first()->getMatrixChildren(); // => PageArray ?> <!-- Assists with rendering nested RM items in templates Sponsors are nested under sponsorship levels in the RM field --> <div> <?php foreach ($page->sponsors as $sponsorshipLevel): ?> <h2><?=$sponsorshipLevel->title?></h2> <?php if ($sponsorshipLevel->getMatrixChildren()->count()): ?> <ul> <?php foreach ($sponsorshipLevel->getMatrixChildren() as $sponsor): ?> <li> <img src="<?=$sponsor->image->url?>" alt="<?=$sponsor->image->description?>"> <?=$sponsor->title?> </li> <?php endforeach ?> </ul> <?php endif ?> <?php endforeach ?> </div> Add a resizeAspectRatio() method to PageImage objects Adds a simple way to quickly resize an image to a specific aspect ratio. Use cases include sizing images for Google Structured Data and formatting images for consistency in image carousels. Could be improved by accepting second argument to specify an image width, but didn't fit my use case. Github Gist <?php $page->image_field->resizeAspectRatio('square')->url; // Alias for 1:1 $page->image_field->resizeAspectRatio('video')->url; // Alias for 16:9 $page->image_field->resizeAspectRatio('17:10')->url; // Arbitrary values accepted Add a responsiveAttributes() method to PageImage objects Adds a very helpful method to generate image variations and accompanying 'srcset' and 'sizes' attributes for any image. Designed to be very flexible and is Tailwind ready. Responsive sizing can be as simple or complex as your needs require. Includes an optional 'mobile' Tailwind breakpoint that matches a custom tailwind.config.js value: screens: { 'mobile': '320px'}. I added this breakpoint largely to further optimize images for small screens. The array of Tailwind breakpoints and size definitions can be edited to suit your specific setup if there are customizations When sizing for Tailwind, the last media query generated will automatically be switched to "min-width" rather than "max-width" to prevent problems arising from restricting widths. Example, you can specify values only for 'sm' and 'md' and the 'md' size will have the media query correctly adjusted so that it applies to all breakpoints above it. Github Gist <-- The responsiveAttributes() returns a renderable attribute string: srcset="{generated values}" sizes="{generated values}" --> <-- Create responsive images with arbitrary width and height at breakpoints --> <img src="<?=$page->image->url?>" <?=$page->image->responsiveAttributes([ [240, 125, '(max-width: 300px)'], [225, 125, '(max-width: 600px)'], [280, 165, '(max-width: 900px)'], [210, 125, '(max-width: 1200px)'], [260, 155, '(min-width: 1500px)'], ])?> width="240" height="125" alt="<?=$page->image->description?>" > <-- Heights can be selectively ommitted by setting the height value to null --> <img src="<?=$page->image->url?>" <?=$page->image->responsiveAttributes([ [240, 125, '(max-width: 300px)'], [225, null, '(max-width: 600px)'], [280, 165, '(max-width: 900px)'], [210, null, '(max-width: 1200px)'], [260, null, '(min-width: 1500px)'], ])?> width="240" height="125" alt="<?=$page->image->description?>" > <-- Create responsive images with only widths at breakpoints --> <img src="<?=$page->image->url?>" <?=page->image->responsiveAttributes([ [240, '(max-width: 300px)'], [225, '(max-width: 600px)'], [280, '(max-width: 900px)'], [210, '(max-width: 1200px)'], [260, '(min-width: 1500px)'], ])?> width="240" height="125" alt="<?=$page->image->description?>" > <-- Create custom sizes matched to Tailwind breakpoints --> <img src="<?=$page->image->url?>" <?=$page->image->responsiveAttributes([ 'mobile' => [240, 125], // Custom tailwind directive 'sm' => [225, 125], 'md' => [280, 165], 'lg' => [210, 125], 'xl' => [260, 155], ])?> width="240" height="125" alt="<?=$page->image->description?>" > <!-- Resizes width of image to fit Tailwind breakpoints, useful for full width images such as hero images, doesn't change height. Also accepts 'tw' as an alias for 'tailwind' --> <img src="<?=$page->image->url?>" <?=$page->image->responsiveAttributes('tailwind')?> width="240" height="125" alt="<?=$page->image->description?>" > Add PHP higher-order function methods to WireArray and WireArray derived objects WireArray objects are incredibly powerful and have tons of utility, but there are situations where I find myself needing to work with plain PHP arrays. I'm a very big fan of PHP's array functions that are efficient and make for clean readable code. I found myself often reaching for $wireArrayThing->getArray() to work with data then using functions like array_map, array_filter, and array_reduce. These return arrays, but could easily be modified to return WireArray objects if that is more helpful. Github Gist <?php // The EventPage page class has a method that determines sold out status from more than one source of data/page fields // which means that it isn't queryable using a ProcessWire selector. This returns a single integer calculated from ticket availability // of all events from non-queryable data. $totalEventsAvailable = $eventPages->reduce( fn ($total, $eventPage) => $count = $eventPage->isActive() ? $total++ : $total, 0 ); // Requires using a page class to determine status reliant on multiple data points not queryable via a selector. Knowing what the event // page is for an activity can't be determined using a selector for activity pages. $displayableActivities = $matches->filterToArray( fn ($activityPage) => $activityPage->eventPage()->isPublic() && $activityPage->isActive() ); // Iterating over each Page in a PageArray and processing data for sorting/ordering before rendering on a search results page // Executed within a page class $results = $searchResults->mapToArray(function($page) { return (object) [ 'page' => $page, 'summary' => $this->createResultSummary(page: $page, maxLength: 750), 'keywordMatchCount' => $this->getQueryMatchCount(page: $page), ]; }); Add an image orientation method/property to PageImage objects Get the portrait or landscape orientation of a PageImage. Github Gist <?php $page->image->orientation; $page->image->orientation(); Add the ability to get all related pages for Page objects at once Gets all of the related pages to a page at once by both page reference fields and links in fields. Transparently passes native arguments to Page methods for native behavior Github Gist <?php $page->allPageReferences(); $page->allPageReferences(true); // Optionally include all pages regardless of status $page->allPageReferences('your_selector=here', 'field_name'); // Use with native Page::references() and Page::links() arguments Add a saveWithoutHooks() convenience method to Page objects The number of hooks in my most recent project was... a lot. There were many that hooked into the page save event and a lot of operations that happen in the background where pages needed to be modified and saved quietly to prevent clearing ProCache files or excessive DB operations through chained hooks. Being able to use a method to do this rather than passing options felt more deliberate and clear when working across hundreds of files and in critical areas of very expensive operations. This method also accepts page save options, but in a way that hooks will always be disabled even if an option is accidentally passed enabling them. Furthermore, it also accepts a string as the first argument that, if debug mode is enabled, will dump a message to the bar via Tracy. Github Gist <?php // Adding a message can be very helpful during testing, especially when saving a page with/without hooks is conditionally based // where the result of another operation determines how a page is saved $page->saveWithoutHooks('updated event sync data hash, saved without hooks'); $page->saveWithoutHooks(['resetTrackChanges' => true]); $page->saveWithoutHooks('message and options', ['resetTrackChanges' => true]); These are a few that I've used to show some diversity in application. Hooking to ProcessWire events makes it possible to build beyond simple websites and implement truly custom behavior. Hope these may be useful to others. If you have any favorite hooks of your own, have corrections of those I've shared, or improvements, sharing them in the comments would be stellar. Cheers!
    1 point
  17. Wow, how time flies. This post is already 9 years old! The old syntax from the last post is now obsolete. But today you can do much more. I just looked at the file along with claude.ai and here is a new version. Edit: I've added all the core hooks and status codes to this file. Could be useful sometimes. But there's more, here's a little module that adds a few things to an extra phpstorm.meta.php. After installing the module you get suggestions for $fields->get('') // suggests existing field names $templates->get('') // suggests existing template names $modules->get('') // suggests existing module names + all hooks defined in /site/modules/
    1 point
×
×
  • Create New...