Jump to content

Tutorial: A Quick guide to ProcessWire for those transitioning from MODX


Recommended Posts

You've been using MODX but now you've found ProcessWire. It’s totally amazed you and you can’t wait to get started. But…you are wondering where everything is. If this is you, read on…

This guide is primarily aimed at those who are coming in from MODX and wish to know how to accomplish “MODX things” the “ProcessWire (PW) way”.

This is not meant to be a full blown PW tutorial. It will focus on some key MODX concepts/tasks and how to accomplish those in PW. It will cover, whenever possible, both versions of MODX - Evolution and Revolution. The guide assumes that you’ve at least logged into a PW site and/or viewed a demo.

The Table of Contents mostly reflects MODX terminology.

Table of Contents

1. Manager
2. File System
3. Resources
4. Templates
5. Template Variables (TVs)
6. Template Files
7. Snippets
8. Modules
9. Plugins
10. Chunks
11. Miscellaneous
12. Examples - the PW way…

a. Template Variables

b. Snippets

c. Modules

d. Plugins

e. Chunks

1. Manager

In PW, the “manager” is referred to as the “Admin”. The default location of the Admin is www.yoursite.com/processwire. As of PW 2.3, you can rename the Admin to anything you wish during install. Just be careful to remember the name you use or you will be locked out! (If that happens, there are ways to get around it though).

Logging into Admin, you will notice a tree just like in MODX.   The Admin runs on jQuery UI and as you’ve seen, it is insanely fast! Don’t be fooled by its simple facade. PW is a really powerful CMS, highly extensible and very easy to use.

Customising the PW admin is very easy. There are a number of custom themes available. It is also trivial to make yours. Custom Admin themes go into the folder site/templates-admin/. You can have only one theme at a time.  On a related matter, if you wish to create a custom Admin page, it is easy to do so. See this thread for more info.

2. File System

After installing PW, you will see two main folders; “site” and “wire” in your file system. Site is where all things related to your site reside. This is your playground and will survive an upgrade. Wire houses the Core. You will never have to go into that folder.

3. Resources

In MODX, Resources can be many things (documents, etc.). There is no such term in PW. However, the most important “resources” you need to know about in PW are page and pages.

Pages are a very powerful concept in PW. Page can refer to your website’s frontend pages, i.e. what can be viewable by your website users. I say can be for a reason. There are many uses for Pages in PW. This can be confusing to newbies but once you get the concept, you will appreciate the power of the system. Just because something is a Page does not mean it has to be viewable. It can have other uses such as a container that holds data for use by other Pages - in this case the Pages do not have to be displayed on the frontend. In fact, everything you see on the PW tree is a Page. That’s right; even the Admin and its components (Users, Roles, etc.) are all Pages! Still confused by Pages? Have a read here.

Pages reside in the PW tree - you may have noticed O0 . You can drag and drop pages to move them around. If drag and drop doesn’t work you probably have MySQL installed. Upgrade your MySQL and you are sorted. The root of your site is the uppermost Page in the tree. In the default PW install this is called Home. You can change the name to something else.

The default PW install comes with a number of Pages pre-installed. Try editing a Page. All those Fields you see on that Page when under the “CONTENT” tab? They are not default Fields. They are all Custom Fields! Yes, not even the Title! The only required Field for a Page is “name”. You find this under the “SETTINGS” tab on the page edit screen. More about Fields below…

Other related stuff:
Menuindex: As an aside, if outputting something like a menu, unless you state otherwise, it will reflect the tree hierarchy.
Show in Menu: This functionality is covered by the Page status, whether hidden or not. Hidden pages do not get output on menus (more about menus later) unless explicitly stated so using PW API selectors (see below).
Menu Title: No such term in PW. You can name your menu items what you wish to name them.

Very important: All Pages must be assigned a Template.

4. Templates

In PW, Templates mean something slightly different compared to MODX and many other CMS. MODX describes templates as:

Your Resources show themselves to the world through a Template, which gives them an outer HTML “skin”.

That is not entirely true of PW Templates. In PW, the term Template is used in the sense of the English definition of the term:

A template is “something that establishes or serves as a pattern” - Merriam Webster.

PW Templates serve as a pattern for the foundation of your Pages. By foundation, I do not mean the HTML or CSS. Rather, the Template servers as blueprint for your Pages. What is available to the Template is available to the Page that uses it. Templates establish a pattern for the Pages by the inclusion of Fields to the Template. A Template can have as many or as few Fields as you wish. You can create as many or as few Templates as you wish. You can easily change the Template a Page uses when editing the Page. See under SETTINGS. Note that if you have Fields on that Page that are not in the Template you are switching to, those Page Fields will be lost! PW will warn you about this when switching Templates though.

OK, so how do you show “Resources to the world”? That will be the work of Template Files which we’ll look at in section 6.

5. Template Variables

A powerful feature of MODX is Template Variables (TVs). If you loved MODX TVs you will absolutely adore the PW equivalent. In PW, TVs are known as Fields. You can define your own Custom Fields. In fact, you will need to create your own in most cases. This is because PW does not have any required Field except for “name”. It is perfectly reasonable to have a Page with only the name Field! Many people do add at least a Title Field for such Pages. In the default PW install, the reason you see the Title Field in all Templates is because it has been set as a “Global” Field under the ADVANCED settings of the Field (edit the Field to see this setting).

