I have two function to add remove parameters to the query string. The "add_query_params" (thanks to this forum) is working nicely and I can now add multiple tags to the query string of the same type.
For example
http://example.com?tags[]=flowers&tags[]=shrubs&category[]=garden
As you can see, I can add multiple of the same parameters, I am also querying these nicely using queryfilters.
However my newest problem, is simply removing a single tag type without affecting the rest of the query string. I will then rebuild the query without the deleted tag.
Someone kindly yesterday helped me to to a point but this removes ALL tag key values, not just the specified tag.
So if I was to delete say $tags[]shrubs from the above URL it would actually delete BOTH tag[]shrubs AND $tags[]flowers.
This obviously isn't very intuitive for a filter system I am devising. What I would like to know is how to remove just the single key value pair and leave the other keys pairs intact.
Here is my helper function
//Accept a param array which passthrough through tag type eg category/tag and value
function remove_query_params(array $params = [])
{
//Set to array
$existingParams = [];
$existingParams = request()->query();
foreach($params as $key=>$value){
if (isset($existingParams[$value])) {
unset($existingParams[$value]);
}
}
$query = http_build_query($existingParams);
return url()->current() . '?' . $query;
}
//Need to return: user removes tag from filter in blade, URL recontructs without the passed through tag value
//Before
//http://example.com?tags[]=flowers&tags[]=shrubs&category[]=garden
//After
//http://example.com?tags[]=flowers&category[]=garden
This does not work, if I change $value to $key then it will will, but it will remove all keys of the same type, not the behaviour I would like.
I activate this behaviour via a call in the blade template, this forms a href
//Pass through parameter type and parameter value
{{remove_query_params(['category' => $category->id]) }}
Has anybody got any pointers as to where I go next?#
Thanks and fingers crossed I am not far off :)
Adam
I hope this solution will help you:
<?php
function remove_query_params(array $params = [])
{
//Set to array
$existingParams = [
'tags' => [
'aaaa',
'bbbb'
],
'category' => 'ccc'
];
// go trough all parameters
foreach ($existingParams as $key1 => $value1) {
// go to the parameters, which need to be deleted
foreach ($params as $key2 => $value2) {
// only if the keys equals, do something
if ($key1 === $key2) {
// if the param is an array
if (is_array($value1)) {
foreach ($value1 as $k => $v) {
// if the elements to delete are an array
if (is_array($value2)) {
foreach ($value2 as $b => $r) {
if ($v == $r) {
unset($existingParams[$key1][$k]);
}
}
} else {
if ($v == $value2) {
unset($existingParams[$key1][$k]);
}
}
}
} else {
if (isset($existingParams[$key2])) {
unset($existingParams[$key2]);
}
}
}
}
}
$query = http_build_query($existingParams);
return $query;
}
echo remove_query_params(['tags' => 'aaaa']);
echo "\n";
echo remove_query_params(['tags' => ['aaaa', 'bbbb']]);
echo "\n";
echo remove_query_params(['category' => 'ccc']);
echo "\n";
tags is not an associated array. It is just a list of strings. Also, look at the value of $existingParams = request()->query(); It is not the tags array. It is an object that contains it. That is why when you use $key it works but deletes everything because $key is tags. So, in your check $existingParams['tags'] should be checked for the shrubs value. in_array is what you are looking in this case.
Hope this will solve your problem.I just provided the core function to get the things done in a way
$query = "tags[]=flowers&tags[]=shrubs&category[]=garden";
echo (remove_query_params( [ 'tags' => 'shrubs' ], $query ));
function remove_query_params(array $params = [], $query )
{
parse_str( $query, $existingParams );
$existing_keys = array_keys( $existingParams);
foreach($params as $key=>$value){
if( in_array( $key, $existing_keys ) ){
foreach ($existingParams[$key] as $param_key => $param_value) {
if( $param_value == $value ){
unset( $existingParams[$key][$param_key] );
}
}
}
}
$query = http_build_query($existingParams);
return $query;
}
I am developing a search engine with vector space Model. I successfully computed tf-idf with associative array data already define in code. Now I want that data should be come from directory where I have a folders and in each folder there is a number of text files with dummy data. I have tried alot but stuck at 1 point using glob function because I want all .txt files as key and its contents as value in foreach loop of glob function.... Below is my code.
Tf-idf With Associative Array Data
$collection = array(
1 => 'this string is a short string but a good string',
2 => 'this one isn\'t quite like the rest but is here',
3 => 'this is a different short string that\' not as short'
);
$dictionary = array();
$docCount = array();
foreach($collection as $docID => $doc) {
$terms = explode(' ', $doc);
$docCount[$docID] = count($terms);
foreach($terms as $term) {
if(!isset($dictionary[$term])) {
$dictionary[$term] = array('df' => 0, 'postings' => array());
}
if(!isset($dictionary[$term]['postings'][$docID])) {
$dictionary[$term]['df']++;
$dictionary[$term]['postings'][$docID] = array('tf' => 0);
}
$dictionary[$term]['postings'][$docID]['tf']++;
}
}
$temp = ('docCount' => $docCount, 'dictionary' => $dictionary);
As you see in 1st foreach loop is that $DocID is key and $doc is its contents(value) of collection array. But I don't know how to implement exact same thing when files read from directory. See code below..
Tf-idf With .txt Files and its contents read from directory
foreach (glob("C:\\wamp\\www\\Web-info\\documents\\awd_1990_00\\*.txt") as $file) {
$file_handle = fopen($file, "r");
//echo $file;
$dictionary = array();
$docCount = array();
foreach($file as $docID=> $value) {
echo $value;
$terms = explode(' ', $doc);
$docCount[$docID] = count($terms);
foreach($terms as $term) {
if(!isset($dictionary[$term])) {
$dictionary[$term] = array('df' => 0, 'postings' => array());
}
if(!isset($dictionary[$term]['postings'][$docID])) {
$dictionary[$term]['df']++;
$dictionary[$term]['postings'][$docID] = array('tf' => 0);
}
$dictionary[$term]['postings'][$docID]['tf']++;
}
}
}
$temp = array('docCount' => $docCount, 'dictionary' => $dictionary);
This gives me error on 1st foreach loop that invalid arugument supplied for foreach loop. As I mentioned earlier I want .txt files as a key and its contents as a value in 1st foreach loop. But I got this error Can anybody please Tell me how to do this.. Thanks in advance..
If you want to treat the entire file as one value, you can use file_get_contents() to read the file into a string:
$dictionary = array();
$docCount = array();
foreach (glob("C:\\wamp\\www\\Web-info\\documents\\awd_1990_00\\*.txt") as $docID) {
$value = file_get_contents($docID);
...
}
In the script below I have an array. My array stores all the links, titles and descriptions from a web page. But I want to make sure that if there is no description, it will use the first 20 characters of a p tag using a function which works. Only problem is I have the jigsaw pieces and just can't seem to put them together, So I want my if statement to show that if the description is empty to use the function getWord instead of getMetas().
function getMetas($link) {
$str1 = file_get_contents($link);
if (strlen($str1)>0) {
preg_match_all( '/<meta.*?name=("|\')description("|\').*?content=("|\')(.*?)("|\')/i', $str1, $description);
if (count($description) > 1) {
return $description[4];
}
}
}
then my function goes here but there is no need to see that as i know that works.
function getWord() {
$html = file_get_contents($link);
preg_match('%(<p[^>]*>.*?</p>)%i', $html, $re);
$res = get_custom_excerpt($re[1]);
}
$outputs = array();
foreach ($links as $thisLink) {
$output[] = array("link" => $thisLink, "title" => Titles($thisLink), "description" => getMetas($thisLink));
if ($output['description'] == null) {
$output['description'] = $res;
}
$outputs[] = $output;
}
print_r($output);
I would like to append html to an item in my array before echoing it on my page and am unsure how to go about doing it.
My data is put into an array like so:
$query = $this->db->get();
foreach ($query->result() as $row) {
$data = array(
'seo_title' => $row->seo_title,
'seo_description' => $row->seo_description,
'seo_keywords' => $row->seo_keywords,
'category' => $row->category,
'title' => $row->title,
'intro' => $row->intro,
'content' => $row->content,
'tags' => $row->tags
);
}
return $data;
I would like to perform the following on my 'tags' before returning the data to my view:
$all_tags = explode( ',' , $row->tags );
foreach ( $all_tags as $one_tag ){
echo '' . $one_tag . '';
The reason for doing this is that the tags in my database contain no html and are simply separated by commas like so news,latest,sports and I want to convert them into
sports ...
My reason for doing this here rather than when I echo the data is that I don't want to repeat myself on every page.
You could just create a function to be used everyhwere you are including tags in your output:
function formatTags($tags) {
$tmp = explode(',', $tags);
$result = "";
foreach ($tmp as $t) {
$result .= sprintf('%s',
urlencode(trim($t)), htmlentities(trim($t)));
}
return $result;
}
And whenever you do something like echo $tags; you do echo formatTags($tags); instead. View code should be separated from model code, which is why I would advise to not put HTML inside your array.
Well first of all you're overwriting $data with every run of the loop so only the final result row will be listed.
Once that's out of the way (fix with $data[] = ...), try this:
...
'tags' => preg_replace( "/(?:^|,)([^,]+)/", "$1", $row->tags);
...
The question: How do I generate navigation, allowing for applying different classes to different sub-items, from a multi-dimensional array?
Here is how I was doing it before I had any need for multi-level navigation:
Home
Pics
About
and was generated by calling nav():
function nav(){
$links = array(
"Home" => "home.php",
"Pics" => "pics.php",
"About" => "about.php"
);
$base = basename($_SERVER['PHP_SELF']);
foreach($nav as $k => $v){
echo buildLinks($k, $v, $base);
}
}
Here is buildLinks():
function buildLinks($name, $page, $selected){
if($selected == $page){
$theLink = "<li class=\"selected\">$name</li>\n";
} else {
$thelink = "<li>$name</li>\n";
}
return $thelink;
}
My question, again:
how would I achieve the following nav (and notice that the visible sub navigation elements are only present when on that specific page):
Home
something1
something2
Pics
About
and...
Home
Pics
people
places
About
What I've tried
From looking at it it would seem that some iterator in the SPL would be a good fit for this but I'm not sure how to approach this. I have played around with RecursiveIteratorIterator but I'm not sure how to apply a different style to only the sub menu items and also how to only show these items if you are on the correct page.
I built this array to test with but don't know how to work with the submenu1 items individually:
$nav = array(
array(
"Home" => "home.php",
"submenu1" => array(
"something1"=>"something1.php",
"something2" => "something2.php")
),
array("Pics" => "pics.php"),
array("About" => "about.php")
);
The following will print out the lot in order but how do I apply, say a class name to the submenu1 items or only show them when the person is on, say, the "Home" page?
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($nav));
foreach($iterator as $key=>$value) {
echo $key.' -- '.$value.'<br />';
}
And this gets me:
Home
something1
something2
Pics
About
But I have no way to apply classes to those sub items and no way to only display them conditionally because I don't see how to target just these elements.
Don't reinvent the wheel, use Zend_Navigation and you will be happy.
You were on the right track with RecursiveIteratorIterator. It essentially flattens a recursive iterator. Here is the correct way:
$nav = array(
array(
"Home" => "home.php",
"submenu1" => array(
"something1"=>"something1.php",
"something2" => "something2.php")
),
array("Pics" => "pics.php"),
array("About" => "about.php"),
);
$it = new RecursiveIteratorIterator(
new RecursiveArrayIterator($nav),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($it as $k => $v) {
if ($it->getDepth() == 0)
continue;
echo str_repeat(" ", $it->getDepth() - 1) .
"$k => $v\n";
}
gives
Home => home.php
submenu1 => Array
something1 => something1.php
something2 => something2.php
Pics => pics.php
About => about.php
It seems like you might want to do this in a more object oriented way.
If not, it seems like you should at least define an algorithm that makes sense, right now you are just blindly guessing. Instead, DEFINE.
For example:
I am defining my navigation to be a php hash based tree. A navigation item will have the following:
A) if there is a top level link, the array hash will contain an item(sub array) labeled "navigation leaf"
b) A navigation Leaf will contain elements labeled "Display value", "link value", and "alt value". These items will be used to generate an anchor tag.
c) if an element has a submenu, in addition to containing a "Navigation Leaf", a "subnavigation" element will be present. A subnavigation element will have a "Navigation Leaf" if it has a displayable navigation item.
You can then write functions/methods that will display your navigation based on the definition you choose.
What I would do, is something along these lines:
class MenuItem {
protected $active = false;
protected $children = array();
protected $name = '';
protected $link = '';
public function __construct($name, $link, $active) {}
public function __toString() {
//render this item
$out = ''; #render here
if (!$this->isActive()) {
return $out;
}
$out .= '<ul>';
foreach ($this->children as $child) {
$out .= (string) $child;
}
$out .= '</ul>';
return $out;
}
public function isActive() {
if ($this->active) {
return true;
}
foreach ($this->children as $child) {
if ($child->isActive()) {
return true;
}
}
return false;
}
}
Then, all you have is a collection of root menu items in an array... To build your menu, you just do:
$rootItems = array($item1, $item2);
$out = '<ul>';
foreach ($rootItems as $item) {
$out .= (string) $item;
}
$out .= '</ul>';
I'll leave the semantics of constructing the object, adding children, etc to the user...
What about rewrite nav function in the next way:
function nav($links, $level){
foreach($links as $k => $v) {
if (is_array($v)) {
nav($v, $level + 1)
} else {
echo buildLinks($k, $v, $base);
}
}
}
And than call it:
$links = array(
array(
"Home" => "home.php",
"submenu1" => array(
"something1"=>"something1.php",
"something2" => "something2.php")
),
array("Pics" => "pics.php"),
array("About" => "about.php")
);
nav($links, 0);
Simplest way, IMHO, is to just make a recursive call, and use a tree structured description of your navigation (that is, nested arrays). Untested example code:
<?php
$links = array(
"Home" => array("home.php", array(
"something1"=> array("something1.php", array()),
"hello"=> array("hello.php", array(
"world" => array("world.php", array()),
"bar" => array("bar.php", array()),
)),
)),
"Pics" => array("pics.php", array(
"people"=>"people.php",
"places" => "places.php",
)),
"About" => array("about.php", array()), // example no subitems
);
// use the following $path variable to indicate the current navigational position
$path = array(); // expand nothing
$path = array('Home'); // expand Home
$path = array('Home', 'hello'); // also expand hello in Home
// map indent levels to classes
$classes = array(
'item',
'subitem',
'subsubitem',
);
// recursive function to build navigation list
function buildNav($links, $path, $classes)
{
// selected page at current level
// NOTE: array_shift returns NULL if $path is empty.
// it also alters the array itself
$selected = array_shift($path);
$class = array_shift($classes);
echo "<ul>\n";
foreach($links as $name => $link)
{
list($href, $sublinks) = $link;
if ($name == $selected)
{
echo "<li class=\"selected $class\">$name\n";
// recursively show subitems
// NOTE: path starts now with the selected subitem
buildNav($sublinks, $path, $classes);
echo "</li>\n";
}
else
{
echo "<li>$name</li>\n";
}
}
echo "<ul>\n";
}
// actually build the navigation
buildNav($links, $path, $classes);
?>
#catchmeifyoutry
Thank you, you saved my life LoL.
I changed your function a little to adapt it to my use and this came out:
$html['navi'] = array(
"Home" => "/home/",
"DJs & Shows" => "/djs-shows/",
"Playlists" => "/playlists/",
"Newsbeat" => "/newsbeat/",
"Reviews" => "/reviews/",
"TV" => "/tv/",
"Contact" => "/contact/",
"Test" => array("/test/",
array("Submenu 1" => "/test/link1",
"Submenu 2" => "/test/link2",
"Submenu 3" => "/test/link3",
"Submenu 4" => "/test/link4",
"Submenu 5" => "/test/link5",
"Submenu 6" => "/test/link6"
)
)
);
$classes = array(
'first-level',
'second-level',
'third-level',
);
function siteNavi($links, $classes) {
// The best way for MultiArray navigation (LOVE IT!)
// Array Shift selects first element and removes it from array
$class = array_shift($classes);
echo "<ul class=\"$class\">\n";
foreach($links as $name => $link) {
if (is_array($link) AND $class != "") {
list($link, $sublinks) = $link;
if ($_GET['site'] == basename($link)) { $selected = ' class="current"'; } else { $selected = ""; }
echo "<li{$selected}>{$name}\n";
// recursively show subitems
// NOTE: path starts now with the selected subitem
siteNavi($sublinks, $classes);
echo "</li>\n";
} else {
if ($_GET['site'] == basename($link)) { $selected = ' class="current"'; } else { $selected = ""; }
echo "<li{$selected}><a href=\"{$link}\" >{$name}</a></li>\n";
}
}
echo "</ul>\n";
}
Thank you very much !
I wonder how much impact does have this kind of code on the page speed tho. Few microseconds of milliseconds :D