Refactoring a simple PHP application MODx

Posted on Sep 02, 2011 by James Rotering
PHP Skill Level: medium
MODx Skill Level: beginner

Description: this guide shows how to convert to MODx Revolution file with mixed PHP/HTML. It is for people who are comfortable working with PHP, but they are still learning the basics of MODx.

If You fit most of the following descriptions, this guide should be useful to You:
— You write scripts that combine PHP code and HTML
— You understand loops and arrays PHP
You wrote PHP code that connects to database and retrieves records
— You have a General idea of how MODx snippets and other elements

For example, I will consider a simplified version of a real project that I recently finished.
/ > The application was developed in PHP/HTML and do the following:
— gets the list of records from a database table
— displays the records in an HTML table
— displays two drop-down selection boxes to filter the results by certain column values.

If You are interested, the application You can see here.

the

Start with a mixed PHP/HTML file



The source code for this application looked like this (simplified for clarity):

File: acctCodes.php (original version)
the
<html> 
<head> 
<title>account Numbers and definitions of SFS</title> 
</head> 
<body> 
<h1>account Numbers and definitions of SFS</title> 
<form> 
<!-- First SELECT box --> 
<div> 
<label for="byCategory">Search:</label> 
<select name="byCategory" id="byCategory"> 
<option>Select category</option>
 <?php 
// some code connects to the database 
$values = // some code executes a query to retrieve values from the first drop-down list 
foreach ($values as $value) { 
echo '<option value="' . $value . '">' . $value . '</option>'; 
} 
?>
 </select> 
</div> 
<!-- The second SELECT box --> 
<div> 
<label for="byType">Search by type:</label> 
<select name="byType" id="byType"> 
<option>Select a type</option>
 <?php 
$values = // some code executes a query to retrieve values from the second drop-down list 
foreach ($values as $value) { 
echo '<option value="' . $value . '">' . $value . '</option>'; 
} 
?>
 </select> 
</div> 
</form> 
<!-- Table of account numbers --> 
<table id="acctCodes"> 
<thead> 
<!-- You know how to look up cell <th>. I'm oversimplifying here --> 
</thead> 
<tbody>
 <?php 
$records = // some code executes a query to retrieve records for a table 
foreach ($records as $record) { 
?>
 <tr> 
<td><?php echo $record['category']; ?></td> 
<td><?php echo $record['status']; ?></td> 
<td><?php echo $record['account']; ?></td> 
<td><?php echo $record['acctType']; ?></td> 
<td><?php echo $record['title']; ?></td> 
<td><?php echo $record['definition']; ?></td> 
</tr>
 <?php 
} 
?>
 </tbody> 
</table> 
</body> 
</html>

PHP is quite simple. There are three separate block of code where the query is executed, then the results wrapped in the appropriate HTML tags and displayed on the page. The third block of some fancy PHP code is interrupted, so that inside the loop, use a normal HTML code, and not bulky unpleasant operators "echo".
But nothing terribly complicated process.

However, we will have to do some cleaning to prepare for the transition to MODx.

the

Part one: cleaning (First get the values and then output)



The first step to cleaning this PHP code for MODx (and in General) is avoiding mixing of application logic with the markup of the page. Instead of executing each query before to use it, we start all requests and then just output the results using the echo command.
The result is something like this (again, very simplified for clarity):

File: acctCodes.php (purified version)
the
<?php 
// some code connects to the database 
$values = // some code executes a query to retrieve values from the first drop-down list 
$options1 = "; 
foreach ($values as $value) { 
$options1 .= '<option value="' . $value . '">' . $value . '</option>'; 
} 
$values = // some code executes a query to retrieve values from the second drop-down list 
$options2 = "; 
foreach ($values as $value) { 
$options2 .= '<option value="' . $value . '">' . $value . '</option>'; 
} 
$records = // some code executes a query to retrieve records for a table 
$trrows = "; 
foreach ($records as $record) { 
$trrows .= '<tr><td>' . $record['category'] . 
'</td><td>' . $record['status'] . 
'</td><td>' . $record['account'] . 
'</td><td>' . $record['acctType'] . 
'</td><td>' . $record['title'] . 
'</td><td>' . $record['definition'] . 
'</td></tr>'; 
} 
?>
<html> 
<head> 
<title>account Numbers and definitions of SFS</title> 
</head> 
<body> 
<h1>account Numbers and definitions of SFS</h1> 
<form> 
<!-- First SELECT box --> 
<div> 
<label for="byCategory">Search:</label> 
<select name="byCategory" id="byCategory"> 
<option>Select category</option> 
<?php echo $options1; ?> 
</select> 
</div> 
<!-- The second SELECT box --> 
<div> 
<label for="byType">Search by type:</label> 
<<font color="#006699">select name="byType" id="byType"> 
<option>Select a type</option> 
<?php echo $options2; ?> 
</select> 
</div> 
</form> 
<!-- Table of account numbers --> 
<table id="acctCodes"> 
<thead> 
<!-- You know how to look up cell <th>. I'm oversimplifying here --> 
</thead> 
<tbody> 
<?php echo $trrows; ?> 
</tbody> 
</table> 
</body> 
</html>