There are many types of Fields to hold all sorts of data - images, texts, urls, passwords, reference to Pages, etc. You can call your Fields anything you wish. You can call the Field for your main content “body”, or “stuff”, or “content” or whatever you wish as long as you follow the naming convention, i.e. “[a-z], numbers [0-9], or underscores (no dashes or spaces)”.

Fields are reusable across different Templates. The order in which they appear on your Page follows the order in which they are arranged on your Template.  However, it is important to note that:

The order in which Fields appear on your page and/or the inclusion of a Field on your Page does not mean that:

  • The Field will be output on the Frontend. The inclusion of a Field on a Page does not automatically mean it will be output; PW does not make that decision for you. It only makes the Field available to you to use as you require. You can output all or none or few of the Fields on your Page.
  • The order in which Fields appear on a Page does not mean the same order will be reflected when you output the Page. You make that decision.

Also note that you are able to arrange Fields side-by-side on your Page (via settings on the Page’s Template) to mimic your site’s layout or for other visual/ease-of-use purposes as you edit the Page in the Admin. You can also give each Field a label and a description. These will appear above each Field when editing a Page.

In most cases, the content of Fields will be saved directly to the database. In the case of file related Fields, the path to the file will be stored in the database.

There are no direct equivalents of MODX TVs “@Bindings” (data sources). These are inherently built into the different types of Fields. Note that you cannot run PHP code within Fields (so no @Eval). This is by design.

Fields are a very powerful and much loved concept in PW. Just Google "custom fields cms" and you’ll see what comes up tops.

6. Template Files

So far, we've seen that you create Fields, add them to a Template you've created and edit a Page using that Template to input your content into that Page’s Fields. So far so good but how do you output the content to the world? You do this via Template Files. Template Files live in /site/templates/. You have to create your own Template Files.

In MODX, a Template will have both system fields, e.g. body, title, etc. and custom fields (TVs). Rendering a Template is achieved by adding MODX tags to your Template. In PW, in order for the content of your Page to be seen by the world, its Template must have a Template File (or there must be some other Template File associated with a different Template that is dynamically pulling and outputting elsewhere the content of a Page whose Template does not have a Template File). This does not mean that all content within a Page will be automatically output by the Template File. No; in the Template File you can choose to render all or some Fields present in the Pages using that Template or none at all!

You tell the Template about the associated Template File when you create/edit the Template. PW assumes that there is a Template File with the same name as the Template in /site/templates/ and will tell you if it does not find one. However, you have two other choices. You can either enter an alternative name for your Template File or tell PW you do not wish to have a Template File for that Template. This means that a Template does not require a Template File. Obviously, in such a case, you will not be able to directly output the content of the Pages using that Template. In some cases, that is exactly what you want ;) . As you get to know more about the system, you will find out how powerful and flexible the PW Template system can be. For instance, you can use your Template as a controller. That’s beyond this guide but feel free to search the forums for more info.

Template Files are typically PHP files with logic to dynamically output your content. In most cases, Template Files are HTML with PHP tags inserted to output your content. The PHP in the Template File will in most cases be PW API. Hence, you will see things like $page and $pages in Template Files. The former always refers to the current Page and the latter to any other Pages in the system. These are very powerful variables in PW and give you access to ALL information about ALL pages including their Fields and whether those are empty or not, etc. See below for more info about these variables and have a look the docs too.

Note that Templates do not care about what’s in your Template Files. In fact, they won’t even check. All they want is for you to tell them if and how you wish your Pages' contents to be rendered. Your Template Files can even contain pure HTML (although that won’t be dynamic!)! Your Template Files can have references to other things related (or not related!) to the Page using that Template File. For instance, within your Template File, you can pull in the 10 latest “posts” from your Blog or the Children Pages of that Page. There are just too many possibilities to list them all here.

Don’t let the PHP in Template Files scare you if you are no coder. I am no coder but I am able to use PW. You will only need to know at least some very basic PHP to use PW. The most important are:

  • echo;
  • foreach; and
  • if…

Anything else is a bonus in most cases. In addition, you will need to know how to use the two most important PW variables - $page and $pages. With these two, most of what you would have done in vanilla PHP is covered. They are easy to use and to understand and very much follow the jQuery concept. Check out the docs to learn more.

7. Snippets

In MODX, Snippets are the method by which MODX allows you to run dynamic PHP code in any of your pages. In PW, the term Snippets does not exist. What!?! Not to worry; in PW MODX-like Snippet functionality can be achieved in two ways:

  • Template Files: Most PW Template Files are essentially dynamic PHP code. That’s Snippets for you right there.
  • PW Modules: PW Modules do what MODX Snippets do and more (see below). It’s just an issue of terminology. For instance, the popular MODX Snippet WayFinder has a Module counterpart in PW. This is the Module Markup Simple Navigation. However, you do not need to use the Module to create a menu in PW. You can do the same thing using PHP in your Template File. See the default PW install for a simple example.

Check out section 12 of this guide for example “popular-MODX-Snippets-to-PW-how-tos”.

8. Modules

MODX Evolution defines Modules as “a program that can only be executed from within the manager.” There are PW Modules that fit this definition, for instance, the Module Batcher which is equivalent to the MODX Revolution add-on Batcher. However, there are other PW Modules that are executed in the frontend, e.g. Markup Simple Navigation previously mentioned. In fact, in PW, a Module is PHP that extends the functionality of PW. Modules contain PHP classes that adhere to PW’s Module interface.

