### ProcessWire Pagination Example Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates how to implement pagination in ProcessWire using the `find` method and `renderPager`. This is useful for displaying large lists of items, such as blog posts. ```php find("template=blog-post, limit=10, sort=-date"); echo $results->renderPager([ 'numPageLinks' => 10, 'nextItemLabel' => 'Next »', 'previousItemLabel' => '« Prev', ]); foreach($results as $post) { echo "

$post->title

"; } ``` -------------------------------- ### ProcessWire Selector Syntax Examples Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates various ways to use ProcessWire's selector syntax for finding and filtering pages. This includes basic template matching, conditional filtering based on field values, date comparisons, text matching operators, and combining multiple criteria. ```php find("template=product"); $featured = $pages->find("template=product, featured=1"); $expensive = $pages->find("template=product, price>=100, price<=500"); // Comparison operators $pages->find("price>100"); // Greater than $pages->find("price<=50"); // Less than or equal $pages->find("id!=1"); // Not equals $pages->find("created>=today"); // Date comparison // Text matching operators $pages->find("title*=hello world"); // Contains phrase (fulltext) $pages->find("title%=hello world"); // Contains phrase (LIKE) $pages->find("title^=Hello"); // Starts with $pages->find("title$=World"); // Ends with $pages->find("body~=sushi tobiko"); // Contains all words (any order) $pages->find("title~|=red blue green"); // Contains any word // Multiple fields OR (pipe) $pages->find("title|body|sidebar*=search term"); // Multiple values OR (pipe) $pages->find("name=about|contact|services"); $pages->find("color=red|blue|green"); // Sorting and limiting $pages->find("template=blog-post, sort=-date, limit=10"); $pages->find("template=product, sort=title, sort=-price"); $pages->find("parent=/products/, sort=sort"); // Admin drag order // Count selectors for multi-value fields $pages->find("children.count>0"); // Has children $pages->find("images.count>=3, images.count<=5"); $pages->find("categories.count=0"); // No categories // Subfield selectors $pages->find("images.width>=800"); $pages->find("files.ext=pdf"); $pages->find("category.name=featured"); $pages->find("author.roles.name=editor"); // Access control modifiers $pages->find("template=product, include=hidden"); $pages->find("template=product, include=unpublished"); $pages->find("template=product, include=all"); // Sanitize user input for selectors $q = $sanitizer->selectorValue($input->get->q); $results = $pages->find("title|body~=$q, limit=20"); // Sub-selectors for related pages $pages->find("template=product, company=[locations>5]"); // OR-groups for complex logic $pages->find(" template=product, stock>0, (featured_from<=today, featured_to>=today), (highlighted=1) "); ``` -------------------------------- ### ProcessWire Type-Safe Function and Hooks with Custom Page Classes Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Shows a type-safe function `blogPostByline` that accepts a `BlogPostPage` object and an example of using hooks (`ProductPage::saveReady`) to interact with custom page class properties and methods. ```php authorName on $post->date"; } // Hooks target specific page types $wire->addHookBefore('ProductPage::saveReady', function($event) { $product = $event->object; if($product->num_available <= 0 && !$product->isHidden()) { $product->addStatus('hidden'); } }); ``` -------------------------------- ### ProcessWire Custom Page Class Definition Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Defines a custom page class `BlogPostPage` extending `ProcessWirePage`. It includes type-hinted properties, custom methods like `getRelatedPosts`, `getAuthorName`, `getExcerpt`, and `numWords`, and enables property access via `get()`. ```php usePageClasses = true; // /site/classes/BlogPostPage.php /** * Blog Post Page * * @property string $title * @property string $summary * @property string $body * @property string $date * @property PageArray|CategoryPage[] $categories * @property-read string $authorName * @property-read int $numWords */ class BlogPostPage extends Page { public function getRelatedPosts(): PageArray { return $this->wire()->pages->find([ 'template' => 'blog-post', 'categories' => $this->categories, 'sort' => '-date', 'id!=' => $this->id, 'limit' => 5, ]); } public function getAuthorName(): string { $u = $this->createdUser; return "$u->first_name $u->last_name"; } public function getExcerpt(int $maxLen = 200): string { $excerpt = $this->summary; if(empty($excerpt)) { $excerpt = $this->wire()->sanitizer->truncate($this->body, $maxLen); } return $excerpt; } public function numWords(): int { return str_word_count(strip_tags($this->body)); } // Enable property access via get() public function get($key) { if($key === 'authorName') return $this->getAuthorName(); if($key === 'numWords') return $this->numWords(); return parent::get($key); } } ``` -------------------------------- ### ProcessWire Delayed Output with Prepend/Append Files Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates using `config.prependTemplateFile` and `config.appendTemplateFile` in ProcessWire to include initialization and main layout files. This allows for modular template structure. ```php prependTemplateFile = '_init.php'; $config->appendTemplateFile = '_main.php'; // /site/templates/_init.php $headline = $page->get("headline|title"); $bodycopy = $page->body; $sidebar = $page->sidebar; // /site/templates/basic-page.php $bodycopy .= "

