Jump to content

Help making code more modular


SamC
 Share

Recommended Posts

I've got two files. _search-form.php renders a search form (funny that!) and search.php renders the results. I have a few pre-requisites:

1) The search form select box for towns must be made up of only towns which exist in a field ($addressBuildingTown), on a building page (building-entry), in the system.
2) The values in the selectors must remain after submission, so I need to use a whitelist to GET the current values.

_search-form.php loads above search.php

1) _search-form.php sets up the values which are populated into the actual form (that the user sees), 
2) It also gets the values from the URL, sanitizes them (compares with an allowed list), then adds to a whitelist
3) The whitelist is used for when search.php is loaded so the select lists/checkboxes show the last selected values

The problems:

1) _search-form.php appears on the homepage. At this URL 'mysite.com' there are no values TO get in the first place which kicked up some errors about arrays being required by sanitizer, which leads onto 2...
2) I had to use 'if ($input->get->town)' so that part of code doesn't run when on a page which includes the search form, but has yet to be submitted. Is this not dodgy? The value used in this check is not sanitized.
3) search.php uses a variable called '$theSelector' which is constructed in _search.php.

The whitelist works though, even the checkboxes which I was pleased with, took a while to work out how to get an array of checkbox values into the whitelist and check a value against them.

So, my form 'works' but HAS to be above search.php or I get a bunch of errors which isn't very modular. What if I wanted to shift it to the bottom? if I did that, how would the '$theSelector' variable ever make it to search.php? Would just be undefined.

Anyway, here's the code so far:

// search-form.php
<?php namespace ProcessWire; ?>

<?php
    // get all buildings
    $buildings = $pages->find("template=building-entry");
    // get categories as pagearray of Ids sorted by title
    $buildingTypes = $pages->find("template=category-entry, sort=title");
    // modify to standard array of Ids
    $buildingTypes = $buildingTypes->getArray();
    //get unique towns into standard array
    $uniqueTowns = array_unique($buildings->explode("addressBuildingTown"));
    sort($uniqueTowns);

    // set up variables
    $minAreas = [
        500 => '500 sq ft',
        1000 => '1,000 sq ft',
        2000 => '2,000 sq ft',
        3000 => '3,000 sq ft',
        4000 => '4,000 sq ft',
        5000 => '5,000 sq ft',
        6000 => '6,000 sq ft',
        7000 => '7,000 sq ft',
        8000 => '8,000 sq ft',
        9000 => '9,000 sq ft',
        10000 => '10,000 sq ft',
        50000 => '50,000 sq ft'
        ];

    $maxAreas = [
        500 => '500 sq ft',
        1000 => '1,000 sq ft',
        2000 => '2,000 sq ft',
        3000 => '3,000 sq ft',
        4000 => '4,000 sq ft',
        5000 => '5,000 sq ft',
        6000 => '6,000 sq ft',
        7000 => '7,000 sq ft',
        8000 => '8,000 sq ft',
        9000 => '9,000 sq ft',
        10000 => '10,000 sq ft',
        60000 => '60,000 sq ft',
        100000 => '100,000 sq ft'
        ];

?>


