How to call an outside function within a WP loop and get the "next" item, but for specific slugs - php

In my WordPress child theme workspace, I am trying to learn how to optimize my code below to be more efficient in its PHP process. I have 10 images total that I'm working with. I want any number of these images to be called onto different pages by slug. I know in PHP how to return once from a function, but how do I say in PHP...
"I want photos 1, 2, 6 to go to slugA"
"I want photos 2, 3, 5, 9 to go to slugB"
"I want photos 1, 7 to go to slugC"
etc...
This is what I have so far
//DEFINING ALL MY PHOTOS AND RETURNING THEM
function my_post_photos (
$mQuery = 1,
$image01 = "filler_01.jpg",
$image02 = "filler_02.jpg",
$image03 = "filler_03.jpg",
$image04 = "filler_04.jpg",
$image05 = "filler_05.jpg",
$image06 = "filler_06.jpg",
$image07 = "filler_07.jpg",
$image08 = "filler_08.jpg",
$image09 = "filler_09.jpg",
$image10 = "filler_10.jpg"
): string
{
$vars = get_defined_vars();
foreach ($vars as $key => $value) {
$placeholders[] = get_stylesheet_directory_uri() . '/dist/images/' . $value;
}
return $placeholders[$mQuery];
}
//FURTHER DOWN THIS SAME PAGE...WITHIN MY WP LOOP, CALLING THEM
<?php
$query = new WP_Query($args);
if ($query->have_posts()) :
//WITH THE BELOW $i, I AM FULLY AWARE THAT THE NUMBER OF PHOTOS LOADS IS DEPENDENT ON HOW MANY TIMES MY LOOP CYCLES. I'M COOL WITH THAT BECAUSE I'M EVENTUALLY GOING TO HAVE MORE PHOTOS THAN POSTS.
$i=1;
while ($query->have_posts()) : $query->the_post();
$current_page = sanitize_post($GLOBALS['wp_the_query']->get_queried_object());
$slug = $current_page->post_name;
?>
<?php
if ($slug == "slugA") {
?>
<div>
<h6>
<!-- //THIS FUNCTION LOADS EACH PHOTO PERFECTLY FINE, BUT HOW CAN I ONLY RETURN PHOTOS 1, 2, 6 HERE? -->
<img src="<?php echo my_career_placeholder($i); ?>" border="0" alt="">
</h6>
</div>
<?php
} elseif ($slug = "slugB") {
//HOW CAN I RETURN ONLY PHOTOS 2, 3, 5, 9 HERE?
} elseif ($slug = "slugC") {
//HOW CAN I RETURN ONLY PHOTOS 1, 7 HERE?
}
?>
<?php
$i++;
endif;
endwhile;
endif;
wp_reset_postdata();
?>

Looking at your code as a whole, I think your main function could be rewritten as follows:
function my_post_photos(...$indexes): array
{
$pathPrefix = get_stylesheet_directory_uri().'/dist/images/';
$placeholders = [
$pathPrefix.'filler_01.jpg',
$pathPrefix.'filler_02.jpg',
$pathPrefix.'filler_03.jpg',
$pathPrefix.'filler_04.jpg',
$pathPrefix.'filler_05.jpg',
$pathPrefix.'filler_06.jpg',
$pathPrefix.'filler_07.jpg',
$pathPrefix.'filler_08.jpg',
$pathPrefix.'filler_09.jpg',
$pathPrefix.'filler_10.jpg',
];
$ret = [];
foreach ($indexes as $index) {
if (isset($placeholders[$index])) {
$ret[] = $placeholders[$index];
}
}
return $ret;
}
This would allow you to call it and pass as many indexes in as you want by doing my_post_photos(3,5), which would return an array that you could loop over.
Demo here: https://3v4l.org/8aq6d
Additional optimizations could be done if you truly name your images in that consistent manner, but I'm just keeping this simple for now.
edit
Based on your comments, I think you want to call the function in the loop and get the "next" item, but for the specific slug.
If that's right, one way to do this would be to create a second array that maps slugs to image indexes. You can then use static with that array which means that subsequent calls to the function won't recreate the array, but instead use the first one. Lastly, you can use array_shift which gets you the first item from an array and actually removes that index from the array. (The function array_shift is considered non-performant for large arrays, if I understand things correctly, but I don't think you'll notice that here at all.)
The code below changes this back to returning a string (or null), and can be called repeatadly with a known-slug to get the "next" item. If you'd ever have more posts than images you could take the "next" and append it to the end of the array giving you a circular pool, too.
function my_post_photos(string $slug): ?string
{
$pathPrefix = get_stylesheet_directory_uri().'/dist/images/';
$placeholders = [
$pathPrefix.'filler_01.jpg',
$pathPrefix.'filler_02.jpg',
$pathPrefix.'filler_03.jpg',
$pathPrefix.'filler_04.jpg',
$pathPrefix.'filler_05.jpg',
$pathPrefix.'filler_06.jpg',
$pathPrefix.'filler_07.jpg',
$pathPrefix.'filler_08.jpg',
$pathPrefix.'filler_09.jpg',
$pathPrefix.'filler_10.jpg',
];
// Mapping of slugs to placeholders
// NOTE: This array is created static so that it only ever
// gets created once in the lifetime of the PHP request
static $slugToPlaceholders = [
'slugA' => [1, 2, 6],
'slugB' => [2, 3, 5, 9],
'slugC' => [1, 7],
];
// Unknown slug, return null
if(!isset($slugToPlaceholders[$slug])){
return null;
}
// Grab the next unused index, and remove it from the array
$nextIndex = array_shift($slugToPlaceholders[$slug]);
// Return either the image or nothing if we're out of indexes
return $placeholders[$nextIndex] ?? null;
}
Demo: https://3v4l.org/JDKOH

