I'm working on a search-based website, and am trying to pass parameters using SEO-friendly URLs.
Is it possible to pass the following URL and get the URL in CodeIgniter?
http://www.example.com/search/prize/10/13/14.5/size/xl/2xl/color/black/white-grey/
I am able to create the URL, but I want to get the URL values like $prize = array("10","13","14.5"), $size= array("xl","2xl"), and $color = array("black","white-grey").
I tried to use the uri_to_assoc() function, but it didn't work. I get the following output:
[price] => 10,
[13] => 14.5
...
which is wrong.
Note: I tried to use $this->uri->segment(1), etc., but in this case, the segment position is dynamic.
For example, users may search for only prices of $10, so the URL will get changed to:
http://www.example.com/search/prize/10/size/xl/2xl/color/black/white-grey/
Now the segment position of getting the size must be changed. In this case, I want:
$prize = array("10");
$size = array("xl", "2xl");
$color = array("black", "white-grey");
How can I achieve this?
You are using quite the unconventional "friendly URI" format. Normally when passing parameters there is a single identifier and then the parameter e.g. /name/key/name/key/name/key.
When you use the correct format /name/key/name/key/name/key in conjunction with uri_to_assoc(), you would get:
array(
'name' => 'key',
// etc...
)
but using something like /prize/1/2/3/size/s/m/l/color/black/white/grey would yield:
array(
'prize' => 1,
2 => 3,
'size' => 's',
'm' => 'l',
// etc...
)
Which is useless to you.
You will have to fetch all of the segments individually and build your arrays with a foreach:
$segments = $this->uri->segment_array();
$prize = array();
$size = array();
$color = array();
$curKey = '';
foreach ($segments as $seg) {
if (in_array($seg, array('prize', 'size', 'color'))) {
$curKey = $seg; continue;
}
if (!empty($curKey)) ${$curKey}[] = $seg;
}
// use $prize, $size, and $color as you wish
or alternatively using a multi-dimensional array:
$parameters = array(
'prize' => array(),
'size' => array(),
'color' => array(),
);
$segments = $this->uri->segment_array();
$curKey = '';
foreach ($segments as $seg) {
if (in_array($seg, array('prize', 'size', 'color'))) {
$curKey = $seg; continue;
}
if (!empty($curKey)) $parameters[$curKey][] = $seg;
}
Fetch all the segments
Iterate over them, check each for keywords like size, color, etc. (make a whitelist)
If a keyword is found, assume the segment values until the next keyword segment or the end are the values for that specific keyword (so xl and 2xl will denote the size if preceeded with a keyword size, etc.)
Related
I have an array, so I wanted to add keys to each value, for example, if an array contains Facebook URL then the key should be Facebook if an array has a link of Instagram then the key should be Instagram and the list goes on.
Here's the code
<?php
foreach($social_media as $social){
$typesocial = $social['type'];
if($social['type'] === 'social network') {
$val[] = $social['url']['resource'];
}
}
print_r($val);
?>
Array (
[0] => https://plus.google.com/+beyonce
[1] => https://twitter.com/Beyonce
[2] => https://www.facebook.com/beyonce
[3] => https://www.instagram.com/beyonce/
[4] => http://www.weibo.com/beyonceofficial
)
It should become, if the value has a link of twitter then the key should be twitter if Instagram then it should be Instagram
Array (
[google] => https://plus.google.com/+beyonce
[twitter] => https://twitter.com/Beyonce
[facebook] => https://www.facebook.com/beyonce
[instagram] => https://www.instagram.com/beyonce/
[weibo] => http://www.weibo.com/beyonceofficial
)
$indexed = [
'https://plus.google.com/+beyonce',
'https://twitter.com/Beyonce',
'https://www.facebook.com/beyonce',
'https://www.instagram.com/beyonce/',
'http://www.weibo.com/beyonceofficial',
];
Assuming the host consists of 2 or 3 parts
$assoc = [];
foreach($indexed as $url) {
$host = explode('.', parse_url($url, PHP_URL_HOST));
switch (count($host)) {
case 2:
$assoc[$host[0]] = $url;
break;
case 3:
$assoc[$host[1]] = $url;
}
}
Will output $assoc as
array(5) {
'google' => string(32) "https://plus.google.com/+beyonce"
'twitter' => string(27) "https://twitter.com/Beyonce"
'facebook' => string(32) "https://www.facebook.com/beyonce"
'instagram' => string(34) "https://www.instagram.com/beyonce/"
'weibo' => string(36) "http://www.weibo.com/beyonceofficial"
}
This maybe? Hope to help
EDITED
$array = Array (
0 => 'https://plus.google.com/+beyonce',
1 => 'https://twitter.com/Beyonce',
2 => 'https://www.facebook.com/beyonce',
3 => 'https://www.instagram.com/beyonce/',
4 => 'http://www.weibo.com/beyonceofficial',
6 => 'http://www.bbc.co.uk/a/witty/documentary'
);
$output = Array();
foreach($array as $key => $url){
$urlarr = parse_url($url);
$arr = explode('.',$urlarr['host']);
$name = $arr[count($arr) - 2];
if($name == 'co'){
$name = $arr[count($arr) - 3];
}
$output[$name] = $url;
}
print_r($output);
You may want to use strpos() so you can find the words and add specific key, something like this:
$new_array = [];
foreach($old_array AS $value){
if(strpos($value, 'facebook')) {
$new_array['facebook'] = $value;
}elseif(strpos($value, 'instagram')) {
$new_array['instagram'] = $value;
}elseif(strpos($value, 'twitter')) {
$new_array['twitter'] = $value;
}else{ //so it goes...
$new_array['unknow'] = $value;
}
}
Of course if you have two url with facebook, the second will overwrite the first one so depends of what you need you may add an aditional index... like:
$new_array['facebook'][] = $value;
Hope it helps!
An quick/easy-ish way to approach this might be to do a regex match on the domains.
For example, if you have a finite set of social media and can trust the middle of the domain name, then you can extract that part of the url to put it as part of your key.
For example the regex /google|twitter/ would match google from https://plus.google.com/+beyonce. This can then be used for your key.
So looping through your set, you'd run preg_match which would attempt to find the key based on the domain name. preg_match has a parameter called $match that then stores the google part of what you're matching. You can use this to be the key of your formatted array.
$array = [
'https://plus.google.com/+beyonce',
'https://twitter.com/Beyonce',
'https://www.facebook.com/beyonce',
'https://www.instagram.com/beyonce/',
'http://badmedia.com/beyonce',
'http://www.weibo.com/beyonceofficial'
];
$keyed = [];
foreach ($array as $i => $url) {
preg_match('/google|twitter|facebook|instagram|weibo/', $url, $match);
$key = isset($match[0]) ? $match[0] : 'notfound-' . $i;
$keyed[$key] = $url;
}
The result here is that $keyed now contains your array for $keyed['google'] and the others.
Add or remove from the google|twitter|facebook|instagram|weibo to have a |another where you want to have more or less support for different sites.
It's not very scalable, and if the domain doesn't match a key you want to use, then in this case I've set it to create a key called notfound- with the original array key. (I've added badmedia.com to the list to demonstrate it.). You also run the risk of having a conflict where someone might be smart and have their Facebook username as "nottwitter" which will technically match "twitter"
So what you might want to consider rather than relying on the domain to provide your key for you, to keep a regex match for each full domain name. You could store these regex queries against the key you wanted to use, and nest your two loops to find a match...
For example:
$socialMedia = [
'facebook' => '/^(https?:\/\/)?(www\.)?facebook.com\/[a-zA-Z0-9(\.\?)?]/',
]
foreach ($array as $url) {
foreach ($socialMedia as $key => $regex) {
if (preg_match($regex, $url)) {
$keyed[$key] = $url;
}
}
}
(Shoutout to https://gist.github.com/atomicpages/4619196 for the full FB domain regex)
For very small sets (which I imagine you're dealing with at any one time) this would scale slightly better and give you a better result... It also rejects and removes any you don't support, but you would need to catch those separately.
For larger sets you probably want to look at how you collect the urls in the first place... Rather than accept any list, maybe have the form or API call you're receiving these from state which one it is on the way in.
This code works great, until something is case sensitive. I've tried using strnatcasecmp instead of the cmp, and it grabs the whole string not the last name. any suggestions on how to write this better?
for example the current order is:
Aaron Carson
Adam Davidson
Jen Hennings
ektor bluemouth
I want ektor bluemouth to be on top.
foreach($connected->posts as $p) {
$lastname = explode(' ', $p->post_title);
if(sizeof($lastname) == 1) {$p->lastname = $lastname[0];}
if(sizeof($lastname) == 2) {$p->lastname = $lastname[1];}
if(sizeof($lastname) == 3) {$p->lastname = $lastname[2];}
}
usort($connected->posts, 'cmp');
I would do something like this
$aPosts = array(
array('post_title' => 'Aaron Carson'),
array('post_title' => 'Adam Davidson'),
array('post_title' => 'Jen Hennings'),
array('post_title' => 'ektor bluemouth'),
);
$aOrderedPosts = array();
foreach($aPosts as $iPos => $p){
$a = explode(' ', $p['post_title']);
// cast the last name to lowercase and assign by reference the post
$aOrderedPosts[strtolower($a[1])] =& $aPosts[$iPos];
}
var_dump($aOrderedPosts);
// perform a key sort
ksort($aOrderedPosts);
var_dump($aOrderedPosts);
It could be adapted to work with your data format
$aOrderedPosts = array();
foreach($connected->posts as $iPos => $p){
$a = explode(' ', $p->post_title);
$aOrderedPosts[strtolower($a[1])] =& $connected->posts[$iPos];
}
ksort($aOrderedPosts);
var_dump($aOrderedPosts);
Best to create $aOrderedPosts when you create/read in the post objects from DB. Additionally I would create it as a property of the connection class.
What: I'm attempting to compare data in two arrays and write a statement based on the comparison,
$sys = array("1"=>'kitchen lights', "2"=>'living lights', "3"=>'living fan');
$input = array('off kitchen lights','on living fan');
Note: The input can come in any order! :-/ any ideas
Compare these to allow for me to change the state in a database and write to a change log.
The sys array key is also important here.
Im shooting for a result like the following:
$write = '1:0,2:256';// means off kitchen lights and on living fan
The write is broken into bits like this:
($sys array key number):('256' on or off '0'),(separate next listing...)
Im familiar with array_intersect.
$wordin = explode(" ", $input);
$wordsys = explode(" ", $sys);
$result = array_intersect($wordin, $wordsys);
Im sure I could loop through the array looking for lets say on and replace it with 256 or 0 but im running to issues thinking of how to do the following:
Handle variations like lights versus light...I need them to be equal for this...
Preserve the sys array key number
Note: Im not sure of a "easier" method but I will take any feed back!
Thanks,
JT
More Info: A user types a string. Im pulling all the detail out of the string and arriving at the input array. The sys is a predefined database that the user set up.
To have different triggers for the same thing, you can do something like this (allows you to add more triggers easily). You could also place some regex in the triggers and evaluate them, but you can figure that out yourself ;)
<?php
define('SWITCHED_ON', 256);
define('SWITCHED_OFF', 0);
$sys = array(
'1' => array(
'name' => 'Kitchen Lights',
'triggers' => array(
'kitchen light',
'kitchen lights',
),
),
'2' => array(
'name' => 'Living Lights',
'triggers' => array(
'living lights',
'lights in living room',
'light in living room',
),
),
'3' => array(
'name' => 'Living Fan',
'triggers' => array(
'living fan',
'fan in living room',
),
),
);
$input = array('off kitchen lights','on living fan');
$output = array();
foreach ( $input as $command ) {
// split command at first whitespace
// $command_array = preg_split('%\s+%', $command, 2);
// update to allow input like $input = array('kitchen off lights','living fan on');
$split = preg_split('%\s+%', $command);
$input_switch = false;
$input_trigger = array();
foreach ( $split as $part ) {
if ( $input_switch === false ) {
switch ( $part ) {
case 'on': $input_switch = SWITCHED_ON; break;
case 'off': $input_switch = SWITCHED_OFF; break;
default: $input_trigger[] = $part; break;
}
} else {
$input_trigger[] = $part;
}
}
if ( $input_switch === false || empty($input_trigger) ) {
continue;
}
$input_trigger = implode(' ', $input_trigger);
// insert check if command is valid (for example contains only spaces and alphanumerics.. etc..)
// ...
foreach ( $sys as $syskey => $conf ) {
foreach ( $conf['triggers'] as $trigger ) {
if ( $trigger == $input_trigger ) {
$output[] = $syskey.':'.$input_switch;
continue 3; // continue outer foreach
}
}
}
// if you arrive here, the command was not found in sys
}
$output = implode(',', $output);
echo $output;
PS: The $sys array looks different, but as u say the user sets them up. So there would be no way to check for all cases of "kitchen lights", "kitchen light", and what other stuff the user puts into the array. So they could just fill the array like above, with different triggers for the same thing. I think the ease of use makes up the extra structure of the new $sys. ^^
UPDATE: Updated to allow unordered input. I think the unordered input is kind of hard to deal with, if you can not be sure how many instances of the word "off" or "on" are found in one command. If there are more instances, you won't be able to decide which "on" or "off" is the correct one. There could be a rule.. like "The first instance of "on" or "off" is the one we'll use" or something. The code above will use that rule. So if you input a command like "kitchen off lights on off", it will result in trying to turn OFF the thing that has a trigger "kitchen lights on off". Another possible way is to reject the command if there are more instances of "on"|"off". Or to cut multiple instances of "on"|"off".
Try this:
$values = array();
foreach ($input as $i) {
$parts = explode(' ', $i);
// first word: 'on' || 'off'
$val = array_shift($parts);
// attach the remaining words again to form the key
$key = implode(' ', $parts);
// get the index of the $key value in $sys array
// and concat 0 or 156 depending on $val
$values[] = array_shift(array_keys($sys, $key)).':'.($val == 'on' ? 256: 0);
}
$write = implode(';', $values);
makes use of the second parameter of array_keys to fetch the correct key of the $sys array.
See it in action in this fiddle
edit
For managing different inputs in different formats (without changing the $sys array):
$alts = array(
'kitchen lights' => array(
'kitchen lights', 'kitchen lights', 'lights in kitchen', 'light in kitchen'
),
'living fan' => array(
'living fan', 'living fans', 'fans in living', 'fan in living'
),
);
foreach ($input as $i) {
$i = strtolower($i); // make sure we have all lower caps
// check if on in in the start or beginning of the input
$flag = substr($i, 0, 2) === 'on' || strpos($i, strlen($i)-1, 2) === 'on';
// remove on and off from the string, trim whitespace
$search = trim(str_replace(array('on', 'off'), '', $i));
// search for the resulting string in any of the alt arrays
$foundSysKey = false;
foreach ($alts as $sysKey => $alt) {
if (in_array($search, $alt)) {
$foundSysKey = $sysKey;
break;
}
}
// did not find it? continue to the next one
if ($foundSysKey === false) {
echo 'invalid key: '.$search;
continue;
}
// now you have the info we need and can precede as in the previous example
$values[] = array_shift(array_keys($sys, $foundSysKey)).':'.($flag ? 256: 0);
}
I tried saving an updated fiddle but the site seems to have some problems... it did work though.
I have in PHP an array string which contain stuff like this:
$a = array(
array('inner' => 'N.person = P.id'),
array('inner' => 'L.person = P.id'),
array('left' => 'A.person = P.id AND A.excluded IS NULL')
);
And I want to inform primary key P.id and remove all alias from sql resulting in something like this:
$x = someFunctionThatSanitizeSQL('P.id', $a);
print_r on $x result:
array(
array('inner' => 'person = :pk'),
array('inner' => 'person = :pk'),
array('left' => 'person = :pk AND excluded IS NULL')
);
Thanks in advance
#edit: miss a typo
After doing a str_replace($pkreplace, ':pk', $mystring);, the regex I think you're looking for is something like:
/(?=(^|\w))[A-Z0-9_-]+\./
For example, to clean out each string, you can do
preg_replace('/(?=(^|\w))[A-Z0-9_-]+\./i', '', $mystring);
However, if you are removing the A., L., etc., there may be other issues with your query due to the values no longer being under the database tables and possibly causing ambiguous value errors. Are you certain this is the behavior you want in your queries?
EDIT
A final implementation of your function would look something like (assuming you need arrays):
function someFunctionThatSanitizeSQL($pkreplace, $dataarray) {
if (!is_array($dataarray))
return null;
$retval = array();
foreach ($dataarray as $key => & $curentry) {
$curstring = str_replace($pkreplace, ':pk', $curentry);
$curstring = preg_replace('/(?=(^|\w))[A-Z0-9_-]+\./i', '', $curstring);
$retval[$key] = $curstring;
}
return $retval;
}
I use foreach to loop the array below and then making the order number to be store in the database,
$items_variable = array(
'page_id',
'page_content_1',
'page_content_2',
'page_content_3',
'page_content_4',
...
);
The code only loop 4 items from the array above (but what if I have these page_content_# increased in the future?)
foreach( $items_variable as $item_variable )
{
if (in_array($item_variable, array(
'page_content_1',
'page_content_2',
'page_content_3',
'page_content_4'
)))
{
if($item_variable == 'page_content_1') $order_in_page = 1;
if($item_variable == 'page_content_2') $order_in_page = 2;
if($item_variable == 'page_content_3') $order_in_page = 3;
if($item_variable == 'page_content_4') $order_in_page = 4;
....
}
}
The current method I have above doesn't look good to me especially when it comes to the line like this,
if($item_variable == 'page_content_1') $order_in_page = 1;
I will add more lines like this when I have page_content_# increased in the future and the script will look pretty ugly I suppose.
What if I have another type of data with order number (for instance - code_1, code_2, etc)? Then I have copy the code above and change the item name each time - this looks pretty gloomy isn't!
How can I make it better and dynamic?
Associative array
You can do this:
$items_definitions = array(
'page_content_1' => 1,
'page_content_2' => 2,
'page_content_3' => 3,
'page_content_4' => 4,
'page_content_5' => 5,
);
foreach( $items_variable as $item_variable ){
if( isset( $items_definitions[ $item_variable])){
$order_in_page = $items_definitions[ $item_variable];
}
...
}
Dynamically extracting last part of string
Or do it completely dynamically assuming that it's always page_content_{$order_in_page}, either with regexp as hackartist suggested or use "oldschool method":
$prefix = 'page_content_';
foreach( $items_variable as $item_variable ){
if( strncmp( $item_variable, $pregix, strlen( $prefix))){
continue; // Assume that you don't want to do anything if it doesn't match
}
$page = intval( substr( $item_variable, strlen( $prefix)));
if( !$page){
continue;
}
$order_in_page = $page;
}
I recommend studying examples from intval() documentation :)
Switch statement
Php provides switch which allows you to handle many different cases with relatively small amount of code.
foreach( $items_variable as $item_variable ){
switch( $item_variable){
case 'page_content_1':
$order_in_page = 1;
break;
case 'page_content_2':
$order_in_page = 2;
break;
case 'page_content_3':
$order_in_page = 3;
break;
...
default:
}
}
I'd however do this only if first two options wouldn't work out for you (eg. you need to call different function for each case).
not sure exactly what you want but try this instead of the if statement:
preg_match('/page_content_([0-9]+)/',$item_variable,$matches);
$order_in_page = $matches[1];
I'm not sure I understand your question, but maybe an associative array would be a solution for you. You can use it to match a string to a value:
$order_in_page = array(
'page_content_1' => 1,
'page_content_2' => 2,
'page_content_3' => 3,
'page_content_4' => 4,
'someotherpage' => 5,
'yet_another_page' => 6
);
$o = $order_in_page[$item_variable];
Stuff on the data structure
http://en.wikipedia.org/wiki/Associative_array
PHP Documentation
http://php.net/manual/de/language.types.array.php
Your current array, if you write it out with the explicit keys, looks like the following:
$items_variable = array(
0 => 'page_id',
1 => 'page_content_1',
2 => 'page_content_2',
3 => 'page_content_3',
4 => 'page_content_4',
...
);
Notice that the key number matches completely with the page content number. Thus, you could change the foreach loop to the following:
foreach( $items_variable as $order_in_page => $item_variable )
Now $order_in_page should be stored with the key number, which in your array correlates directly to the page content number. You may need to cast it into an int, although I am not sure of this fact:
$order_in_page = (int) $order_in_page;
If instead, your array looked like the following (without the 'page_id' element):
$items_variable = array(
0 => 'page_content_1',
1 => 'page_content_2',
2 => 'page_content_3',
3 => 'page_content_4',
...
);
Do the same thing as above, but add one to the result:
++$order_in_page;
If casting is needed, cast before the increment.
foreach($items_variable as $item_variable) {
preg_match('/[0-9]*$/',$item_variable , $suffix);
$suffix = $suffix[0];
if ($suffix !== '') {
# code 1
} else {
# code 2
}
}
I'm not sure I understood your question, but you might want to store your data in a multidimensional array, like:
$items_variable = array(
'page_id',
'content' => array(
//content
)
);
Then you could just iterate through each content-array, such as:
foreach($items_variable['content'] as $content) {
//do stuff with the content
}
No need for regex or other stuff.