This makes the markup much easier for viewing and editing. All PHP code is contained in four blocks: the first query sets the values, and the following three simple commands echo output these values to the screen.
It is more acceptable to the transfer of this small web application in MODx.

the

Part two: Snippets and placeholders



To bring our cleaned code into MODx, we need to make some additional changes.
Our HTML code you can just copy to a MODx resource. (more Resource will not change! — approx. TRANS.)
But the PHP code we need to replace the snippets and some placeholders.

The content of our site MODx will look as follows:
Resource MODx: 'acctCodes'
the
[[accountCodes]] 
<html> 
<head> 
<title>account Numbers and definitions of SFS</title> 
</head> 
<body> 
<h1>account Numbers and definitions of SFS</h1> 
<form> 
<!-- First SELECT box --> 
<div> 
<label for="byCategory">Search:</label> 
<select name="byCategory" id="byCategory"> 
<option>Select category</option> 
[[+options1]] 
</select> 
</div> 
<!-- The second SELECT box --> 
<div> 
<label for="byType">Search by type:</label> 
<select name="byType" id="byType"> 
<option>Select a type</option> 
[[+options2]] 
</select> 
</div> 
</form> 
<!-- Table of account numbers --> 
<table id="acctCodes"> 
<thead> 
<!-- You know how to look up cell <th>. I'm oversimplifying here --> 
</thead> 
<tbody> 
[[+trrows]] 
</tbody> 
</table> 
</body> 
</html>