Additional content

"; // /site/templates/_main.php includes all the HTML wrapper ``` -------------------------------- ### ProcessWire Hooks: After, Before, and Replace Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates how to use after, before, and replace hooks in ProcessWire to modify return values, validate arguments, or completely override method behavior. These hooks are essential for extending ProcessWire's functionality. ```php addHookAfter('Page::render', function($event) { $page = $event->object; $event->return = str_replace( '', '

Page ID: ' . $page->id . '

', $event->return ); }); // Before hook - validate/modify arguments $wire->addHookBefore('Pages::save', function($event) { $page = $event->arguments(0); if($page->template == 'product' && !$page->price) { throw new WireException("Products must have a price"); } }); // Replace hook - completely replace behavior $wire->addHookBefore('Page::render', function($event) { $page = $event->object; if($page->template == 'maintenance') { $event->replace = true; $event->return = "

Site Under Maintenance

"; } }); ``` -------------------------------- ### Create Symlinks for Multi-Tool AI Support (Bash) Source: https://github.com/webmanufaktur/pwaiworkflow/blob/master/README.md This script creates symbolic links from tool-specific directories (e.g., .claude/, .cline/) to the .agents/skills/ folder. This allows AI coding assistants that do not natively support the .agents/ directory structure to utilize the same skills. ```bash ./create-symlinks.sh ``` -------------------------------- ### Basic ProcessWire Module Structure Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Defines a simple 'Hello World' module for ProcessWire. This module implements the Module interface and provides a basic method. It requires ProcessWire 3.0.0 or higher and can be used by retrieving it via the modules API. ```php 'Hello World', 'summary' => 'A simple example module', 'version' => 100, // 1.0.0 'author' => 'Your Name', 'autoload' => false, 'singular' => true, 'requires' => ['ProcessWire>=3.0.0'], ]; } public function hello($name = 'World') { return "Hello, $name!"; } } // Usage $module = $modules->get('HelloWorld'); echo $module->hello('ProcessWire'); ``` -------------------------------- ### ProcessWire Hooks: Adding Methods and Properties Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Illustrates how to dynamically add new methods and properties to existing ProcessWire objects using hooks. This allows for custom logic and data retrieval without modifying the core classes. ```php addHook('Page::summarize', function($event) { $page = $event->object; $maxLen = $event->arguments(0) ?: 200; $event->return = wire('sanitizer')->truncate($page->body, $maxLen); }); // Usage: echo $page->summarize(150); // Add new property to Page class $wire->addHookProperty('Page::intro', function($event) { $page = $event->object; $intro = strip_tags($page->body); $event->return = substr($intro, 0, 255); }); // Usage: echo $page->intro; ``` -------------------------------- ### ProcessWire Autoload Module with Hooks Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates an autoloading module in ProcessWire that executes code on every request or specifically within the admin area. It utilizes the `ready` method to safely add hooks, such as logging page save events. ```php 'My Autoload Module', 'version' => 1, 'autoload' => true, // Loads on every request // 'autoload' => 'template=admin', // Only in admin ]; } public function init() { // Early initialization - API may not be ready } public function ready() { // API is ready - safe for hooks $this->addHookAfter('Pages::saved', function($event) { $page = $event->arguments(0); $this->log->save('my-module', "Page saved: {$page->path}"); }); } } ``` -------------------------------- ### Process Image and File Fields in PHP Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates how to access, manipulate, and display images and files associated with a ProcessWire page. It covers iterating through multiple images, accessing single images, generating thumbnails with various resizing and cropping options, retrieving image properties, and filtering pages based on image and file attributes. ```php images as $image) { echo "$image->description"; } // Single image field if($page->image) { echo ""; } // First/random image $first = $page->images->first(); $random = $page->images->getRandom(); // Image resizing - cached automatically $thumb = $image->width(300); // Proportional width $thumb = $image->height(200); // Proportional height $thumb = $image->size(300, 200); // Exact size (crops) echo ""; // Cropping options $thumb = $image->size(300, 200, 'north'); // Top center $thumb = $image->size(300, 200, 'southeast'); // Bottom right $thumb = $image->size(300, 200, 'center'); // Center (default) $thumb = $image->size(300, 200, '50%,30%'); // Percentage position // Resize options $options = [ 'quality' => 90, 'upscaling' => false, 'cropping' => 'center', ]; $thumb = $image->size(300, 200, $options); // Image properties echo $image->url; // Full URL echo $image->filename; // Server path echo $image->width; // Width in pixels echo $image->height; // Height in pixels echo $image->description; // Description text echo $image->filesizeStr; // "1.5 MB" // Image tags (enable in field settings) $featured = $page->images->getTag('featured'); $gallery = $page->images->findTag('gallery'); // File fields foreach($page->files as $file) { echo "$file->description ($file->filesizeStr)"; } // Find pages by file properties $pages->find("images.width>=800"); $pages->find("files.ext=pdf"); $pages->find("files.filesize>1000000"); ``` -------------------------------- ### ProcessWire Markup Regions for HTML-First Approach Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Illustrates ProcessWire's Markup Regions feature, enabling an HTML-first approach to template development. Regions can be targeted for replacement, appending, or prepending content using `pw-append` and `pw-prepend` attributes. ```php useMarkupRegions = true; $config->appendTemplateFile = '_main.php'; // In template files, target regions by ID: ?>

