Jump to content

Help with filtering based on the Skyscraper functions


Flashmaster82
 Share

Recommended Posts

Hi, i need some help with the filtering based on the Skyscrapers demo/func. I´m not that great with php so be patient.

My structure looks like this

Youtube videos > Youtube channel > Video

on the video template (youtube_channel_video) i have multiple drop down fields and other fields that i would like to filter on the front end. I also have other pages that need this functions with search/filter etc but with different filters.

 

This is the code i have in my search-form.php. Right now i experimenting with the field/option (category) that every video have in their template. The first filter (youtube_channel) works but im trying to get the category filter to work.

<div id='skyscraper-search' class='uk-panel uk-panel-box xuk-panel-box-primary uk-margin-bottom'>
	
	<h3 class='h3'>Youtube videos</h3>
	
	<form class='uk-form uk-form-stacked' method='get' action='<?php echo $config->urls->root?>search/'>

		<div class='row'>
			<label class='uk-form-label' for='search_keywords'>Keywords</label>
			<div class='uk-form-controls'>
				<input type='text' class='uk-form-width-large' name='keywords' id='search_keywords' value='<?php 
					if($input->whitelist('keywords')) echo $sanitizer->entities($input->whitelist('keywords')); ?>' />
			</div>
		</div>

		
			<div class='col-6'>
				<div class='row'>
					<label class='uk-form-label' for='youtube_channel'>Youtube channel</label>
					<div class='uk-form-controls'>
						<select id='youtube_channel' name='youtube_channel' class='uk-form-width-large'>
							<option value=''></option>
							<?php 
							// generate the youtube_channel options, checking the whitelist to see if any are already selected
							foreach($pages->find("template=youtube_channel_page") as $youtube_channel) {
								$selected = $youtube_channel->name == $input->whitelist->youtube_channel ? " selected='selected' " : ''; 
								echo "<option$selected value='{$youtube_channel->name}'>{$youtube_channel->title}</option>"; 
							}
							?>
						</select>
					</div>	
				</div>
			</div>


			<div class='col-6'>
				<div class='row'>
					<label class='uk-form-label' for='category'>Category</label>
					<div class='uk-form-controls'>
						<select id='category' name='category' class='uk-form-width-large'>
							<option value=''></option>
							<?php 
							// generate the category options, checking the whitelist to see if any are already selected
							foreach($pages->find("template=youtube_channel_video") as $category) {
								$selected = $category->youtube_video_category == $input->whitelist->youtube_video_category ? " selected='selected' " : ''; 
								echo "<option$selected value='{$category->youtube_video_category}'>{$category->youtube_video_category->title}</option>"; 
							}
							?>
						</select>
					</div>	
				</div>
			</div>
		
		
		

		<div class='uk-margin-top'>
			<button type='submit' id='search_submit' class='uk-button uk-button-primary' name='submit' value='1'>
				<i class='uk-icon-search'></i>
				Search
			</button>
		</div>

	</form>
</div>

 

This is my search.php

<?php namespace ProcessWire;

$selector = '';



$summary = array(
	"title" => "",
	"youtube_channel" => "",
	"category" => "",
	"country" => "", 
	);




if($input->get('youtube_channel')) {
	$youtube_channelName = $sanitizer->pageName($input->get('youtube_channel'));
	$youtube_channel = pages("/youtube-videos/$youtube_channelName/");
	if($youtube_channel->id) {
		$selector .= "parent=$youtube_channel, ";
		$summary['youtube_channel'] = $youtube_channel->title;
		$input->whitelist('youtube_channel', $youtube_channel->name); 
	}
}




foreach(array('category') as $key) {

	if(!$value = $input->get($key)) continue; 

	 else {	
		$value = (int) $value; 
		$selector .= "$key=$value, ";
		$summary[$key] = $value;
		$input->whitelist($key, $value); 
	}
}




if($input->get('keywords')) {
	$value = $sanitizer->selectorValue($input->get('keywords'));
	$selector .= "title|body|category%=$value, sort=title"; 
	$summary["keywords"] = $sanitizer->entities($value); 
	$input->whitelist('keywords', $value); 
}


$videos = findSkyscrapers($selector);




$browserTitle = 'Youtube video search - ';

foreach($summary as $key => $value) {
	if($value) {
		$key = ucfirst($key);
		$browserTitle .= ucfirst($key) . ": $value, ";
	} else {
		unset($summary[$key]);
	}
}




