manipulating post order in Timber $context['posts'] object - timber

I'm trying to ensure the first two posts in my {{ posts }} object have thumbnails while the rest of the posts continue to order by most recently published first. I am trying to create a filter like so to accomplish this:
Filter definition in PHP:
function posts_with_thumbs_first_filter( $posts, $num_top_posts = 2 ){
// find first posts with thumbnails in them to decide what posts need to be reordered to first position in the array
$first_posts_with_thumbs = array();
foreach ($posts as $key => $post) {
if ($post->thumbnail) {
$first_posts_with_thumbs[] = $key;
if (count($first_posts_with_thumbs) == $num_top_posts) { break; }
}
}
...manipulate order of posts here...
return $posts;
}
Use of filter in Twig:
{% for post in posts|posts_with_thumbs_first %}
...regular post output here, only with posts now reordered...
{% endfor %}
But it looks like manipulating the order of the posts is tricky because it's not a standard array, it's a Timber\PostQuery object with private iterators and methods inside that control the posts data. I can't use PHP's array functions like array_slice, array_splice, etc. because posts is not an array, it's an object.
Is there a solid way to manipulate the order of the posts?

Here's one way to do it: convert the posts object to an array to enable usage of PHP's array manipulation functions.
function posts_with_thumbs_first_filter( $posts, $num_top_posts = 2 ){
$posts = (array) $posts; // convert posts object to array to use PHP's array manipulating functions
$first_posts_with_thumbs = array();
foreach ($posts as $key => $post) {
if ($post->thumbnail) {
$first_posts_with_thumbs[] = $key;
if (count($first_posts_with_thumbs) === $num_top_posts) { break; }
}
}
$posts_to_move_to_top = array();
for ($i = count($first_posts_with_thumbs) - 1; $i >= 0; $i--){
$posts[$first_posts_with_thumbs[$i]]->moved = true;
$posts_to_move_to_top[] = array_splice($posts, ($first_posts_with_thumbs[$i]), 1); // extract post from posts array
}
foreach ($posts_to_move_to_top as $post) { // put back into posts at beginning
array_unshift($posts, $post[0]);
}
return $posts;
}

Related

Wordpress - setting a custom post per category

I have added the below code to the functions.php file to load different post templates per category. The code is doing what it is supposed to do. The issue I am facing is that when I open posts these load correctly but whilst loading an error is displayed for a instant saying "trying to get property of non-object in .......functions.php" How may I get rid of that error? Appreciate your help.
/* ----------------------------------------------------------------------------
custom single posts per category
*/
add_filter('single_template', 'check_for_category_single_template');
function check_for_category_single_template( $t )
{
foreach( (array) get_the_category() as $cat )
{
if ( file_exists(get_stylesheet_directory() . "/single-category-{$cat->slug}.php") ) return get_stylesheet_directory() . "/single-category-{$cat->slug}.php";
if($cat->parent)
{
$cat = get_the_category_by_ID( $cat->parent );
if ( file_exists(get_stylesheet_directory() . "/single-category-{$cat->slug}.php") ) return get_stylesheet_directory() . "/single-category-{$cat->slug}.php";
}
}
return $t;
}
The error trying to get property of non-object means, you try to get a property of something that is not an object. So looking at your code you are getting two properties with ->: "slug" and "parent".
This gives us the hint, that the value saved in the $cat variable might not be an object.
You are using get_the_category_by_ID which gives you a name, not a category object. https://developer.wordpress.org/reference/functions/get_the_category_by_id/ So you cannot access any property. The problem is this: get_the_category_by_ID( $cat->parent ); There is no parent property in $cat, because it only contains the name. You need to get the object to make use of the parent property. Moreover the property is called category_parent, and not parent.
$parent_id = $cat->category_parent;
$cat = get_category($parent_id);
This should solve your problem.
If it does not solve your problem, I wrote the code in another way. It shows single steps and maybe it is useful for you or some other users.
The function to get all your category objects is: get_categories() https://developer.wordpress.org/reference/functions/get_categories/
You can save all the IDs of the categoires into a array. After that, you loop through each position of the array and check if the single post you are viewing has the same category ID. We can check if the post is in given categories using in_category() https://developer.wordpress.org/reference/functions/in_category/
I did no testing, but the code can look something like:
add_filter('single_template', 'check_for_category_single_template');
function check_for_category_single_template() {
// Get all category objects
$categories = get_categories();
// Array for IDs
$categories_ids = [];
foreach($categories as $category) {
// add id to array
array_push($categories_ids, $category->cat_ID);
}
// Loop through array of IDs
foreach($categories_ids as $category_id) {
// Check if ID equals current post category ID
if ( in_category($category_id) ) {
// get current category object
$cat = get_category($category_id);
// check if category has parent
if( $cat->category_parent > 0 ) {
$parent_id = $cat->category_parent;
// get parent object
$cat = get_category( $parent_id );
}
// get slug of category
$cat_slug = $cat->slug;
if ( file_exists(get_stylesheet_directory() . "/single-category-" .$cat_slug. ".php") ) {
return get_stylesheet_directory() . "/single-category-" .$cat_slug. ".php";
}
}
}
}

Get parent category for CPT

I'm working on an index page that lists all posts for a custom post type. I've been listing the categories for each post using <?php echo strip_tags(get_the_term_list( $post->ID, 'genre', ' ',' • ')); ?>.
We need to add sub-categories but I don't want these to display on the index page - just the parent category. Have tried using get_term_parents_list and a few other examples from here but can't get anything working.
Can anyone help?
You can use get_the_terms filter to change the terms to return.
add_filter('get_the_terms', 'only_parent_genre', 10, 3);
function only_parent_genre($terms, $post_id, $taxonomy) {
// TODO for you : Add condition to heck if you are not on your custom index too.
if(is_admin() || $taxonomy !== 'genre') {
return $terms;
}
// Loop over terms and if parent is something different than 0, it means that's its a child term
foreach($terms as $key => $term) {
if($term->parent !== 0) {
unset($terms[$key]);
}
}
return $terms;
}