This replaces the default bodycopy content.

``` -------------------------------- ### ProcessWire Core API Variables and Access (PHP) Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates how to access and utilize core ProcessWire API variables like $page, $pages, $user, and $input within templates and modules. It covers finding pages, accessing page properties, traversing page trees, and retrieving API services like sanitizer and session. ```php useFunctionsAPI = true; // Access pages - all equivalent methods $pages->find("template=product"); // Via API variable pages()->find("template=product"); // Via function pages("template=product"); // Shortcut // Get single page $contact = $pages->get("/about/contact/"); $product = pages("/products/hammer/"); // Common API variables $currentPage = $page; // Current page being viewed $allPages = $pages; // Find, get, save, delete pages $currentUser = $user; // Current logged-in user $input = wire('input'); // GET, POST, COOKIE, URL segments $sanitizer = wire('sanitizer'); // Input sanitization $session = wire('session'); // Session management $config = wire('config'); // Site configuration // Page properties echo $page->id; // Page ID echo $page->title; // Page title echo $page->path; // Full path: /parent/child/ echo $page->url; // URL with domain handling echo $page->httpUrl; // Full URL with scheme echo $page->template->name; // Template name echo $page->parent->title; // Parent page title // Page traversal $children = $page->children("limit=10"); $siblings = $page->siblings(); $ancestors = $page->parents(); $rootParent = $page->rootParent; // Access API in classes/modules class MyModule extends WireData implements Module { public function doSomething() { $pages = $this->wire('pages'); // Preferred in modules $user = $this->wire()->user; // IDE-friendly return $pages->find("template=product"); } } ``` -------------------------------- ### Define Fields and Templates with RockMigrations in PHP Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt This snippet demonstrates how to use RockMigrations to define custom fields (text, textarea, datetime, rich text, page reference, image, repeater) and templates within a ProcessWire project. It utilizes a fluent API for a structured approach to database and content structure management. ```php migrate([ 'fields' => [ // Text fields 'my_text' => ['type' => 'text', 'label' => 'Title'], 'my_textarea' => ['type' => 'textarea', 'rows' => 5], // Date field 'my_date' => [ 'type' => 'datetime', 'dateOutputFormat' => 'd.m.Y H:i', 'dateInputFormat' => 'j.n.y', 'datepicker' => 1, 'defaultToday' => 1, ], // Rich text (CKEditor) 'my_body' => [ 'type' => 'textarea', 'inputfieldClass' => 'InputfieldCKEditor', 'contentType' => 2, 'formatTags' => 'p;h2;h3;h4', 'toolbar' => 'Format, Bold, Italic, BulletedList, PWLink, Source', ], // Page reference 'my_category' => [ 'type' => 'page', 'derefAsPage' => 1, 'inputfield' => 'InputfieldPageListSelect', 'findPagesSelector' => 'template=category', 'labelFieldName' => 'title', ], // Image field 'my_image' => [ 'type' => 'image', 'maxFiles' => 1, 'extensions' => 'jpg jpeg png svg', 'maxSize' => 3, 'gridMode' => 'grid', ], // Repeater 'my_items' => [ 'type' => 'FieldtypeRepeater', 'fields' => ['title', 'body', 'image'], 'repeaterTitle' => '#n: {title}', ], ], 'templates' => [ 'product' => [ 'fields' => ['title', 'my_body', 'my_image', 'my_category'], 'sortfield' => '-created', 'parentTemplates' => ['products'], 'noChildren' => 1, ], ], ]); // Create roles with permissions $rm->createRole('editor', ['page-edit', 'page-view']); // Create pages with data using named parameters $rm->createPage( template: 'product', parent: '/products/', name: 'demo-product', title: 'Demo Product', data: [ 'my_body' => '