9. Plugins

In MODX Plugins are PHP code that are set to execute during certain system events. In PW, although the term Plugin does not exist (except maybe in reference to Modules), MODX Plugin functionality is easily doable in PW. This is achieved via Hooks. There is one difference though. In MODX, Plugins are standalone code you can download and install. You cannot download and install PW Hooks. Instead, PW contains many methods that you may hook into in order to modify the behaviour of the method. In other words, PW offers the ability to hook into its processes/events and manipulate them before or after the event or even replace them, etc. Hooks are usually invoked inside Modules. However, Hooks may be attached from anywhere that you use PW's API, for instance in Template Files. 

The average PW user will not need to use Hooks. For more info about Hooks check the documentation.

10. Chunks

MODX defines Chunks as “bits of static text which you can reuse across your site”. There is no equivalent term in PW. You can, however, easily create Chunks in PW. You can create Chunks as a Page that contains various Fields each of which can act as a Chunk. This means you can have Chunks of all sorts of data. You can then set the Page you create to hold your Chunks as hidden (not available to searches). The Page can also be assigned a Template without a Template File to further limit frontend access. It can also be created as a child/grandchild of the page Admin. That will limit access by User (e.g. login required to view it in Admin). Accessing the Fields of that Page as your Chunks using PW API is quite trivial really. Since you can use labels and descriptions to further define each of your Pages’ Fields, this makes it quite easy to describe what each Chunk is for and/or give instructions on how to use the Chunks. See this example for more info.

You can also simply use text files to pull into your content as Chunks. Personally I prefer using Pages as Chunks when I need to.

In MODX, it is usual for many Snippets to use Chunks (HTML + placeholders) to structure their output (i.e. tpl Chunks). You do not need to do this in PW. Code output can be wrapped in HTML right within the Template File.

The important thing to remember is that any Field in any PW Page is available to any other Page and any Template File. Cross-referencing Fields and Pages is child’s play. Seriously; it is that easy. 

11. Miscellaneous

  • Tag Syntax: PW does not use a templating language (tagging syntax) like MODX does. See this article why this decision was taken. I agree 100% with the approach and have come to realise its many benefits over using a templating language. However, there are two Modules that allow you to use template language tags in PW. I have never used them so cannot comment further. They are here and here.
  • Settings Page: PW has no settings page like MODX. Many settings are set in the /site/config.php. Other MODX settings equivalents are interspersed in various places including caching content on a Template by Template basis and in Modules. You can also store custom settings needed for your site in the /site/config.php. You can even use Pages to store your settings as mentioned in section 10 (Chunks).
  • Modularity: PW is a very modular system. The whole of PW is made up of Modules (Core Modules) that accomplish different tasks.

12. Examples - the PW way…

In this section I will show you equivalent MODX versus PW add-ons as well as how to accomplish various task using PW “Snippets” equivalent.

a. Template Variables

As previously mentioned, MODX TVs are PW Fields (although PW Fields are more versatile). Outputting the contents of your Fields is very simple.

echo $page->name_of_your_field;

This gives you the contents of the Field in the current page. Works slightly different for image fields though.

For other pages echo:

One page

$pages->get(ID or PATH or NAME, etc.)->name_of_your_field;
//note replace ID with ID of the Page you want, etc.

Many pages

//this will return an array. You can then go through the array using foreach as shown in the examples below to output the field(s) contents

MODX - output main content of Page

//MODX Evolution[*content*]
//MODX Revolution[[*content]]

ProcessWire - output main content of Page

echo $page->content;//note; this assumes you have a Field called content in the Template of the current page.

In ProcessWire you can find Fields of other pages like so