Related

Very Complex SQL Data Result Merge via PHP (Multi Dimentional Arrays)

Sorry, I have a hard time to write proper thread title for this problem.
Here's my question:
In short: How to merge array item and calculate.
Here's the full explanation
1) I have a (WP custom) database to track statistics: visits, unique visits, etc, and it's stored per post per date.
To make it easier to understand. Here's the screenshot of the table:
2) This is the example data when I queried it:
https://gist.github.com/turtlepod/8e7dc93bae7f0b665fd5aea8a9694998
So in this example we have multiple post ID: "90", "121", & "231"
We have multiple date in db: "2017-03-20", "2017-03-21", "2017-03-22"
We have multiple stats: "visits", and "unique_visits"
We also have a "stat_value" for each item.
Each item have unique ID.
All data is dynamically created when an event happen. so not all post_id have 2 stats or the above date.
Note: keep in mind that in real code, we have a lot more data and variations than the example above.
3) I need to merge the data:
The post_id "121" is the same as post "231", so we need to merge and add the "stat_value" into one data and remove "231" entry.
What is the best way to do this (dynamically) via PHP ?
I have this data:
$raw_data = array( ... ); // the one in github gist
$post_groups = array(
'121' => array( '121', '231' ), // main post_id => array of alias.
);
It need to return the same data format as $raw_data, but remove the data of "231" and include/sum the "stat_value" of "231" to "121".
Thank you.
Try it with this:
function david_transform_data($data, $groups) {
if (empty($groups) === true) {
return $data;
}
// Transform groups into a more useful format
$transformed_groups = array();
foreach ($groups as $post_id => $aliases) {
foreach ($aliases as $alias) {
if (absint($post_id) === absint($alias)) {
continue;
}
$transformed_groups[absint($alias)] = $post_id;
}
}
// Replace aliases with the real post id
foreach ($data as $index => $stat) {
if (isset($transformed_groups[absint($stat->post_id)]) === false) {
continue;
}
$data[$index]->post_id = $transformed_groups[absint($stat->post_id)];
}
// Go through stats and merge those with the same post_id, stat_id
// and stat_date
$merged_stats = array();
$index_tracker = 0;
$stats_hash = array();
foreach ($data as $index => $stat) {
$hash_key = sprintf(
'%s-%s-%s',
$stat->post_id,
$stat->stat_id,
$stat->stat_date
);
if (isset($stats_hash[$hash_key]) === true) {
$merged_stats[$stats_hash[$hash_key]]->stat_value += absint($stat->stat_value);
continue;
}
$merged_stats[] = $stat;
$stats_hash[$hash_key] = $index_tracker;
$index_tracker++;
}
return $merged_stats;
}
var_dump(david_transform_data($raw_data, $post_groups));
There might be a faster solution but this is the first thing that came to my mind.

How to paginate a foreach loop?