Product description

', 'my_category' => $pages->get('/categories/featured/'), ], ); ``` -------------------------------- ### ProcessWire Performance Optimizations: findIDs and findRaw Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates methods for optimizing ProcessWire queries by retrieving only necessary data. `findIDs` returns an array of page IDs, while `findRaw` returns an array of associative arrays containing specified fields, bypassing full page objects for better performance. ```php findIDs("template=product"); $data = $pages->findRaw("template=product", ["title", "price"]); ``` -------------------------------- ### ProcessWire Hooks: Custom Routing and Conditional Logic Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Shows how to implement custom routing using URL hooks and how to apply hooks conditionally based on page templates or argument values. This enables flexible URL handling and targeted behavior modification. ```php addHook('/api/products/{id}', function($event) { $id = (int) $event->id; $product = $event->pages->get("template=product, id=$id"); if($product->viewable()) { return [ 'id' => $product->id, 'title' => $product->title, 'price' => $product->price, ]; } return false; // 404 }); // Conditional hooks - target specific page types $wire->addHookAfter('Page(template=product)::render', function($event) { // Only executes for product pages }); // Hook with argument conditions $wire->addHookAfter('Page::changed(status)', function($event) { $oldValue = $event->arguments(1); $newValue = $event->arguments(2); wire('log')->save('changes', "Status changed from $oldValue to $newValue"); }); ``` -------------------------------- ### ProcessWire URL Segments for Routing Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Shows how to implement URL segment routing in ProcessWire templates. This allows different content or includes to be loaded based on URL segments, enabling cleaner routing. ```php Templates > [template] > URLs $segment = $input->urlSegment1; switch($segment) { case '': echo $page->body; break; case 'photos': include('./_photos.php'); break; case 'map': include('./_map.php'); break; default: throw new Wire404Exception(); } ``` -------------------------------- ### ProcessWire Direct Output Template Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt A basic ProcessWire template file demonstrating direct output of page properties like title and body. This is the simplest method for rendering content. ```php <?=$page->title?>

title?>