In the four blocks of PHP code has been replaced with MODx tags. First, [[accountCodes]], will cause the snippet accountCodes (which we haven't written yet). The following three tag placeholders that are replaced with values that have been established by our snippet.
Now we need to write this snippet

the

Part three: the Snippet



Our snippet "accountCodes" will contain the code from the first block of PHP query and loop. The only thing we are going to add a few lines of code MODx API to create the placeholders.
the Snippet: "accountCodes"
the
<?php 
// some code connects to the database 
$values = // some code executes a query to retrieve values from the first drop-down list 
$options1 = "; 
foreach ($values as $value) { 
$options1 .='<option value="' . $value . '">' . $value . '</option>'; 
} 
$values = // some code executes a query to retrieve values from the second drop-down list 
$options2 = "; 
foreach ($values as $value) { 
$options2 .= '<option value="' . $value . '">' . $value . '</option>'; 
} 
$records = // some code executes a query to retrieve records for a table 
$trrows = "; 
foreach ($records as $record) { 
$trrows .= '<tr><td>' . $record['category'] . 
&nbsp; '</td><td>' . $record['status'] . 
'</td><td>' . $record['account'] . 
'</td><td>' . $record['acctType'] . 
'</td><td>' . $record['title'] . 
'</td><td>' . $record['definition'] . 
'</td></tr>'; 
} 
$modx- > setPlaceholder('options1', $options1); 
$modx- > setPlaceholder('options2', $options2); 
$modx- > setPlaceholder('trrows', $trrows);


This is our snippet. Compared to our original PHP code, it was amended by three lines "setPlaceholder" at the bottom. They just tell MODx that if and when he will meet below on the page placeholder [[+options1]], it should replace the value $options1. The placeholders is a simple and elegant way to handle the output values from the snippets.

Now we've done everything we need to have the web application running inside MODx. However, we can take a few extra steps that will make our app much hotter, using more API functions to MODx. I would rate our current implementation as "satisfactory" (original "C". Let's continue the work.

the

Part four: Using chunks as output templates



One of the criticisms that can be made to our current PHP code is that it continues to mix markup with logic. Our series of 'foreach' still contains pieces of HTML markup:
the
foreach ($values as $value) { 
$options1 .= '<option value="' . $value . '">' . $value . '/option>'; 
}

This strategy is far from ideal: suppose the boss asks for a HTML coder Skippy (orig. — "Skippy the HTML code monkey" — approx. TRANS.) to change the HTML code of this app, adding a few additional attributes to each tag <option>. But we don't want Skippy ("Skippy the HTML code monkey") was somewhere near our PHP script, right?

There are several ways to correct this situation in PHP comes to mind is pulling the HTML code from external files and replace strings. However, we do not need to worry and find out a suitable solution. For this task, MODx offers more simple and elegant solution: chunks with placeholders.

Code option from the above snippet could be rewritten in the form of a chunk, named "option":

chunk: "option"
the
<option value="[[+value]]" > [[+value]]</option>

This chunk is nothing special — a bit of HTML with a couple of placeholders. "Option" — simple, useful chunk that can be used anywhere — a few snippets in different resources.

Now we can change our snippet by adding a little more MODx API code to use the value of this chunk in our loop:
the
<?php 
// some code connects to the database 
$values = // some code executes a query to retrieve values from the first drop-down list 
$options1 = "; 
foreach ($values as $value) { 
$modx- > setPlaceholder('value', $value); 
$options1 .= $modx- > getChunk('option'); 
}

Now our cycle creates a placeholder for the $value and adds the value of the chunk 'option' in the output string.
This allows you to separate all HTML code from PHP. If Skippy ("Skippy the HTML code monkey") you must add the attribute to the elements <option> he can do it by editing the chunk option. (Just tell him not to touch the placeholders!)

Table rows can be done similarly:
chunk: "trrows"
the
<tr> 
<td>[[+category]]</td> 

<td>[[+account]]</td> 
<td>[[+acctType]]</td> 
<td>[[+title]]</td> 
<td>[[+definition]]</td> 
</tr>

and the PHP code to call it
the
each ($records as $record) { 
$trrows .= $modx- > getChunk('trrows', $record); 
}

Note that in this example, we don't even have to install any placeholders!
An associative array $record we can pass in GetChunk() as argument, and the placeholders will automatically be** found for each pair key/value in the array!
(**MODX Awesomeness! You're soaking in it!**) (Analogy with the slogan of some washing powder? — approx. TRANS.)

So now we can keep all our HTML chunks in the chunk and rewrite our snippet here:
the
<?php 
// some code connects to the database 
$values = // some code executes a query to retrieve values from the first drop-down list 
$options1 = "; 
foreach ($values as $value) { 
$modx- > setPlaceholder('value', $value); 
$options1 .= $modx- > getChunk('option'); 
} 
$values = // some code executes a query to retrieve values from the second drop-down list 
$options2 = "; 
foreach ($values as $value) { 
$modx- > setPlaceholder('value', $value); 
$options2 .= $modx- > getChunk('option'); 
} 

$records = // some code executes a query to retrieve records for a table 
$trrows = "; 
foreach ($records as $record) { 
$trrows .= $modx- > getChunk('trrows', $record); 
} 
$modx- > setPlaceholder('options1', $options1); 
$modx- > setPlaceholder('options2', $options2); 
$modx- > setPlaceholder('trrows', $trrows);

It is already starting to look very good. We used MODx API for template output and our snippet looks really clean. However, there is one thing that bothers me and could be potentially problematic — we now have the MODx API calls introduction to the logic of our application. If we are 100% sure that we only ever need the data printed on this web page and in this format, it is really not a problem. But if there is a possibility that these data might need to use a different- somewhere outside of MODx, it would be better to keep the main application logic separate from the entity MODx.

At the moment the app receives a rating of "good" (original — "B"). Let's do the last step and raise our rating to "excellent" (original — "A").

the

Part five: the Separation of Your application logic



All we need to do for the last step is dividing the snippet in two parts. The first part will contain our app logic and return all the arrays as members of one big array.
This will be done in PHP, and should ideally be saved in a file on our server.

The second part is the snippet. He will first get the array returned by the logic file, and then uses chunks for the standardization of rows of the result, and finally will set the placeholders to display the results in the resources.

Here is how it all ends:

File: acctCodes.php
the
<?php 
// some code connects to the database 
$values = // some code executes a query to retrieve values from the first drop-down list 
$values2 = // some code executes a query to retrieve values from the second drop-down list 
$records = // some code executes a query to retrieve records for a table 
return array('values'= > $values, 
'values2'=>$values2, 
'records'=>$records); 
?>


the Snippet: 'acctCodes'
the
<?php 
$data = include_once(MY_INCLUDE_PATH . 'acctCodes.php'); 

$options1 = "; 
foreach ($data['values'] as $value) { 
$modx- > setPlaceholder('value', $value); 
$options1 .= $modx- > getChunk('option'); 
} 

$options2 = "; 
foreach ($data['values2'] as $value) { 
$modx- > setPlaceholder('value', $value); 
$options2 .= $modx- > getChunk('option'); 
} 

$trrows = "; 
foreach ($data['records'] as $record) { 
$trrows .= $modx- > getChunk('trrows', $record); 
} 

$modx- > setPlaceholder('options1', $options1); 
$modx- > setPlaceholder('options2', $options2); 
$modx- > setPlaceholder('trrows', $trrows);

Now our ducks in a row (Now we have our ducks in a row — approx. TRANS.). Let's look at what we have done:
— We pass the data directly (without whatsoever HTML markup) in our MODx snippet.
— We allow MODx to manage the standardization of our output records.
— We set all values of the placeholders in one snippet at the top of the resource.
— Where necessary, we output our data to the page using placeholders.

We have come a long way from our initial mixed PHP/HTML file, isn't it? I would estimate that the final implementation to "excellent" (original — "A"). However, someone probably has a better idea how to separate the different problem areas. But for connoisseurs PHP (orig. — PHP hacks — approx. TRANS.) like me, this is a good place to start.

I hope you have learned some of the key challenges when writing simple PHP applications for use in MODx. Feel free to leave any thoughts, questions or suggestions about this guide in the comments here.

Approx. lane — the result was created:
resource acctCodes (part 2)
— snippet accountCodes (part 5)
file acctCodes.php (part 5)
— chunk option (part 4)
— chunk trrows (part 4)
Article based on information from habrahabr.ru

Популярные сообщения из этого блога

Approval of WSUS updates: import, export, copy

The Hilbert curve vs. Z-order

Kaspersky Security Center — the fight for automation