region('browserTitle', rtrim($browserTitle, ', '));
region('content',
	files()->render('./includes/search-summary.php', array('items' => $summary)) . 
	renderSkyscraperList($videos)
);

 

This is my skyscraper-list-item.php file

 

<?php
echo " 
<div class='col-12 col-sm-12 col-md-6 col-lg-6 col-xl-4 col-xxl-4 col-xxxl-4 bmar10'>
<div class='youtube_search_holder'>
<a data-fancybox data-autoclose='true' data-width='1500' data-height='844' data-height='360' href='{$skyscraper->youtube}autoplay=1'>
<div class='youtube_thumbnail_placeholder'><img src='{$skyscraper->youtube_thumbnail->url}' class='youtube_thumbnail_image w-100'>
 <div class='playicon'></div></div></a>
 <div class='youtube_search_thumbnail_content'>
 <div class='h6 text-uppercase green tmar3 title'><a href='{$skyscraper->parent->channel_url}' target='_blank' title='{$skyscraper->parent->title}'>{$skyscraper->parent->title}</a></div><div class='text-uppercase white h3 limit'><a href='{$skyscraper->youtube}' data-fancybox data-width='1500' data-height='844' data-height='360'>{$skyscraper->title}</a></div>
 <div class='h5 text-uppercase gray date'>{$skyscraper->youtube_video_publishdate}</div>
<span class='white h7'>
{$skyscraper->youtube_video_category->title}
{$skyscraper->countries->title}
{$skyscraper->competition->title}
{$skyscraper->competition->parent->parent->title}
{$skyscraper->armwrestler_competition_gender->title}
{$skyscraper->armwrestler_arm->title}
{$skyscraper->armwrestler_age_category->title}
{$skyscraper->competition_weight_class->title}
{$skyscraper->competition_video_match->title}
{$skyscraper->videotags}
</span>

</div></div></div>
";
?>