<?php
    // catchall selector for empty fields
    $theSelector = "template=building-entry";

    // start building selectors
    $sorting = "sort=addressBuildingTown";
    $townSelector = "addressBuildingTown";
    $buildingTypeSelector = "category";
    $areaSelector = "buildingArea";


    // used to output 'filtered by' above results
    // unused at the moment
    $summary = [
        "towns" => "", 
        "type" => "",
        "min" => "", 
        "max" => "", 
        ];
    

    // Array to hold towns from GET
    $towns = [];
    
    // is this right?
    if ($input->get->town) {
        $allowedTowns = array_values($uniqueTowns);
        $towns = $sanitizer->options($input->get->town, $allowedTowns);
        $input->whitelist("towns", $towns);
    }

    // GET building type
    $allowedTypes = array_values($buildingTypes);
    $buildingType = $sanitizer->option($input->get->type, $allowedTypes);
    $input->whitelist("buildingType", $buildingType);

    // GET (sanitized) min area and add to whitelist
    $allowedMinAreas = array_keys($minAreas);
    $minArea = $sanitizer->option($input->get->minArea, $allowedMinAreas);
    $input->whitelist("minArea", $minArea);

    // GET max area
    $allowedMaxAreas = array_keys($maxAreas);
    $maxArea = $sanitizer->option($input->get->maxArea, $allowedMaxAreas);
    $input->whitelist("maxArea", $maxArea);

    // if a town has been selected
    if ($towns) {
        $townSelectorPart = "";
        
        foreach ($towns as $town) {
            $townSelectorPart .= $town . "|";
        }

        $townSelectorPart = rtrim($townSelectorPart, "|");
        $summary["towns"] = $townSelectorPart;
        $theSelector .= "," . $townSelector . "=" . $townSelectorPart;
    }

    // if a building type has been selected
    if ($buildingType) {
        $summary["type"] = $buildingType;
        $theSelector .= "," . $buildingTypeSelector . "=" . $buildingType;
    }

    // if min or max area has been selected
    if ($minArea || $maxArea) {
        // if min area selected
        if ($minArea) {
            $summary["min"] = $minArea;
            $minAreaSelectorPart = $areaSelector . ">={$minArea}";
            $theSelector .= "," . $minAreaSelectorPart;
        }

        // if max area selected
        if ($maxArea) {
            $summary["max"] = $maxArea;
            $maxAreaSelectorPart = $areaSelector . "<={$maxArea}";
            $theSelector .= "," . $maxAreaSelectorPart;
        }
    }

?>

// render the actual form
<form id='building_search' method='get' action='<?php echo $config->urls->root?>search/'>

    <h3>Building search</h3>
    
    <p>
        <label for='search_town'>Filter by town</label><br>
            <?php
                foreach ($uniqueTowns as $town) {
                    $selected = "";
                    if ($input->get->town) {
                        $selected = $town == in_array($town, $input->whitelist("towns"))  ? " checked" : '';
                    }
                    echo "<input type='checkbox' name='town[]' value='{$town}'{$selected}>{$town}<br>";
                }
            ?>
    </p>

    <p>
        <label for='search_type'>Building type</label>
        <select id='search_type' name='type'>
            <option value=''>Any</option>

            <?php
                foreach ($buildingTypes as $type) {
                    $selected = $type == $input->whitelist("buildingType")  ? "selected='selected'" : '';
                    echo "<option {$selected} value='{$type}'>{$type->title}</option>";            
                }
            ?>
        </select>
    </p>


    <p>
    <label for='search_min_area'>Min area</label>
    <select id='search_min_area' name='minArea'>
        <option value=''>No min</option>

        <?php 
            foreach($minAreas as $value => $label) {
                $selected = $value == $input->whitelist("minArea") ? "selected='selected'" : '';
                echo "<option {$selected} value='{$value}'>{$label}</option>";
            }
        ?>

    </select>
    </p>


    <p>
    <label for='search_max_area'>Max area</label>
    <select id='search_max_area' name='maxArea'>
        <option value=''>No max</option>

        <?php 
            foreach($maxAreas as $value => $label) {
                $selected = $value == $input->whitelist("maxArea") ? "selected='selected'" : '';
                echo "<option {$selected} value='{$value}'>{$label}</option>";
            }
        ?>

    </select>
    </p>


    <p><button type='submit' id='search_submit' name='submit' value='1'>Search</button></</p>

</form>

// search.php
<?php namespace ProcessWire; ?>

<?php
    $buildings = $pages->find($theSelector . "," . $sorting);
    foreach ($buildings as $building):
?>

<h2><?php echo $building->title; ?></h2>

<?php if ($building->addressBuildingName): ?>
    <p>BUILDING NAME: <?php echo $building->addressBuildingName; ?></p>
<?php endif; ?>

<p>ADDRESS: 
<?php echo $building->addressBuildingNumber . ' ' . $building->addressBuildingStreet . ', '; ?>
<a href='#'><?php echo $building->addressBuildingTown; ?></a>
</p>

// URL segments are next on the todo list for the following
<p>TYPE: <a href="<?php echo $building->category->url; ?>"><?php echo $building->category->title; ?></a></p>

<p>AREA: <?php echo $building->buildingArea; ?></p>

<?php echo $building->buidingDescription; ?>

<?php endforeach; ?>