Get categories of a newly saved post inside the save_post hook

I have a post_save hook that manages setting the title of a particular post type. Within that logic I need to retrieve the post's categories. However, the categories are not yet saved at the time the post_save triggers for the first save of a new post.
add_action('save_post', 'save_report');
function save_report($post_id) {
$data = get_post($post_id, ARRAY_A);
if($data['post_type'] == 'report') {
$date = get_post_meta($post_id)['date'][0];
// I need to get the category of the post on the first time the post is saved
// $categories only gets a value AFTER the first save
$categories = get_the_terms($post_id, 'report_type');
$cat_string = '';
foreach($categories as $value) {
$cat_string .= $value->slug;
}
$new_title = date('m/d/Y', strtotime($date)).' '.$cat_string;
remove_action('save_post', 'save_report');
wp_update_post(array(
'ID' => $post_id,
'post_title' => $new_title
));
add_action('save_post', 'save_report');
}
}
Because of this, I have to save the post twice to get it to change the title to what I want. Is it possible to get the new category of the post as it is being saved like this?
Not quite sure what you're using the categories for after the fact, but you can just retrieve them after the wp_update_post runs.
add_action('save_post', 'save_report');
function save_report($post_id) {
$data = get_post($post_id, ARRAY_A);
if($data['post_type'] == 'report') {
$date = get_post_meta($post_id)['date'][0];
$new_title = date('m/d/Y', strtotime($date)).' '.$cat_string;
remove_action('save_post', 'save_report');
/*
* return the updated post ID (which is the same really as your
* initial ID, so you could just use that. But for demonstration it helps
* to see it logically.)
*/
$updated_post_id = wp_update_post(array(
'ID' => $post_id,
'post_title' => $new_title
));
$categories = get_the_terms($updated_post_id, 'report_type');
$cat_string = '';
foreach($categories as $value) {
$cat_string .= $value->slug;
}
add_action('save_post', 'save_report');
}
}
An alternative, if you're both creating and updating posts programatically is to use wp_insert_post, which covers both new creation (if you don't pass an ID), and updating (if you do pass an ID).
You could also create a separate function to retrieve IDs that you could use with any number of other functions you run.
function retrieve_report_cats($post_id) {
cats_list = array();
$categories = get_the_terms($post_id, 'report_type');
foreach($categories as $value) {
$cat = $value->slug;
$cats_list[] = $cat;
}
return $cats_list;
}

Show Post Title and how many days ago it got published in Wordpress

I want to customize my archive-list on my WordPress page so it displays something like this:
POST TITLE - 2 days ago
POST TITLE - 4 days ago
etc...
So far I only managed to display the post title with following code:
<?php wp_get_archives( array( 'type' => 'postbypost', 'limit' => 16) ); ?>
I have no idea how to move forward from this, any help?
The function wp_get_archives calls the function get_archives_link to prepare the output. The last step done in that function is to apply the filter by the same name as the function (i.e., get_archives_link). So to modify as you desire, define your own filter function and add that filter in your functions.php file.
For example, the following code would add a class to the output of the function get_archives_link.
function example_get_archives_link($link_html) {
if (is_day() || is_month() || is_year()) {
if (is_day()) {
$data = get_the_time('Y/m/d');
} elseif (is_month()) {
$data = get_the_time('Y/m');
} elseif (is_year()) {
$data = get_the_time('Y');
}
// Link to archive page
$link = home_url($data);
// Check if the link is in string
$strpos = strpos($link_html, $link);
// Add class if link has been found
if ($strpos !== false) {
$link_html = str_replace('<li>', '<li class="current-archive">', $link_html);
}
}
return $link_html;
}
add_filter("get_archives_link", "example_get_archives_link");
You can find more info about the filter in the Codex.

Wordpress: is child of category function

I I am looking for a function that merely returns true or false. The function would check if the category archive being queried is a child of a particular parent category. The function would look something like this (similar to the is_category() function):
if(is_child_of_category('3')){ //do something }
if(is_child_of_category('4')){ //do something else }
if(is_child_of_category('5')){ //do something entirely different }
I have looked through the documentation and the forums but have not found any solutions, any ideas if this has been done or how one would go about it?
/////////////
addendum
////////////
Here is the working code that I wrote with help from Gavin:
Thanks Gavin
first a function for the functions.php file to get the category id:
function getCurrentCatID(){
global $wp_query;
if(is_category() || is_single()){
$cat_ID = get_query_var('cat');
}
return $cat_ID;
}
Next use cat_is_ancestor_of
$post = $wp_query->post;
$id = getCurrentCatID();
if ( cat_is_ancestor_of(3, $id) { include(TEMPLATEPATH . '/unique.php'); }
cat_is_ancestor_of
This will return true still, however, if the category is a grandchild of the specified category.
I need something similar yesterday, so I wrote this function which hands me back an array with all the child categories + the parent-category (which might be useful if you want to, let's say, hide the comment form on all FAQ-cat posts and childs thereof, but show it on others).
Pass $catname as a var and get an array back, which you can use as an argument in e.g. in_category() function.
// Get List of Child Categories
function get_child_cats( $catname ) {
$parentcat = get_cat_ID( '$catname' );
$subcat = get_categories( array('child_of' => $parentcat ) );
$cat_array = array();
array_push($cat_array, $parentcat); // add the parent cat to the array
foreach ($subcat as $sc) {
array_push($cat_array, $sc->cat_ID);
}
return $cat_array;
}

Resources