body?> ``` -------------------------------- ### ProcessWire Hooks: Priority and Execution Order Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Explains how to control the execution order of multiple hooks attached to the same method using the 'priority' option. Lower priority numbers execute earlier, while higher numbers execute later. ```php addHookAfter('Page::render', function($event) { // Runs first }, ['priority' => 50]); $wire->addHookAfter('Page::render', function($event) { // Runs last }, ['priority' => 200]); ``` -------------------------------- ### ProcessWire WireArray Methods: Filtering, Access, Iteration, Aggregation, Sorting, Modifying, Combining Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Demonstrates common operations on ProcessWire's WireArray objects, including filtering collections based on selectors, accessing specific items, iterating through items, aggregating data, sorting, adding/removing items, and merging arrays. These methods are crucial for efficient data manipulation within ProcessWire. ```php find("template=product"); // Filtering $filtered = $items->find("price>100"); $items->filter("featured=1"); // In-place filter $excluded = $items->not("hidden=1"); // Exclude matching // Access $first = $items->first(); $last = $items->last(); $third = $items->eq(2); // 0-indexed $random = $items->getRandom(); $subset = $items->slice(0, 5); // Iteration $items->each(function($item) { echo $item->title; }); echo $items->each("
  • {title}
  • "); // Template string // Aggregation $count = $items->count(); $titles = $items->implode(", ", "title"); $titleArray = $items->explode("title"); // Sorting $items->sort("title"); // Ascending $items->sort("-date"); // Descending $items->shuffle(); // Random order $items->reverse(); // Adding/Removing $items->add($newItem); $items->prepend($item); $items->append($item); $items->remove($item); // Combining $merged = $items->and($otherItems); $items->import($array); ``` -------------------------------- ### Configurable ProcessWire Module Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Illustrates a ProcessWire module that can be configured through the admin interface. It implements the `ConfigurableModule` interface and defines input fields for API keys and feature toggles using ProcessWire's Inputfield system. ```php 'Configurable Module', 'version' => 1, ]; } public function __construct() { $this->apiKey = ''; // Default value $this->enabled = true; } public static function getModuleConfigInputfields(array $data) { $inputfields = new InputfieldWrapper(); $f = wire('modules')->get('InputfieldText'); $f->name = 'apiKey'; $f->label = 'API Key'; $f->value = isset($data['apiKey']) ? $data['apiKey'] : ''; $inputfields->add($f); $f = wire('modules')->get('InputfieldCheckbox'); $f->name = 'enabled'; $f->label = 'Enable Feature'; $f->checked = isset($data['enabled']) ? $data['enabled'] : false; $inputfields->add($f); return $inputfields; } } ``` -------------------------------- ### Process Repeater Fields in PHP Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Illustrates how to manage and access data within repeater fields in ProcessWire. It covers iterating through repeater items, checking for their existence, accessing the first item, finding pages based on repeater field values, adding new repeater items programmatically, removing existing items, and rendering repeater items using template files. ```php buildings as $building) { echo "

    $building->title

    "; echo "

    Height: $building->feet_high feet

    "; echo "

    Floors: $building->num_floors

    "; } // Check for items if(count($page->buildings)) { echo "Has " . count($page->buildings) . " buildings"; } // First item $first = $page->buildings->first(); // Find pages by repeater values $pages->find("buildings.height>500"); $pages->find("buildings.year_built=1940, buildings.num_floors>=20"); $pages->find("buildings.count>0"); // Has at least one item // Add new repeater item via API $building = $page->buildings->getNew(); $building->title = 'Empire State Building'; $building->feet_high = 1454; $building->num_floors = 102; $building->save(); $page->buildings->add($building); $page->save(); // Remove repeater item $building = $page->buildings->first(); $page->buildings->remove($building); $page->save(); // Render with template file /site/templates/repeater_buildings.php foreach($page->buildings as $building) { echo $building->render(); } ``` -------------------------------- ### ProcessWire Admin Process Module Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt Shows how to create a Process module for the ProcessWire admin interface. This module defines its title, permission, admin page structure, and navigation links. The `execute` method handles the main view, while `executeAdd` handles a specific sub-view. ```php 'My Admin App', 'version' => 1, 'permission' => 'my-app', 'page' => [ 'name' => 'my-app', 'parent' => 'setup', 'title' => 'My App', ], 'nav' => [ ['url' => './', 'label' => 'List', 'icon' => 'list'], ['url' => 'add/', 'label' => 'Add', 'icon' => 'plus'], ], ]; } public function execute() { // Main view: /admin/setup/my-app/ return "

    My App Dashboard

    "; } public function executeAdd() { // Add view: /admin/setup/my-app/add/ return "

    Add New Item

    "; } } ``` -------------------------------- ### Implement Security Best Practices in ProcessWire with PHP Source: https://context7.com/webmanufaktur/pwaiworkflow/llms.txt This code showcases ProcessWire's security features, including input sanitization for various data types (text, email, URL, integer, page name), selector value sanitization, HTML output encoding, and using HTML Purifier for rich text content. It also covers CSRF protection for forms and secure login procedures. ```php text($input->post->name); $email = $sanitizer->email($input->post->email); $url = $sanitizer->url($input->post->website); $id = $sanitizer->int($input->get->id); $pageName = $sanitizer->pageName($input->post->username); // Safe for selectors $q = $sanitizer->selectorValue($input->get->q); $results = $pages->find("title|body~=$q, limit=20"); // HTML output encoding echo $sanitizer->entities($userInput); // Rich text with HTML Purifier $safeHtml = $sanitizer->purify($input->post->content); // CSRF protection for forms ?>
    CSRF->renderInput()?>
    post->submit) { if(!$session->CSRF->hasValidToken()) { throw new WireException("Invalid request"); } // Process form safely } // Secure login form if($input->post->login) { if(!$session->CSRF->hasValidToken()) { $error = "Invalid request"; } else { $name = $sanitizer->pageName($input->post->username); $pass = $input->post->password; if($session->login($name, $pass)) { $session->regenerateId(); // Prevent session fixation $session->redirect('/members/'); } else { $error = "Invalid username or password"; } } } // Access control checks if(!$page->viewable()) throw new Wire404Exception(); if(!$page->editable()) throw new WirePermissionException("Access denied"); // Whitelist approach for user input $allowed = ['article', 'news', 'blog']; $template = $input->get->template; if(!in_array($template, $allowed)) { $template = 'article'; } $pages->find("template=$template"); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.