Jump to content
kongondo

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

Recommended Posts

Welcome to PW creativ3minds!,

Thanks for your compliments. I am glad you've found the thread useful. Let us know if you need any help :)

Share this post


Link to post
Share on other sites

Hi Kongondo.

Coming from modX revo, I'm testing pWire's file field type, so I added this to output a link to a PDF from a file field type:

if ($page->pdf_docs_insert){ 
  echo "<a href='{$page->pdf_docs_insert->url}$page->pdf_docs_insert' target='_blank'> $page->pdf_docs_insert</a>"; 
} else {
// ...
}

The link is working but I can't output the description from the file field  pdf_docs_insert  I get the name of the PDF file but not the description of it. I tried several ways, but it doesn't seem to work like the description for image field types. This doesn't work too ->$file->description

Thank you again for your help and patience.

Share this post


Link to post
Share on other sites

Hi nfil,
 
Don't know whether I've said welcome to PW or not. If not, welcome :).
 
Are you allowing multiple or single file uploads on that field? 
 
If single...

if ($page->pdf_docs_insert){ 

	$myfile = $page->pdf_docs_insert;
	echo "<a href='{$myfile->url}' target='_blank'>$myfile->description</a>"; 
}
	
else {
// ...

}

If multiple files, the field returns an array, hence you need to loop through it...

if ($page->pdf_docs_insert){ 

	$myfiles = $page->pdf_docs_insert;
	
	foreach ($myfiles as $myfile) {
		echo "<a href='{$myfile->url}' target='_blank'>$myfile->description</a>";
	}
}

else {
// ...please give me some files mate!
}

Check out the cheatsheet for more info

  • Like 3

Share this post


Link to post
Share on other sites

Thank you for your welcome Kongondo.

You helped before, I'am glad don't remember my newbie questions bothering pWire experts. Your awesome answer helped me a lot to correct and understand the correct pWire syntax. I hope some other pWire newbies can learn too from my mistakes.

Check out the cheatsheet for more info  Bookmarked!!

Share this post


Link to post
Share on other sites

Ha! Now I have another ten pages to read, along with those 30 designer depot etc. articles piled up in my inbox and a couple of issues of .Net gathering dust under the bed. I'm actually working on ClipperCMS, a fork of Evo, now, and it's while researching something that cropped up during a write-up of its API that I found your post. I suspect it's what I needed some time last year to help me make sense of PW, since my brain has been rewired to view the world from an Evo standpoint. And now I'll have to read it and have another look. Pretty much like MODX, in fact - my first reaction to that was: "Oh no, another CMS that keeps everything in the database" (after finding Joomla site after Joomla site like watching paint dry as the page built up from bits & pieces). Something spurred me to have another look at MODX later, and I latched on to it immediately second time around. I'm pretty committed to Clipper, I think it has a great future for Evo fans, but I'm sure it's worth having a go at PW as well, and your guide could well give me the boost I need to do that in a reasonable time.

TVM!

:P  KP

  • Like 3

Share this post


Link to post
Share on other sites

Ha! Now I have another ten pages to read, along with those 30 designer depot etc. articles piled up in my inbox and a couple of issues of .Net gathering dust under the bed. I'm actually working on ClipperCMS, a fork of Evo, now, and it's while researching something that cropped up during a write-up of its API that I found your post. I suspect it's what I needed some time last year to help me make sense of PW, since my brain has been rewired to view the world from an Evo standpoint. And now I'll have to read it and have another look. Pretty much like MODX, in fact - my first reaction to that was: "Oh no, another CMS that keeps everything in the database" (after finding Joomla site after Joomla site like watching paint dry as the page built up from bits & pieces). Something spurred me to have another look at MODX later, and I latched on to it immediately second time around. I'm pretty committed to Clipper, I think it has a great future for Evo fans, but I'm sure it's worth having a go at PW as well, and your guide could well give me the boost I need to do that in a reasonable time.

TVM!

:P  KP

OMG! Keith is here!!! :) Welcome to PW and the forums! Nice to see you in these parts. I didn't know you went with Clipper...anyway, hope to see you here more :):D

  • Like 1

Share this post


Link to post
Share on other sites

Hi kp52 and welcome. Nice to read from you here ;)

the boost I need to do that in a reasonable time.

check this out for more boost.

COMING FROM MODX ?

Link2: A MODX refugee: questions on features of ProcessWire
http://processwire.c...of-processwire/

Link3: Code comparison between modx and processwire.

http://processwire.c...ge-2#entry30349

PROCESSWIRE CMS INSIGHTS

Link1: Very good case study from RayDale giving good insight in processwire
http://processwire.c...a-a-case-study/

Link2: Symphony or Processwire ? Another good insight.

http://getsymphony.c...s/thread/79645/

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

Find more boost in these useful collected posts here:

http://processwire.com/talk/topic/4173-grouped-forum-posts-links-articles-tutorials-code-snippets/

  • Like 2

Share this post


Link to post
Share on other sites
echo "<ul>";
foreach ($page->children as $child) {
         echo "<li><a href='{$child->url}'>{$child->title}</a></li>";
}
echo "</ul>";

How do I take this and make it so that I can get pull the children of an unpublished page? The site I'm working on is for a fairly large fitness center that offers multiple program/classes. My structuture is something like this:

-Program Overview

-Program Classes (unpublished to act as a container)

-Class One

-Class Two

-Class Three

-Program Instructors (unpublished to act as a container)

-Instructor One

-Instructor Two

-Instructor Three

 

Any insight is always appreciated. I'm still ironing out issues with the transition from ModX.

 

Share this post


Link to post
Share on other sites

This should do it assuming the current page is "Program Overview"

foreach ($page->children("include=all") as $child)
 
  • Like 1

Share this post


Link to post
Share on other sites

Thanks adrian. I gave it a shot but, that just pulls the container page or "Program Classes", not "Class one" etc... I want it to ignore "Program Classes" and just pull it's children.

Share this post


Link to post
Share on other sites

Oh, I understand now. Try this:

foreach ($page->children("include=all") as $child){
    foreach($child as $grandchild){
        echo $grandchild->title;
    }
}

However there are other ways that might be cleaner. Perhaps you could use a selector to find all those pages like this:

$grandchildren = $pages->find("template=template_name");

Or something along those lines, then foreach through $grandchildren

Or maybe:

$grandchildren = $pages->find("parent.template=template of the unpublished child pages"); 

So many options :)

  • Like 1

Share this post


Link to post
Share on other sites

Thanks adrian, but still no luck. I'd like to try one of the cleaner methods, but I'm still feeling in over my head on a lot of this.

Share this post


Link to post
Share on other sites

bodhic,

Can you show us the selectors exactly as you tried them? and also recreate your page tree again, this time with the names of the templates for each page in brackets after the names of the pages.

In case you don't already know about it, you should install Nik's awesome Selector Test module:

http://mods.pw/2r

It will help you test your selectors in a very quick way.

Share this post


Link to post
Share on other sites

-Program Classes (unpublished to act as a container)

-Class One

-Class Two

-Class Three

-Program Instructors (unpublished to act as a container)

-Instructor One

-Instructor Two

-Instructor Three

 

 

I suppose you don't need "Programm Classes" to be ever displayed, right? Then give theses container pages no template file at all. The child pages get their own template ("class_and_instructor", to give it a name for example) then, and now you can use

$childpages = $pages->find('template=class_and_instructor');

foreach($childpages as $child) {
echo $child->title;
}
 

Share this post


Link to post
Share on other sites

This is too blatant. 

In ModX I had a basic structure for my concept.

I knew what to do in the header chunk and the footer chunk the rest I've done in the content.

I figure it with Content and Template Variables. 

Im PW its much more complicated for me. its too flexible. you can do what you want and on every place. 

DAMN 

I have  really get used to it. :) 

  • Like 1

Share this post


Link to post
Share on other sites

In MODX there's an Extra called GetResources which allows you to loop through Resources and display the contents via a mini template (TPL).

For the PW equivalent, I use the foreach loop combined with selectors. Simple enough.

Back to getResources, I had a great setup whereby I could loop through several resources and output the content through a different TPL (mini template) depending on a field value within a Resource. IE I'd end up with a single page but the content inside would have several different layouts such as

Block 1 = Image on left. Text on right.

Blcok 2 = Image on right. Text on left.

Block 3 = Text on top. Image underneath.

Edit: There's a visual explanation here: http://www.sepiariver.ca/blog/modx-web/how-to-dynamic-content-blocks-with-modx-getresources/

Is this relatively simple with PHP and PW? I'm not looking for code samples but a more accurate idea of what to google. IE What's the proper terminology in PHP?

Share this post


Link to post
Share on other sites

@peter: take a look at InputFieldFile.

// usage with a Inputfield file with the name 'your_field':
$tpl = new TemplateFile($config->paths->templates . "includes/" . $page->your_field);
$tpl->set('current', $page);
$markup = $tpl->render(); 
You then can access al Page fields in the php file with $current, like $current->title or $current->id etc.
  • Like 2

Share this post


Link to post
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 concept, 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 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.
    • By Sten
      Hello,
      I'd like to participate to a documentation about the page flow in PW. I am still a beginner and still have difficulties to see the big picture but I could help to the writing.
      What is the flow when a page is displayed or created ? It would be fine to draw a scheme with every stage in which one can add hooks. Something to describe the flow in time.
      May be it exists.
      page
      |
      fields
      |
      template
      What do you think ?
×
×
  • Create New...