Hope I'm making sense here. Thanks for any advice.

Link to comment
Share on other sites

Looks mostly good to me.

My thoughts...

If I'm understanding your code correctly (it would be clearer if you used a separate forum code block for each separate file) I think the code that builds the selector would be better placed in your search template (search.php) rather than in your search-form.php include. I think this would solve some of your concerns, and also allows you to create links to the search results page that don't come via the search form (e.g. suppose you had a page that listed a link for each town to show results for that town).

If your search form appears on the search results page you just need to make sure the code that builds the selector is above the search-form.php include so that the whitelist variables have been populated.

 

This part...

$townSelectorPart = "";
        
foreach ($towns as $town) {
	$townSelectorPart .= $town . "|";
}

$townSelectorPart = rtrim($townSelectorPart, "|");

...could be shortened to...

$townSelectorPart = implode('|', $towns);

 

33 minutes ago, SamC said:

I had to use 'if ($input->get->town)' so that part of code doesn't run when on a page which includes the search form, but has yet to be submitted. Is this not dodgy? The value used in this check is not sanitized.

I think it's okay to use an unsanitized GET variable if you are using it only to check if that variable is present.

  • Like 1
Link to comment
Share on other sites

 

 

Thanks @Robin S I did a little bit of re-arranging (and used implode). Works when I do this:

// _search-form.php
<?php namespace ProcessWire; ?>

<?php
    // get all buildings
    $buildings = $pages->find("template=building-entry");
    // get categories as pagearray of Ids sorted by title
    $buildingTypes = $pages->find("template=category-entry, sort=title");
    // modify to standard array of Ids
    $buildingTypes = $buildingTypes->getArray();
    //get unique towns into standard array
    $uniqueTowns = array_unique($buildings->explode("addressBuildingTown"));
    sort($uniqueTowns);

    // set up variables
    $minAreas = [
        500 => '500 sq ft',
        1000 => '1,000 sq ft',
        2000 => '2,000 sq ft',
        3000 => '3,000 sq ft',
        4000 => '4,000 sq ft',
        5000 => '5,000 sq ft',
        6000 => '6,000 sq ft',
        7000 => '7,000 sq ft',
        8000 => '8,000 sq ft',
        9000 => '9,000 sq ft',
        10000 => '10,000 sq ft',
        50000 => '50,000 sq ft'
        ];

    $maxAreas = [
        500 => '500 sq ft',
        1000 => '1,000 sq ft',
        2000 => '2,000 sq ft',
        3000 => '3,000 sq ft',
        4000 => '4,000 sq ft',
        5000 => '5,000 sq ft',
        6000 => '6,000 sq ft',
        7000 => '7,000 sq ft',
        8000 => '8,000 sq ft',
        9000 => '9,000 sq ft',
        10000 => '10,000 sq ft',
        60000 => '60,000 sq ft',
        100000 => '100,000 sq ft'
        ];

        // Array to hold towns from GET
        $towns = [];
        
        if ($input->get->town) {
            $allowedTowns = array_values($uniqueTowns);
            $towns = $sanitizer->options($input->get->town, $allowedTowns);
            $input->whitelist("towns", $towns);
        }

        // sanitizing and adding to whitelist have to be here
        // or I get errors when trying to pre-populate the
        // form with previosuly submitted values

        // GET building type
        $allowedTypes = array_values($buildingTypes);
        $buildingType = $sanitizer->option($input->get->type, $allowedTypes);
        $input->whitelist("buildingType", $buildingType);

        // GET (sanitized) min area and add to whitelist
        $allowedMinAreas = array_keys($minAreas);
        $minArea = $sanitizer->option($input->get->minArea, $allowedMinAreas);
        $input->whitelist("minArea", $minArea);

        // GET max area
        $allowedMaxAreas = array_keys($maxAreas);
        $maxArea = $sanitizer->option($input->get->maxArea, $allowedMaxAreas);
        $input->whitelist("maxArea", $maxArea);

?>