This is the front end right now (no styling ?

bild.thumb.png.e94245452d70ab29307d3b084438f2a0.png

In the category filter, right now there is multiple categories with the same name, i would also like to restrict it to just one result per category.

Please help!

Link to comment
Share on other sites

17 hours ago, Flashmaster82 said:

i have multiple drop down fields and other fields that i would like to filter on the front end.

Hi. I didn't have time to go through all your code, so you might have already answered this.

  1. It is not clear to me which is the search form that triggers the initial search. Is it the keywords or the channel or both and the category as well?
  2. Normally, with search dynamic selects, you need both PHP and JavaScript. Do you need help with the JavaScript as well?
  3. How do you want the results returned? For instance, if you fetch a parent, do you want it to come with all its children in one go?
Link to comment
Share on other sites

Hi Kongondo and thanks for replying.

 

Here is the _func.php. All files are straight from the Skyscrapers demo btw.

<?php namespace ProcessWire;

/***************************************************************************************
 * SHARED SKYSCRAPER FUNCTIONS
 *
 * The following functions find and render skyscrapers are are defined here so that
 * they can be used by multiple template files.
 *
 */

/**
 * Returns an array of valid skyscraper sort properties
 *
 * The keys for the array are the field names
 * The values for the array are the printable labels
 *
 * @return array
 *
 */
function getValidSorts() {
	return array(
		// field => label
		'name' => 'Name (A-Z)',
		'-name' => 'Name (Z-A)',
		'date' => 'Publish date (asc)',
		'-date' => 'Publish date (desc)',
	);
}

/**
 * Find Skyscraper pages using criteria from the given selector string.
 *
 * Serves as a front-end to $pages->find(), filling in some of the redundant
 * functionality used by multiple template files.
 *
 * @param string $selector
 * @return PageArray
 *
 */
function findSkyscrapers($selector) {

	$validSorts = getValidSorts();

	// check if there is a valid 'sort' var in the GET variables
	$sort = sanitizer('name', input()->get('sort'));

	// if no valid sort, then use 'title' as a default
	if(!$sort || !isset($validSorts[$sort])) $sort = 'name';

	// whitelist the sort value so that it is retained in pagination
	if($sort != 'name') input()->whitelist('sort', $sort);

	// expand on the provided selector to limit it to 10 sorted skyscrapers
	$selector = "template=youtube_channel_video, limit=20, " . trim($selector, ", ");

	// check if there are any keyword searches in the selector by looking for the presence of 
	// ~= operator. if present, then omit the 'sort' param, since ProcessWire sorts by 
	// relevance when no sort specified.
	if(strpos($selector, "~=") === false) $selector .= ", sort=$sort";

	// now call upon ProcessWire to find the skyscrapers for us
	$skyscrapers = pages($selector);

	// save skyscrapers for possible display in a map
	mapSkyscrapers($skyscrapers);

	return $skyscrapers;
}

/**
 * Serves as a place to store and retrieve loaded skyscrapers that will be displayed in a google map.
 *
 * To add skyscrapers, pass in a PageArray of them.
 * To retrieve skyscreapers, pass in nothing and retrieve the returned value.
 *
 * @param null|PageArray $items Skyscraper pages to store
 * @return PageArray All Skyscraper pages stored so far
 *
 */
function mapSkyscrapers($items = null) {
	static $skyscrapers = null;
	if(is_null($skyscrapers)) $skyscrapers = new PageArray();
	if(!is_null($items) && $items instanceof PageArray) $skyscrapers->add($items);
	return $skyscrapers;
}

/**
 * Render the <thead> portion of a Skyscraper list table
 *
 * @return string
 *
 */
function renderSkyscraperListSort() {

	// query string that will be used to retain other GET variables in searches
	input()->whitelist->remove('sort');
	$queryString = input()->whitelist->queryString();
	if($queryString) $queryString = sanitizer('entities', "&$queryString");

	// get the 'sort' property, if it's present
	$sort = input()->get('sort');
	$validSorts = getValidSorts();
	
	// validate the 'sort' pulled from input
	if(!$sort || !isset($validSorts[$sort])) $sort = 'name';

	$options = array();
	$selectedLabel = '';

	// generate options
	foreach($validSorts as $key => $label) {
		if($key === $sort) $selectedLabel = $label;
		$options["./?sort=$key$queryString"] = $label;
	}

	// render output
	$out = files()->render('./includes/skyscraper-list-sort.php', array(
		'options' => $options, 
		'selectedLabel' => $selectedLabel
	));
	
	return $out;
}


/**
 * Render a list of skyscrapers
 *
 * @param PageArray $skyscrapers Skyscrapers to render
 * @param bool $showPagination Whether pagination links should be shown
 * @param string $headline
 * @return string The rendered markup
 *
 */
function renderSkyscraperList(PageArray $skyscrapers, $showPagination = true, $headline = '') {

	$pagination = '';
	$sortSelect = '';
	$items = array();
	
	if($showPagination && $skyscrapers->count()) {
		$headline = $skyscrapers->getPaginationString('Skyscrapers'); // i.e. Skyscrapers 1-10 of 500
		$pagination = renderPagination($skyscrapers); // pagination links
		$sortSelect = renderSkyscraperListSort();
	}

	foreach($skyscrapers as $skyscraper) {
		$items[] = renderSkyscraperListItem($skyscraper);
	}

	$selector = (string) $skyscrapers->getSelectors();
	if($selector) $selector = makePrettySelector($selector);
	
	$out = files()->render('./includes/skyscraper-list.php', array(
		'skyscrapers' => $skyscrapers, 
		'headline' => $headline, 
		'items' => $items, 
		'pagination' => $pagination, 
		'sortSelect' => $sortSelect, 
		'selector' => $selector
	));
		
	return $out;
}

/**
 * Render a single skyscraper for presentation in a skyscraper list
 *
 * @param Page $skyscraper The Skyscraper to render
 * @return string
 *
 */
function renderSkyscraperListItem(Page $skyscraper) {


	// here's a fun trick, set what gets displayed when value isn't available.
	// the property "unknown" is just something we made up and are setting to the page.
	$skyscraper->set('unknown', '??');

	// send to our view file in includes/skyscraper-list-item.php
	$out = files()->render('./includes/skyscraper-list-item.php', array(
		'skyscraper' => $skyscraper,
		'title' => $skyscraper->title, 
		'category' => $skyscraper->youtube_video_category->title,
	));
	
	return $out;
}

/**
 * ProcessWire pagination nav for UIkit
 *
 * @param PageArray $items
 * @return string
 *
 */
function renderPagination(PageArray $items) {

	if(!$items->getLimit() || $items->getTotal() <= $items->getLimit()) return '';
	$page = page();
	if(!$page->template->allowPageNum) {
		return "Pagination is not enabled for this template";
	}

	// customize the MarkupPagerNav to output in Foundation-style pagination links
	$options = array(
		'numPageLinks' => 5, 
		'nextItemLabel' => '<i class="uk-icon-angle-double-right"></i>',
		'nextItemClass' => '',
		'previousItemLabel' => '<span><i class="uk-icon-angle-double-left"></i></span>',
		'previousItemClass' => '',
		'lastItemClass' => '',
		'currentItemClass' => 'uk-active',
		'separatorItemLabel' => '<span>&hellip;</span>',
		'separatorItemClass' => 'uk-disabled',
		'listMarkup' => "<ul class='uk-pagination uk-text-left'>{out}</ul>",
		'itemMarkup' => "<li class='{class}'>{out}</li>",
		'linkMarkup' => "<a href='{url}'>{out}</a>",
		'currentLinkMarkup' => "<span>{out}</span>"
	);

	$pager = modules('MarkupPagerNav');
	$pager->setBaseUrl($page->url);

	return $pager->render($items, $options);
}

/**
 * Make the selector better for display readability
 *
 * Since we're displaying the selector to screen for demonstration purposes, this method optimizes the
 * selector is the most readable fashion and removes any parts that aren't necessary
 *
 * This is not something you would bother with on a site that wasn't demonstrating a CMS. :)
 * 
 * @param string $selector
 * @return string
 *
 */
function makePrettySelector($selector) {
	if(preg_match('/(architects|parent)=(\d+)/', $selector, $matches)) {
		if($page = pages()->get($matches[2]))
			$selector = str_replace($matches[0], "$matches[1]={$page->path}", $selector);
		if($matches[1] == 'parent') $selector = str_replace("template=youtube_channel_video, ", "", $selector); // template not necessary here
	}
	$selector = sanitizer('entities', $selector);
	$span = "<span class='uk-text-nowrap'>";
	$selector = $span . str_replace(", ", ",</span> $span ", $selector) . "</span>";
	return $selector;
}


/**
 * Generate a summary from the given block of text or HTML and truncate to last sentence
 *
 * @param string $text
 * @param int $maxLength
 * @return string
 *
 */
function summarizeText($text, $maxLength = 500) {

	if(!strlen($text)) return '';
	$summary = trim(strip_tags($text));
	if(strlen($summary) <= $maxLength) return $summary;

	$summary = substr($summary, 0, $maxLength);
	$lastPos = 0;

	foreach(array('. ', '!', '?') as $punct) {
		// truncate to last sentence
		$pos = strrpos($summary, $punct);
		if($pos > $lastPos) $lastPos = $pos;
	}

	if(!$lastPos) {
		// if no last sentence was found, truncate to last space
		$lastPos = strrpos($summary, ' ');
	}

	if($lastPos) {
		$summary = substr($summary, 0, $lastPos + 1); // and truncate to last sentence
	}

	return trim($summary);
}

 

The main thing i want to fix is how to add more filters. The one that i working with is (categories).

 

If i only filter with youtube channel (which is a modifyed by the city filter) it works and the result are visible, but then the category filter is not working.

 

bild.thumb.png.ac98017f6e3cecbb126cecd44c34139e.png

 

 

So in the search-form.php i have this. The first section in the code below is the drop down for the channels and the second is the category filter.

	<div class='col-6'>
				<div class='row'>
					<label class='uk-form-label' for='youtube_channel'>Youtube channel</label>
					<div class='uk-form-controls'>
						<select id='youtube_channel' name='youtube_channel' class='uk-form-width-large'>
							<option value=''></option>
							<?php 
							// generate the youtube_channel options, checking the whitelist to see if any are already selected
							foreach($pages->find("template=youtube_channel_page") as $youtube_channel) {
								$selected = $youtube_channel->name == $input->whitelist->youtube_channel ? " selected='selected' " : ''; 
								echo "<option$selected value='{$youtube_channel->name}'>{$youtube_channel->title}</option>"; 
							}
							?>
						</select>
					</div>	
				</div>
			</div>


			<div class='col-6'>
				<div class='row'>
					<label class='uk-form-label' for='category'>Category</label>
					<div class='uk-form-controls'>
						<select id='category' name='category' class='uk-form-width-large'>
							<option value=''>Category</option>
							<?php 
							// generate the category options, checking the whitelist to see if any are already selected
							foreach($pages->find("template=youtube_channel_video, sort=youtube_video_category") as $category) {
								$selected = $category->youtube_video_category == $input->whitelist->youtube_video_category->title ? " selected='selected' " : ''; 
								echo "<option$selected value='{$category->youtube_video_category}'>{$category->youtube_video_category->title}</option>"; 
							}
							?>
						</select>
					</div>	
				</div>
			</div>

 

and then in the search.php i have this.

<?php namespace ProcessWire;

$selector = '';



$summary = array(
	"title" => "",
	"youtube_channel" => "",
	"category" => "",
	);






if($input->get('youtube_channel')) {
	$youtube_channelName = $sanitizer->pageName($input->get('youtube_channel'));
	$youtube_channel = pages("/youtube-videos/$youtube_channelName/");
	if($youtube_channel->id) {
		$selector .= "parent=$youtube_channel, ";
		$summary['youtube_channel'] = $youtube_channel->title;
		$input->whitelist('youtube_channel', $youtube_channel->name); 
	}
}





foreach(array('category', 'floors', 'year') as $key) {

        if(!$value = $input->get->$key) continue;

        // see if the value is given as a range (i.e. two numbers separated by a dash)
        if(strpos($value, '-') !== false) {
                list($min, $max) = explode('-', $value);
                $min = (int) $min;
                $max = (int) $max;
                $selector .= "$key>=$min, $key<=$max, ";
                $summary[$key] = (substr($max, 0, 3) == '999') ? "$min and above" : "$min to $max";
                $input->whitelist($key, "$min-$max");

        // see if the value ends with a +, which we used to indicate 'greater than or equal to'
        } else if(substr($value, -1) == '+') {
                $value = (int) $value;
                $selector .= "$key>=$value, ";
                $summary[$key] = "$value and above";
                $input->whitelist($key, "$value+");

        // plain value that doesn't need further parsing
        } else {
                $value = (int) $value;
                $selector .= "$key=$value, ";
                $summary[$key] = $value;
                $input->whitelist($key, $value);
        }
}




if($input->get('keywords')) {
	$value = $sanitizer->selectorValue($input->get('keywords'));
	$selector .= "title|body%=$value, sort=title"; 
	$summary["keywords"] = $sanitizer->entities($value); 
	$input->whitelist('keywords', $value); 
}


$videos = findSkyscrapers($selector);




$browserTitle = 'Youtube video search - ';

foreach($summary as $key => $value) {
	if($value) {
		$key = ucfirst($key);
		$browserTitle .= ucfirst($key) . ": $value, ";
	} else {
		unset($summary[$key]);
	}
}




region('browserTitle', rtrim($browserTitle, ', '));
region('content',
	files()->render('./includes/search-summary.php', array('items' => $summary)) . 
	renderSkyscraperList($videos)
);

 

The code that starts with this is what im trying to modify from the demo, but its wrong of course, because in the demo it targets a field and a range (floors/year). My filter with category is an drop down option field in the backend. When im loading the page website/search.php it shows all the youtube videos and the pagination, so it works but its only the category filter that is wrong just to be clear.

foreach(array('category', 'floors', 'year') as $key) {
Link to comment
Share on other sites

Ok to make it more understandable. This is the other filter (dropdown) in the skyscraper demo. This works but in my case i dont want to be filtering with any range only specific category.

search-form.php

<div class='uk-width-1-2'>
				<div class='uk-form-row'>
					<label class='uk-form-label' for='search_floors'>Floors</label>
					<div class='uk-form-controls'>
						<select id='search_floors' name='floors' class='uk-form-width-large'>
							<option value=''>Any</option><?php
							// generate our range of floors, checking to see if any are already selected
							foreach(array('1-20', '20-40', '40-60', '60-80', '80+') as $range) {
								$selected = $range == $input->whitelist->floors ? " selected='selected'" : '';
								echo "<option$selected value='$range'>$range floors</option>";
							}
							?>
						</select>
					</div>	
				</div>
			</div>	

 

and in the search.php

foreach(array('floors', 'year') as $key) {

        if(!$value = $input->get->$key) continue;

        // see if the value is given as a range (i.e. two numbers separated by a dash)
        if(strpos($value, '-') !== false) {
                list($min, $max) = explode('-', $value);
                $min = (int) $min;
                $max = (int) $max;
                $selector .= "$key>=$min, $key<=$max, ";
                $summary[$key] = (substr($max, 0, 3) == '999') ? "$min and above" : "$min to $max";
                $input->whitelist($key, "$min-$max");

        // see if the value ends with a +, which we used to indicate 'greater than or equal to'
        } else if(substr($value, -1) == '+') {
                $value = (int) $value;
                $selector .= "$key>=$value, ";
                $summary[$key] = "$value and above";
                $input->whitelist($key, "$value+");

        // plain value that doesn't need further parsing
        } else {
                $value = (int) $value;
                $selector .= "$key=$value, ";
                $summary[$key] = $value;
                $input->whitelist($key, $value);
        }
}

 

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

×
×
  • Create New...