I have this foreach loop wich shows the supporters located in the post_meta from a custom post type. What I want to do is add pagination to the foreach loop. I have already found a way to decide how many supporters are shown by slicing the array, but now I am at a loss. And have no idea how to proceed.
Function to get the supporters array
function getSupporters($petitieID){
$support = get_post_meta(get_the_ID(), 'supporters', true);
if (!empty($support)){
return $support;
}}
Function to show the individual supporters in the array
function showSupporters($petitieID){
$supporters = getSupporters($petitieID);
if (!empty($supporters)){
foreach (array_slice($supporters, 0, 2) as $supporter){
$supporterID = $supporter->post_author;
the_author_meta('first_name', $supporterID);
}
}else {
echo 'no votes';
}
}
You could determine which page is currently shown in a GET variable in your address
.../supporters.php?page=1
Then you could set the offset of your array_slice function accordingly
$nItemsPerPage = 2;
$page = isset($_GET['page'])?$_GET['page']:1;
array_slice($supporters, $nItemsPerPage*($page-1), $nItemsPerPage)

php function returning null value

I am recursively searching a tree node for its parents, and then trying to return its parent categories in an array.
The function receives and passes itself an array of each parent which is finally returned.
Even though this array has elements, the statement before the return when viewed outside the function is nul.
To make it work, I just made the parameter by reference. But why is it always nul?
Here is my code:
function getParent($id,$parents){ // to work changed this to getParent($id,&$parents)
if($id < 2) { // 1 is the Top of the Tree , so job is done
return $string;
}
$child = DB::fetchExecute((object)array( // pdo query for category to get parents
'sql' => "category",
'values'=> array($id),
'single'=> 1,
'debug' => 0)
);
$parent = DB::fetchExecute((object)array( // pdo query for parents info
'sql' => "category",
'values'=> array($child->native_parent_category_id),
'single'=> 1,
'debug' => 0)
);
$string[]= "<li>$parent->name ($parent->native_category_id)</li>
";
getParent($parent->native_category_id , $parents);
}
// call function
$array = array();
$returnString = getParent($id,$string);
var_dump($returnString,$array); // will both be NULL, or if called by Reference $array has the goods
?>
Change:
function getParent($id,$parents){
To:
function getParent($id,$parents, $string){
And change:
getParent($parent->native_category_id , $parents);
To:
getParent($parent->native_category_id , $parents, $string);
The scope of $string only exists in the function as it runs - so, if you rerun the function, it will reset all the variables within it. You need to send the variable each time you rerun it.
I would declare $string before function, and inside function, use global $string; at top.

How to sort a complex PHP array

I am trying to reorganize a PHP array (on a WordPress page). My site was coded by someone else, and I'm trying to make things better. I think they did things in a confusing way (poor use of the categories). Rather than recode the entire site, I wanted to see if someone could help me work around a fix.
So, basically, I have an array inside an array. A list of categories, and each category has specific details. I'm calling the categories, based on it's parent. I need to reorganize the list/array of categories, so they can print out in the order that I need them to.
$cat_tree = get_category_parents(wt_get_category_ID(), FALSE, ':', TRUE);
$top_cat = split(':',$cat_tree);
$parent = $top_cat[0];
$idObj = get_category_by_slug($parent);
$args = array(
'child_of' => $idObj->term_id,
'orderby' => 'name',
'order' => 'ASC',
'hide_empty' => 0);
$counter_c=1;
$categories = get_categories( $args );
if($counter_c == 1) $counter_c = '';
foreach ($categories as $category) {
?>
<div> this is where each category array info gets printed... information goes:<br />
Name: <?=$category->cat_name ?> (<?=$category->cat_ID ?>)
</div>
<?php if($counter_c == '') $counter_c = 1; $counter_c++; } // Reset Query
wp_reset_query();
?>
On one page, $cat_tree outputs "entertainment:actors:".
$parent or $topcat[0] outputs "entertainment"
$topcat[1] outputs "actors"
When the "foreach" runs, I get an list sorted by the ID ...
Actors (13)
Artists (14)
Directors (25)
Writers (26)
DeeJays (35)
I need to reorganize this list into a specific order. Each page is different. So I may have to reorganize a list differently for each page. I may need...
Writers (26)
Actors (13)
DeeJays (35)
Artists (14)
Directors (25)
So, I need a way to do this. I thought that maybe I could say "if" on the "actor page", slide "Writers" to the end of the array (I know there is some way to do this), then slide Actors to the end, then DeeJays, then Artists, then Directors. Then run the foreach.
Or, if page is something else, I can slide other elements to the end of the array. That seems a bit much, but it's the only work around that I could think of. Of course, my php knowledge is limited, so any help is appreciated.
You could try a custom sort:
$order = array('Writers', 'Actors', 'DeeJays', 'Artists', 'Directors');
function catsort($a, $b) {
global $order;
$ak = array_search($a->cat_name, $order);
$bk = array_search($b->cat_name, $order);
if($ak === false) $ak = 0;
if($bk === false) $bk = 0;
return $ak - $bk;
}
usort($categories, 'catsort');
Each page would just need a different order array.

Converting a multidimensional array into a single dimensional one

If from a function I am returned a multidimensional array like this..
array(0 => array('a' => 5), 1 => array('a' => 8))
But I just really need the contents of the key 'a' what is the best way for me to convert.
Current I have been doing something like..
$new_array = array();
foreach ($multi_array AS $row) {
$new_array[] = $row['a']
}
without foreach:
$array = array(0 => array('a' => 1), 1 => array('a' => 8));
$new_array = array_reduce($array, function ($result, $current) {$result[]=current($current); return $result;}, array());
If that is all your requirements are, I think that is the best way. Anything else will have the same processing. Even after looking through the Array functions, I would say that this would be the best way. You can, however, make it a function to make it a bit more versatile:
$array = array(0 => array('a' => 1), 1 => array('a' => 8));
$new_array = flatten($array, 'a');
function flatten($array, $index='a') {
$return = array();
if (is_array($array)) {
foreach ($array as $row) {
$return[] = $row[$index];
}
}
return $return;
}
But yea, I would say what you have would be the most efficient way of doing it.
You Can Try As Like Following ......
$multi_array = array(0 => array('a' => 5), 1 => array('a' => 8));
$new_array = array();
foreach ($multi_array AS $key => $value) {
$new_array[] = $value['a'];
}
I recently found myself facing this problem, and I believe I have found the solution.
I will go over the problem itself, and also the solution, trying to explain everything along the way.
The problem was that I didn't have a two-dimensional array, but an array which could have any number of arrays inside arrays, so the solution by Brad F Jacobs couldn't apply here, although it's very simple and functional.
I had to work with a self-referencing database table called 'webpage', where one of the columns was 'parentWebpageId', which referenced an Id of some other row in that same table. This way, a tree structure can be built and easily managed, if you get your loops right.
Ia easily made a function which is supposed to generate a multi-dimensional array from one-dimensional self-referencing array, but the problem arose when I tried to make a function which should to the opposite. I needed this because if I wanted to delete a certain webpage, all it's children should also be deleted, in order to preserve the self-referential integrity.
It was easy to generate a tree whose root was the page that was initially to be deleted, but then I needed a list of all child webpage's Ids, in order to delete all of them.
So, the structure I had was something like this:
webpage1
id
title
...
childWebpageArray
webpage2
id
title
...
childWebpageArray
webpage2.1
id
url
...
childWebpageArray
webpage2.2
id
url
...
childWebpageArray
webpage2.2.1
id
url
...
childWebpageArray
webpage2.2.2
id
url
...
childWebpageArray
webpage2.3
id
url
...
childWebpageArray
webpage3
id
title
...
childWebpageArray
As you can see, the depth can go forever.
What I came up with is this:
function flattenMultidimensionalArray($multidimensionalArray) {
// Set anchor.
ohBoyHereWeGoAgain:
// Loop through the array.
foreach ($multidimensionalArray as $key1 => $value1) {
// Check if the webpage has child webpages.
if (isset($multidimensionalArray[$key1]["childWebpageArray"]) && (count($multidimensionalArray[$key1]["childWebpageArray"]) > 0)) {
// If it does, loop through all the child webpages, and move them into the initial multi-dimensional array.
foreach ($multidimensionalArray[$key1]["childWebpageArray"] as $key2 => $value2) {
$multidimensionalArray[] = $multidimensionalArray[$key1]["childWebpageArray"][$key2];
}
// Unset element's child pages, because all those child pages, along with their child pages
// have been moved into the initial array, thus reducing the depth of a multi-dimensional array by 1.
unset($multidimensionalArray[$key1]["childWebpageArray"]);
}
}
// Loop once again through the whole initial array, in order to check if any of the pages has children
foreach ($multidimensionalArray as $key => $value) {
// If a page which has children is found, kick the script back to the beginning, and go through it again.
if (isset($multidimensionalArray[$key]["childWebpageArray"]) && (count($multidimensionalArray[$key]["childWebpageArray"]) > 0)) {
goto ohBoyHereWeGoAgain;
}
}
// In the end, when there are no more pages with children, return an array of pages.
return $multidimensionalArray;
}
This solution worked in my case, and I believe is the right one for this kind of problem. It probably isn't much of a hassle to change it in order to fit your particular needs.
Hope this helps!

Categories