<form id='building_search' method='get' action='<?php echo $config->urls->root?>search/'>

    <h3>Building search</h3>
    
    <p>
        <label for='search_town'>Filter by town</label><br>
            <?php
                foreach ($uniqueTowns as $town) {
                    $selected = "";
                    if ($input->get->town) {
                        // in_array() expects parameter 2 to be array, null given
                        $selected = $town == in_array($town, $input->whitelist("towns"))  ? " checked" : '';
                    }
                    echo "<input type='checkbox' name='town[]' value='{$town}'{$selected}>{$town}<br>";
                }
            ?>
    </p>

    <p>
        <label for='search_type'>Building type</label>
        <select id='search_type' name='type'>
            <option value=''>Any</option>

            <?php
                foreach ($buildingTypes as $type) {
                    $selected = $type == $input->whitelist("buildingType")  ? "selected='selected'" : '';
                    echo "<option {$selected} value='{$type}'>{$type->title}</option>";            
                }
            ?>
        </select>
    </p>


    <p>
    <label for='search_min_area'>Min area</label>
    <select id='search_min_area' name='minArea'>
        <option value=''>No min</option>

        <?php 
            foreach($minAreas as $value => $label) {
                $selected = $value == $input->whitelist("minArea") ? "selected='selected'" : '';
                echo "<option {$selected} value='{$value}'>{$label}</option>";
            }
        ?>

    </select>
    </p>


    <p>
    <label for='search_max_area'>Max area</label>
    <select id='search_max_area' name='maxArea'>
        <option value=''>No max</option>

        <?php 
            foreach($maxAreas as $value => $label) {
                $selected = $value == $input->whitelist("maxArea") ? "selected='selected'" : '';
                echo "<option {$selected} value='{$value}'>{$label}</option>";
            }
        ?>

    </select>
    </p>


    <p><button type='submit' id='search_submit' name='submit' value='1'>Search</button></</p>

</form>
// search.php
<?php namespace ProcessWire; ?>

<?php
    // start building the selector
    // catchall selector for empty fields
    $theSelector = "template=building-entry";

    // start building selectors
    $sorting = "sort=addressBuildingTown";
    $townSelector = "addressBuildingTown";
    $buildingTypeSelector = "category";
    $areaSelector = "buildingArea";


    // used to output 'filtered by' above results
    $summary = [
        "towns" => "", 
        "type" => "",
        "min" => "", 
        "max" => "", 
        ];

    // if a town has been selected
    if ($towns) {
        $townSelectorPart = implode('|', $towns);
        $summary["towns"] = $townSelectorPart;
        $theSelector .= "," . $townSelector . "=" . $townSelectorPart;
    }

    // if a building type has been selected
    if ($buildingType) {
        $summary["type"] = $buildingType;
        $theSelector .= "," . $buildingTypeSelector . "=" . $buildingType;
    }

    // if min or max area has been selected
    if ($minArea || $maxArea) {
        // if min area selected
        if ($minArea) {
            $summary["min"] = $minArea;
            $minAreaSelectorPart = $areaSelector . ">={$minArea}";
            $theSelector .= "," . $minAreaSelectorPart;
        }

        // if max area selected
        if ($maxArea) {
            $summary["max"] = $maxArea;
            $maxAreaSelectorPart = $areaSelector . "<={$maxArea}";
            $theSelector .= "," . $maxAreaSelectorPart;
        }
    }

?>

<?php
    $buildings = $pages->find($theSelector . "," . $sorting);
    foreach ($buildings as $building):
?>

<h2><?php echo $building->title; ?></h2>

<?php if ($building->addressBuildingName): ?>
    <p>BUILDING NAME: <?php echo $building->addressBuildingName; ?></p>
<?php endif; ?>

<p>ADDRESS: 
<?php echo $building->addressBuildingNumber . ' ' . $building->addressBuildingStreet . ', '; ?>
<a href='#'><?php echo $building->addressBuildingTown; ?></a>
</p>

<p>TYPE: <a href="<?php echo $building->category->url; ?>"><?php echo $building->category->title; ?></a></p>

<p>AREA: <?php echo $building->buildingArea; ?></p>

<?php echo $building->buidingDescription; ?>

<?php endforeach; ?>

Makes more sense now :)

9 hours ago, Robin S said:

allows you to create links to the search results page that don't come via the search form (e.g. suppose you had a page that listed a link for each town to show results for that town

^ this is exactly what I want to do as well, nice one.

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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...