$fruits = $pages->find("template=yumyum, limit=10");
foreach($fruits as $fruit) {
        echo "<li><a href='{$fruit->url}'>{$fruit->title}</a></li>";
//find 10 Pages that use the Template yumyum and echo their url and titles in a list.

From these examples, you can see the elegance and flexibility of TVs done the PW way. Since there is separation between a Template and a Template File, you can conditionally echo out the contents of Fields found in Pages.

b. Snippets

Common MODX Snippets and PW equivalent Modules

1. WayFinderMarkup Simple Navigation or code in Template File (see head.inc in default PW installation for a simple menu).
2. Ditto (Evolution) and getResources (Revolution): Functionality inbuilt in PW. Use $page and $pages variables + selectors to find anything, anywhere.
3. Jot (Evolution) and Quip (Revolution): Comments Module is part of the PW Core. It is not enabled by default. You will have to enable it in the Admin. See also the related Comments Manager Module.
4. eForm (Evolution) and FormIt and FormItBuilder (Revolution): There are various Form parser codes in the Forums. There is also a commercial Form Builder Module. It’s not expensive, is developed by PW’s lead developer and proceeds support the PW project.
5. MaxiGallery (Evolution) and Gallery (Revolution): Presently, there is no equivalent. However, it is quite easy to build a photo album. See this tutorial in the wiki. There is also an Images Manager Module (still in alpha though).
6. AjaxSearch: Ajax Page Search Module.
7. Breadcrumbs: Quite easy to accomplish using PW API. See default PW install for an example.
8. FirstChildRedirect: Very easy to do using PW API like this: $session->redirect($page->children->first()->url); in a Template File.
9. getField (Evolution) and getResourceField (Revolution): Inbuilt in PW $page and $pages variables as shown above.
10. GetParent: Inbuilt in PW $page and $pages variables. E.g. $page->parent.
11. getPage: PW has inbuilt pagination as part of the Core (Pagination Markup Module). See this article for a quick tutorial.
12. UltimateParent: Inbuilt in PW API as rootParent.
13. if (Revolution): Use vanilla PHP with PW variable and selectors in Template Files.
14. VersionX (Revolution): Versioning for text-based fields is coming in PW 2.4 (release date summer 2013). Support for maintaining separate draft and live versions coming in PW 2.5 (Winter 2013/2014). Currently, there is also the Module Version Control for Text Fields.
15. getRelated (Revolution): No out-of-the-box Module for this. Has been previously accomplished using PW API in various ways. Have a look in the forums.
16. importX (Revolution): Import Pages CSV Module.
17. phpThumbOf: Thumbnail functionality is inbuilt in PW. There is also the Module Thumbnails that further extends this functionality.
18. getValue and getValues (Revolution): Inbuilt in PW. You can get the value of any Page using PW API - $page and $pages.
19. getFeed (Revolution): RSS Feed Loader Module.
20. MIGX (Revolution): Repeater Module which is part of the PW Core. It is not installed by default. You will need to do that yourself. For more info see this tutorial.
21. Articles (Revolution): See the Blog Profile Module.
22. NewsPublisher (Revolution): See below under Plugins.


MODX Ditto

[!Ditto? &parents=`5`&extenders=`summary` &tpl=`tplBlog` &orderBy=`createdon ASC`&display=`6` &truncText=`Continue Reading This Article` !]

PW equivalent

$items = $pages->get(5)->children("sort=date,limit=6");
foreach ($items as $item) {
         echo $item->title;
         echo $item->summary;//etc.
//this assumes you have a Field called summary on that Page

The first line in the above gives you all information about the 6 child Pages of the Page with ID #5. It returns an array. In other words, a basket of various documents containing all the info about those documents. Next, you traverse the array using foreach. In layman terms, you rummage through the basket picking goodies!

In order to wrap HTML around the code output, we do it like this instead (there’s other ways to do it as well!)

echo "<ul class='articles'>";
foreach($pages->get(5)->children("sort=date,limit=6") as $item) {
        echo "<li><p><a href='{$item->url}'>{$item->title}</a><br />
        <span class='summary'>{$item->summary}</span></p></li>";
echo "</ul>";

In this example, we have asked PW to grab the child Pages directly within the foreach rather than creating a variable $items first.

MODX getResources

// Output a list of child Resources of the current Resource, using the 'myRowTpl' chunk:
[[!getResources? &parents=`[[*id]]` &tpl=`myRowTpl`]]

PW equivalent

echo "<ul>";
foreach ($page->children as $child) {
         echo "<li><a href='{$child->url}'>{$child->title}</a></li>";
echo "</ul>";
//You might want to limit the number of child Pages you are getting if they are many!

MODX getResources

// Output the top 5 latest published Resources beneath the Resource with ID '5', with tpl 'blogPost':
[[!getResources? &parents=`5` &limit=`5` &tpl=`blogPost` &includeContent=`1`]]

PW equivalent

echo "<h3>Latest Posts</h3>";
$posts = $pages->get(5)->children("limit=5");
foreach ($posts as $post) {
         echo $post->body;

c. Modules

1. Batcher and Docmanager (Revolution): Batcher Module (covers most equivalent functions).
2. DocFinder (Evolution): Inbuilt in PW default install.

d. Plugins

1. QuickManager (Evolution): Several Modules can do this, i.e., Fredi, Page Frontend Edit and Inline Editor Modules.
2. ManagerManager (Evolution): Same functionality can be achieved using Templates, Access Control and the Modules Page Edit Field Permission, Page Edit Per User and Page Edit Per Role.

e. Chunks

See this example.
Btw, the PW Cheat Sheet is you best friend...
Hope this has been helpful!  O0

  • Like 43
Link to comment
Share on other sites

This looks great Kongondo!! Although I don't know anything about MODX, I will read it tomorrow in the morning in my Kobo reader :)

If someone else feels like, here is the ePub file that I created ;) (Apparently I can't upload .epub files to the forum, so I'm sharing in Google Drive) https://docs.google.com/file/d/0B6RyV62pA8iwTm1BTnNOYmRYRDA/edit?usp=sharing

  • Like 3
Link to comment
Share on other sites

Another sublime post that should be pinned both in a newbie channel and getting started. Thanks a lot kongondo for this great post. Coming from modx evo my self, I recognize a lot of what you are comparing between modx and pw. I still miss the great and fast dashboard of modx evo where everything is so well organized and accessable. But the raw untamed power of pw is so addictive.

Link to comment
Share on other sites

Just some random things I noticed.

Code: You may want to indent your code, use spaces. 

Settings: Apart from the config.php for common basic things, there's a lot of settings in modules. When I started I scanned through all modules that have settings (gear icon) and it's good to know they exists. Also for custom settings needed for your site, you can add to the config.php your own, or even use Pages to store and use them.

Example: $pages->find("selectors")->name_of_your_field; This is wrong and will not work as the find returns PageArray, so the getting the field won't work like this.

MarkupSimpleNavigation - I think I need a more sexy name for it... :D

Link to comment
Share on other sites

Thx Everyone!

@PWIred: You are tempting me to do a demo of how "easy" it is to convert an "Evo-like manager" to PW :lol: . I get you point though ;)

@Mary: Long time no see! Thx for comment.

@Soma: Selector: Corrected selector, thx; Indent code: Will do but the editor is driving me nuts! Keeps messing up code ;); Settings: Thx for info; MSN - yeah, with those initials, you probably want a better name! hehe. Probably, it also wrongly conveys the message that it can only do simple navigation...


  • Like 1
Link to comment
Share on other sites

Does PW have something to replace MODX Revolution's "Contexts"? I really love to work with contexts when using multilingual sites (or even 2 different sites sharing a single install for some reason). It's really easy to set the URL/language/other settings per context and have all pages of that context respect those settings automatically.

Link to comment
Share on other sites

For those who have moved from MODX to ProcessWire what would you say were the main benefits? Any drawbacks?

(maybe this should be a new thread?)

Yes, new thread please :)

Btw, the two questions have been answered various times in the forums. Outside the forums,  If you haven't seen it, this article by Marc sums it up for most(?) of us.

Link to comment
Share on other sites

What about Conditional Output modifiers.


In MODX I sometimes specify a field or content or a chunk based on the page ID or a TV being populated. I also use conditions such as

  • If this field is not empty, use this field else use this field
  • If this page id is X then show this html / field

How does PW handle this?

Link to comment
Share on other sites

What about Conditional Output modifiers.


In MODX I sometimes specify a field or content or a chunk based on the page ID or a TV being populated. I also use conditions such as

  • If this field is not empty, use this field else use this field
  • If this page id is X then show this html / field

How does PW handle this?

You can use plain PHP, so  there is no need for output filters in PW?

Link to comment
Share on other sites

@Kongondo: Brilliant review! You've covered pretty much all key points here. I remember reading your Wayfinder book, which was one of the first things I had read when I started with Evo in 2009. It was a good read, thank you for that!

@Jeroen: Soon after Revo came out I moved from MODx so don't know much about contexts. These topics may be relevant for you:

http://processwire.com/api/modules/multi-site-support/ - two ways of setting up multiple sites

http://processwire.com/talk/topic/1025-multisite/ - Apeisa's multisite module mentioned in above article

http://processwire.com/talk/topic/2979-multi-language-page-names-urls/ - new multi-languge names feature. Currently it's available only in dev branch and is not recommended for production yet. This new functionality makes possible to have unique URLs  for each language used by your site with no need of making separate tree for each language. All your multilingual content resides within one page using multi-linguage fields. When the page is viewed in the front-end the content is shown depending on the current language (which is detected by a languale-specific URL). Really excited about it! I think it's very close by its sense to "language contexts" in MODx you mentioned. API respects the context of the current language so it takes away the burden of taking care of language-related logic in your templates.

  • Like 2
Link to comment
Share on other sites


Welcome to PW! Other examples..

//template for this page
if ($page->id=='1001') {
    include ("./views/myTemplate.inc");

// field empty
$myField = $page->nameOfField;
if ($myField) {//checks if this field is not empty
//do stuff if not empty
else {
// do something else if empty

// another field empty example
if(count($page->images)) {// check if this page has images

   $images = $page->images->find('sort=name');

   foreach ($images as $image) {
	     echo "<figure class='left'>
             <img src='{$image->url}' alt='{$image->description}' />

Link to comment
Share on other sites

For those who have moved from MODX to ProcessWire what would you say were the main benefits? Any drawbacks?

at thingstodo. Actually i'm sure this has been told somewhere else in the forum already. Main benefit = there are almost no abstraction layers in pw and so at almost code level you are free to combine html + php + api + templates in any way you want, lots of power and speed here. Finally a hardcore cms that gives the kick.  Drawback = html/css/photoshop designers can no longer escape coding like they could with modx evo. Over Time modx evo has attracted a large group of designers with little to none coding skills because modx evo was perfect for them (and me) This is not really a drawback but you have to invest time in learning to think in code building parts of your website, output, functionality and practise php and api.

Link to comment
Share on other sites

Great guide Kongondo! Beyond being helpful to people familiar with MODX, I also think this guide would be helpful even to people that don't know MODX, as it introduces many ProcessWire concepts very well. Also, I'm not particularly familiar with MODX, so found it helpful in learning about MODX too. :) Thanks for your great work here. 

  • Like 2
Link to comment
Share on other sites

Exactly my thoughts Ryan, it's sort of a comparison, MODX did it this way, and with PW we can do it that way.

It's sort of a roadmap from Point A to Point B for both systems, just follow the learning curve.  ^-^

Link to comment
Share on other sites

  • 3 weeks later...
Thanks for this Tutorial. 

This is the thread that got me  finally to take a closer look on PW. 

Meanwhile, I have been reading various posts and tutorials.

I am impressed how easy and fast i get  on PW, because im not really a hardcore php coder.  ;)

But easier than I imagined it in Processwire. 

I think I've found my new favorite CMS and say thank you MODx for the nice years of cowork but I have to move on.

  • Like 4
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By MoritzLost
      I've seen a couple of questions regarding namespaces and autoloading floating around the forum recently, so I decided to write a little tutorial. In general, I often see people getting confused when they try to wrap their head around namespaces, autoloading, Composer and the mapping of namespaces to directory structures all at once. In fact, those are very much independent, distinct concepts, and it is much easier to explain and understand them separately. So this guide is structured as follows:
      How namespaces work in PHP. How autoloading works in PHP. Conventions for mapping namespaces to directory structures: PSR-4. How autoloading works in Composer and ProcessWire's class loader. How to use the class loader in a ProcessWire module. Feel free to skip the sections you're already familiar with.
      Namespaces in PHP
      The purpose of namespaces in PHP is to avoid naming conflicts between classes, functions and constants, especially when you're using external libraries and frameworks. Nothing more. It's important to understand that this has nothing at all to do with autoloading, directory structures or file names. You can put namespaced stuff everywhere you want. You can even have multiple namespaces inside a single file (don't try this at home). Namespaces only exist to be able to use a generic name – for example, ProcessWire's Config class – multiple times in different contexts without getting a naming conflict. Without namespaces, I couldn't use any library that includes a Config class of it's own, because that name is already taken. With namespaces, you can have a distinction between the classes ProcessWire\Config and MoritzLost\Config. You can also use sub-namespaces to further segregate your code into logical groups. For example, I can have two classes MoritzLost\Frontend\Config and MoritzLost\Backend\Config– a class name only needs to be unique within it's namespace.
      You can declare the namespace for a PHP file using the namespace statement at the top:
      // file-one.php <?php namespace ProcessWire; // file-two.php <?php namespace MoritzLost\Frontend; This way, all classes, methods and constants defined inside this file are placed in that namespace. All ProcessWire classes live in the ProcessWire namespace.
      Now to use one of those classes – for example, to instantiate it – you have a couple of options. You can either use it's fully qualified class name or import it into the current namespace. Also, if you are inside a namespaced file, any reference to a class is relative to that namespace. Unless it starts with a backward slash, in this case it's relative to the global namespace. So all of those examples are equivalent:
      // example-one.php <?php namespace ProcessWire; $page = new Page(); // example-two.php <?php use ProcessWire\Page; $page = new Page(); // example-three.php <?php $page = new ProcessWire\Page(); // example-four.php <?php namespace MoritzLost\Somewhere\Over\The\Rainbow; $page = new \ProcessWire\Page(); The use statement in the second example can be read like this: “Inside this file, all references to Page refer to the class \ProcessWire\Page”
      How autoloading works
      Every PHP program starts with one entry file – for ProcessWire, that's usually it's index.php. But you don't want to keep all your code in one file, that would get out of hand quickly. Once you start to split your code into several individual files however, you have to take care of manually including them with require or include calls. That becomes very tedious as well. The purpose of autoloading is to be able to add new code in new files without having to import them manually. This, again, has nothing to do with namespaces, not even something with file locations. Autoloading is a pretty simple concept: If you try to use a class that hasn't been loaded yet, PHP calls upon it's registered autoloaders as a last-ditch attempt to load them before throwing an exception.
      Let's look at a simple example:
      // classes.php <?php class A { /** class stuff */ } class B { /** class stuff */ } // index.php <?php spl_autoload_register(function ($class) { include_once 'classes.php'; }); new A(); new B(); This is a complete and functional autoloader. If you don't believe me, go ahead and save those two files (classes.php and index.php) and run the index.php with php -f index.php. Then comment out the include_once call and run it again, then you'll get an error that class A was not found.
      Now here's what happens when index.php is executed (with the autoloader active):
      Our anonymous function is added to the autoload queue through spl_autoload_register. PHP tries to instantiate class A, but can't because it's not loaded yet. If there was no autoloader registered, the program would die with a fatal error at this point. But since there is an autoloader ... The autoloader is called. Our autoloader includes classes.php with the class definition. That was a close one! Since the class has been loaded, execution goes back to the index.php which can now proceed to instantiate A and B. If the class was still not loaded at this point, PHP would go back to the original plan and die. One thing to note is that the autoloader will only be called once in this example. That's because both A and B are in the same file and that file is included during the first call to the autoloader. Autoloading works on files, not on classes!
      The important takeaway is that PHP doesn't know if the autoloader knows where to find the class it asks for or, if there are multiple autoloader, which one can load it. PHP just calls each registered autoloader in turn and checks if the class has been loaded after each one. If the class still isn't loaded after the last autoloader is done, it's error time.
      What the autoloader actually does is pretty much wild wild west as well. It takes the name of the class PHP is trying to load as an argument, but it doesn't have to do anything with it. Our autoloader ignores it entirely. Instead, it just includes classes.php and says to itself “My job here is done”. If class A was in another file, it wouldn't have worked.
      This process has two main advantages:
      Since autoloaders are only called on-demand to load classes just in time, we only include the files we actually need. If in the example above class A and B are not used in some scenarios, the classes.php will not be included, which will result in better performance for larger projects (though this isn't as cut and dry, since autoloading has it's own overhead, so if you load most classes anyway during a single request, it will actually be less efficient). If the autoloader is smart enough to somehow map class names to the files they're located in, we can just let the autoloader handle including the classes we need, without having to worry about jamming include statements everywhere. That brings us to ... PSR-4, namespaces and directory structures
      As you see, namespaces and autoloading are both pretty limited concepts. And they aren't inherently linked to each other. You can namespace your classes without ever adding an autoloader, and you can autoload classes that are all in the same namespace. But they become useful when you put them together. At the core of all that autoloading talk is a simple idea: By putting classes in files named after their class names, and putting those files in directory hierarchies based on the namespace hierarchy, the autoloader can efficiently find and load those files based on the namespace. All it needs is a list of root namespaces with their corresponding directories.
      The exact way class names and namespaces are mapped to directory structures and file names is purely conventional. The accepted convention for this is PSR-4. This is a super simple standard which basically just sums up the ideas above:
      A base namespace is mapped to a specific directory in the file system. When the autoloader is asked to load a class in that namespace (or a sub-namespace of it), it starts looking in that folder. This "base" namespace may include multiple parts – for example, I could use MoritzLost\MyAwesomeLibrary as a base and map that to my source directory. PSR-4 calls this a "namespace prefix". Each sub-namespace corresponds to a sub-directory. So by looking at the namespace, you can follow subdirectories to the location where you expect to find the class file. Finally, the class name is mapped directly to the file name. So MyCoolClass needs to be put inside MyCoolClass.php. This all sounds simple and straightforward - and it absolutely is! It's only once you mash everything together, mix up language features, accepted conventions and proprietary implementations like Composer on top that it becomes hard to grasp in one go.
      Composer and ProcessWire's class loader
      Now all that's left is to talk about how Composer and ProcessWire provide autoloading.
      Composer, of course, is primarily a tool for dependency management. But because most libraries use namespaces and most developers want to have the libraries they're using autoloaded, those topics become a prerequisite to understanding what Composer does in this regard. Composer can use different autoloading mechanisms; for example, you can just give it a static list of files to include for every request, or use the older PSR-0 standard. But most modern libraries use PSR-4 to autoload classes. So all Composer needs to function is a mapping of namespace prefixes to directories. Each library maintains this mapping for it's PSR-4-structured classes through the autoload information in their composer.json. You can do this for your own site to: Just include the autoload information as shown in the documentation and point it to the directory of your class files.
      Composer collects all that information and uses it to generate a custom file at vendor/autoload.php — that's the one you need to include somewhere whenever you set up Composer in one of your projects. Bells and whistles aside, this file just registers an autoloader function that will use all the information collected from your own and your included libraries' composer.json to locate and include class files on demand.
      You can read more about how to optimize Composer's autoloader for production usage here. If you want to read up on how to set up Composer for your own sites, read my ProcessWire + Composer integration guide instead.
      And finally, what does ProcessWire do to handle all this? Turns out, ProcessWire has it's own autoloader implementation that is more or less PSR-4 compliant. You can access it as an API variable ($classLoader or wire('classLoader'), depending on context). Instead of using a static configuration file like Composer, the namespace -> directory mapping is added during the runtime by calling $classLoader->addNamespace. As you would expect, this function accepts a namespace and a directory path. You can use this to register your own custom namespaces. Alternatively, if you have site-specific classes within the ProcessWire namespace, you can just add their location to the class loader using the same method: $classLoader->addNamespace('ProcessWire', '/path/to/your/classes/').
      Utilizing custom namespaces and autoloading in ProcessWire modules
      Now as a final remark, I wanted to give an example of how to use custom namespaces and the class loader in your own modules. I'll use my TrelloWire module as an example:
      Decide what namespace you're going to use. The main module file should live in the ProcessWire namespace, but if you have other classes in your module, they can and should use a custom namespace to avoid collisions with other modules. TrelloWire uses ProcessWire\TrelloWire, but you can also use something outside the ProcessWire namespace. You need to make sure to add the namespace to the class loader as early as possible. If either you or a user of your module tries to instantiate one of your custom classes before that, it will fail. Good places to start are the constructor of your main module file, or their init or ready methods. Here's a complete example. The module uses only one custom namespaced class: ProcessWire\TrelloWire\TrelloWireApi, located in the src/ directory of the module. But with this setup, I can add more classes whenever I need without having to modify anything else.
      /** * The constructor registers the TrelloWire namespace used by this module. */ public function __construct() { $namespace = 'ProcessWire\\TrelloWire'; $classLoader = $this->wire('classLoader'); if (!$classLoader->hasNamespace($namespace)) { $srcPath = $this->wire('config')->paths->get($this) . 'src/'; $classLoader->addNamespace($namespace, $srcPath); } } Source
      Thanks for making it through to the very end! I gotta learn to keep those things short. Anyway, I hope this clears up some questions about namespaces and autoloading. Let me know if I got something wrong, and feel free to add your own tips and tricks!
    • By MoritzLost
      Hi everyone, I'm happy to present my latest project, which is a collection of guides and tutorials for web development with ProcessWire written by me.
      What is this?
      I have written several tutorials in this forum, and I wanted a central place to collect all my tutorials and put them in a logical order. processwire.dev is exactly that, a curated list of tutorials for different topics related to ProcessWire development. I have revised, updated and expanded most of my existing tutorials. There are also some completely new tutorials.
      Notable topics
      How to integrate Composer in your ProcessWire sites, and a general explainer for namespaces and autoloading. A two-part guide to using Twig with ProcessWire and adding custom functionality. How to create flexible content modules with Repeater Matrix fields and Twig. A general guide to performance optimization for ProcessWire. A starter guide for the "ProcessWire mindset" - how to structure your content. ... and much more! What's next?
      I hope this will be a useful resource to all of you fine people. Please note that these tutorials are very much opinionated, and they reflect my personal experience and development practices. So if you disagree with some of my conclusions, that's perfectly fine! I'm happy to discuss all my recommendations and approaches with you, so let me know if you have any feedback, suggestions or error corrections!
      I plan to expand this resource over time and already have some new topics planned. If you have suggestions for new topics, go ahead and post them here as well!
      Start reading now: processwire.dev
    • By Ivan Gretsky
      Most of us know and use site/config-dev.php file. If present, it is used instead of site/config.php, so it is easy to set database connection and debug mode for local development, not touching the production config. It is also very useful when working with git. You can simply ignore it in the .gitignore file, so local settings won’t end up in the repo.
      But sometimes you need to add code to site/ready.php or site/init.php just for the dev environment. For example, to add ryan’s super cool on demand images mirrorer. I can’t live without it when working with big sites, which have more assets then I want to download to my desktop.
      It would be great if there was something like site/ready-dev.php for this. Not out-of-the-box, but it’s pretty easy to achieve. Unlike site/config-dev.php, site/ready.php is not hardcoded. It’s name is set with a special config setting:
      // wire/config.php $config->statusFiles = array( 'boot' => '', 'initBefore' => '', 'init' => 'init.php', 'readyBefore' => '', 'ready' => 'ready.php', 'readySite' => '', 'readyAdmin' => '', 'render' => '', 'download' => '', 'finished' => 'finished.php', 'failed' => '', ); As you can see, we can not only define, which files are loaded on init, ready and finished runtime states, but probably even add more if we need to.
      So we override this setting in site/config-dev.php like this:
      // site/config-dev.php // Change ready.php to ready-dev.php $temp = $config->statusFiles; $temp['ready'] = 'ready-dev.php'; $config->statusFiles = $temp; For some reason we can’t just do
      $config->statusFiles['ready'] = 'ready-dev.php'; and have to override the whole array. Maybe you PHP gurus can explain this in the comments.
      Now we can create the site/ready-dev.php file and place all the dev-only code there. Important thing is to include the main site/ready.php.
      // site/ready-dev.php include 'ready.php'; // DEV HOOK TO MIRROR ASSETS ON DEMAND $wire->addHookAfter('Pagefile::url, Pagefile::filename', function($event) { $config = $event->wire('config'); $file = $event->return; if($event->method == 'url') { // convert url to disk path $file = $config->paths->root . substr($file, strlen($config->urls->root)); } if(!file_exists($file)) { // download file from source if it doesn't exist here $src = 'https://mysite.com/site/assets/files/'; $url = str_replace($config->paths->files, $src, $file); $http = new WireHttp(); try { $http->download($url, $file); } catch (\Exception $e) { bd($file, "Missing file"); } } }); Do not forget to replace "mysite.com" if you’re copypasting this))
      Now, add the newly created file to the `.gitignore` and we’re done.
      # .gitignore # Ignore dev files site/config-dev.php site/ready-dev.php Thanks for reading!
    • By horst
      Change Default Language to be None-English | Walk Trough
      When you start a new (single) language site and the default language shouldn't be English, you can change it this way:
      Go to the modules core section:

      Select the Language ones by the filter function:

      We have four language related modules here, but for a single language site in none english,
      we only need the base module, named "Languages Support". So go on and install it.

      After that, you can leave it, ... 

      ... and switch to the newly created Language section under SETUP:

      Select the default language

      Enter your new language name or its Shortcut and save the page.
      I will use DE for a single language site in german here as example:

      Now I go to the ProcessWire online modules directory, down to the subsection for language packs and select
      and download my desired (german) one: 




      After downloading a lang pack as ZIP, I go back into my SETUP > LANGUAGES > default language page in admin,
      select the downloaded lang pack ZIP and install it:



      After the ZIP is uploaded, the files are extracted and installed, most of my screen is already in
      the new default language. To get all fully switched, we save and leave that page, ...

      ... and completely logout from the admin.

      Now, of course, we directly login back, ...

      ... and see, that now also the cached parts of the admin have switched to the new default language. 🙂

      That was it for a single language site in none english.
      If you want to have a multi language site, just add more languages to the SETUP > LANGUAGES section.
      When using a multi language site, I think you also want to use multi language input fields, and maybe different page names for your language page pendents. If so, you need to go into MODULES > CORE > filter LANGUAGE and install what you need or want to use of it, (if not already done).
      Thanks for reading and happy coding, 🙂
    • By cosmicsafari
      Hi all,
      I have been asked by a client whether we can setup load balancing for their existing Processwire site.
      From my investigations on Google and within these forums, it definitely seems possible but as a newbie with a basic understanding of the subject im a bit lost.
      Does anyone know of any existing tutorials for settings up load balancing with PW?
      What items would need to be changed on their current stand alone install, is there a list of best practices worth consulting etc?
      As I understand it we would need to have some sort of copying mechanism (rsync script most likely) in order to make sure any uploaded assets are shared between the main server and the fallback ones, other than that im not sure what else would need to be ammended.
      Any thoughts/help would be greatly appreciated.
  • Create New...