Support » Plugins » Hacks » Adding custom loop to plugin

  • I am trying to add a custom loop to the “BAW Manual Related Posts” plugin. I believe I need to use apply_filters but am unsure as to how I go about doing so. I’ve included the snippet from the plugin file that allows custom loops as well as my custom loop below. Any insight is appreciated. Thanks in advance!

    Code from plugin file:
    $list[] = '<li style="' . esc_attr( $style ) . '" class="' . $class . '"><a href="' . esc_url( apply_filters( 'the_permalink', get_permalink( $id ) ) ) . '">' . $thumb . '<br />' . apply_filters( 'the_title', get_the_title( $id ) ) . '</a></li>';

    My custom loop:

    <div class="home-property-item">
    					                        <div class="property-detail-block">
    					                            <div class="property-pic-wrapper">
    					                               <?php
    					                                if ( has_post_thumbnail() )
    					                                {
    					                                    $image_id = get_post_thumbnail_id();
    					                                    $image_url = wp_get_attachment_image_src($image_id,'full-size', true);
    
    					                                    {
    					                                        ?>
    					                                        <a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>">
    					                                        <?php
    					                                    }
    					                                                the_post_thumbnail('property-post-thumb',array(
    					                                                                        'alt'   => trim(strip_tags( get_the_title($post->ID) )),
    					                                                                        'title' => trim(strip_tags( get_the_title($post->ID) ))
    					                                                )); ?>
    					                                    </a>
    					                                    <?php
    					                                }
    					                                ?>
    
    					                                <?php
    					                                $property_status = get_post_meta($post->ID, 'locality_status', true);?>
    					                                    <p class="<?php echo $property_status; ?>">
    					                                        <?php echo $property_status; ?>
    					                                    </p>
    					                            </div>
    
    					                            <h4>
    					                                <a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>">
    					                                    <?php the_title(); ?>
    					                                </a>
    					                            </h4>
    
    					                            <div class="features-wrapper">
    					                                <span class="bed"><div class="value"><?php echo get_post_meta($post->ID, 'locality_property_bedrooms', true); ?></div> <?php _e('Beds') ?></span>
    					                                <span class="bath"><div class="value"><?php echo get_post_meta($post->ID, 'locality_property_bathrooms', true); ?></div> <?php _e('Baths') ?></span>
    					                                <span class="size"><div class="value"><?php echo get_post_meta($post->ID, 'locality_property_size', true); echo "</div> "; theme_unit(); ?></span>
    
    					                                 <div class="price-box">
    					                                    <h5 class="price">
    					                                        <?php
    					                                            theme_currency();
    					                                            echo number_format(intval(get_post_meta($post->ID, 'locality_property_price', true)));
    					                                        ?>
    					                                    </h5>
    					                                </div>
    
    					                            </div>
    
    					                            <div class="learnmore">
    					                                <a class="blue" href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>">LEARN MORE</a>
    					                            </div>
    					                        </div>
    					                    </div>

Viewing 8 replies - 1 through 8 (of 8 total)
  • Moderator bcworkz

    (@bcworkz)

    You do not use apply_filter(), you look for occurrences of it in code to determine where you want to inject content. Once you identify the right filter, your code will use add_filter(). In order to use add_filter(), you need to provide a function to call when the apply_filter() function fires. You don’t have one yet, and you can’t necessarily just wrap template loop code into a function. Filters often want a returned value, and your template loop code outputs content, it does not build a value to return. One type can be transformed to another, but it’s not always a simple transformation.

    The other issue is your template loop code is macro scale, it outputs a variety of content. This is fine, this is the nature of loops. The issue is the plugin snippet you provided has hooks that are micro scale, they want very specific, single bits of data, you cannot hook your loop content into these filters.

    You need to find a more appropriate filter to hook. If the plugin code came from where I think it did, the filter ‘bawmrp_more_content’ may be the appropriate hook. Content returned to this filter will be output right after each plugin link. Let’s assume you were able to create a function called ‘my_property_data()’ that returns the needed content as a string instead of directly outputting it to the browser. To hook the filter with your function, you just need the line add_filter('bawmrp_more_content', 'my_property_data');

    Thread Starter aronjeney

    (@aronjeney)

    Hi again bcworkz (your hitting all my threads :). Thanks for this input. I had concluded that what you are explaining is actually how I need to do it, however I am still in question as to how I would create a function for my code above (on the macro level). I am working on this at this very moment, just very slow moving. If you could provide some insight as to how I would create a function from my code above I would be even more grateful! Thanks for all your insight so far.

    Moderator bcworkz

    (@bcworkz)

    How you would change template code into a hook function will depend on the nature of the hook. With action hooks, it is often possible to simply wrap the code in a function definition so that the HTML is directly output when the action fires. This does not always work so easily with some actions, and rarely so with filters.

    Some actions and most filters expect you to return a value, not directly output HTML. The first step still is to wrap the template code in a function definition. The only other step, albeit a large one, is to collect all the HTML output into a string variable that will be returned to the apply_filters() call. A trivial example if your template code only consisted of the first three lines.

    function my_property_data($content) {
       $content .= '<div class="home-property-item">';
       $content .= '<div class="property-detail-block">';
       $content .= '<div class="property-pic-wrapper">';
       return $content;
    }

    Note the apply_filters() call passed a $content parameter to which I simply appended more HTML, then returned the modified parameter. You need to examine the source code of any hook to determine what is passed to your hook function and what is done with the return value, the specifics will vary and you must match expectations.

    Also note that with HTML output, you must be careful the quote types are well coordinated and escaped where applicable, or else you will have a huge mess. That was a trivial example because the template code was all HTML, no PHP was involved. Another trivial transformation will occur when the template code is all PHP that does not generate any output. You simply remove the PHP block delimiters <?php ?>. This means you need to be familiar with WP template tags that do generate output. Watch out for these, as well as any echo and similar statements which also generate output. These must be converted to append content to $content instead of outputting it. In the case of echo 'some text to output'; just replace with $content .= 'some text to output';

    With template tags like the_permalink() that output HTML, there is invariably a variant that returns a value instead of outputting content, usually get_something() replaces the_something(). the_permalink(); could be transformed to $content .= get_permalink();

    Perhaps the trickiest transformation is for lines of mixed HTML and PHP content.
    <a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>">
    is transformed to
    $output .= '<a href="' . get_permalink() . '" title="' . get_the_title() . '">';
    Be careful with quotes! Another example of mixed content:
    <p class="<?php echo $property_status; ?>">
    is transformed to
    $output .= "<p class=\"$property_status\">";
    This time I used double quotes in PHP so I can have PHP expand $property_status for me instead of concatenating it. This means I need to escape HTML double quotes. This also would work, but is harder to read IMO (too many quotes to coordinate):
    $output .= '<p class="' . $property_status .'">';
    Finally, since HTML accepts either quote type, you could also do this:
    $output .= "<p class='$property_status'>";
    Also remember to use curly brackets {} around complex or ambiguous variables to be expanded inside double quoted strings. Example with a complex variable:
    $output .= "<p class=\"{$property_status[0]->previous}\">";

    The main thing is to be careful and meticulous doing such a transformation, and be sure you understand what every PHP function is doing, as how it is handled varies accordingly. Good luck!

    Thread Starter aronjeney

    (@aronjeney)

    Hey bcworkz, I really appreciate you taking the time to write all of this information. It really helps me understand the complexities of writing php on a micro scale vs macro scale as you worded it. You have definitely guided me in the right direction, however I have a lot of work to do to incorporate my php code from my template file into my function. I will continue to research more about this matter while using your input as reference. Again, thanks so much for your time. I will let you know how it turns out!

    Thread Starter aronjeney

    (@aronjeney)

    Well, I am still having troubles adding and applying my own filters, however I have been able to kindof hack the plugin code to call the html of my loop – not rocket science I know but I feel like I’m getting somewhere at least. That said, I’m really having trouble condensing my php properly either with filters or as it is now (still somewhat confused as what I need to do there).

    See my current code below.

    $_content = apply_filters( 'bawmrp_more_content', $_content, $output );
    					$thumb_size = apply_filters( 'bawmrp_thumb_size', array( 300, 300 ) );
    					$thumb = has_post_thumbnail( $id ) ? get_the_post_thumbnail( $id, $thumb_size ) : '<img src="' . $no_thumb . '" height="' . $thumb_size[0] . '" width="' . $thumb_size[1] . '" />';
    
    					$list[] = '<div class="home-property-item">
    									<div class="property-detail-block">
    										<div class="property-pic-wrapper">
    											<a href="' . esc_url( apply_filters( 'the_permalink', get_permalink( $id ) ) ) . '">' . $thumb  . '</a>
    											<h4><a href="' . esc_url( apply_filters( 'the_permalink', get_permalink( $id ) ) ) . '">' . apply_filters( 'the_title', get_the_title( $id ) )  . '</a></h4>
    										</div>
    
    									   <div class="features-wrapper">
    									   		<span class="bed"><div class="value"></div></span>
    									   		<span class="bath"><div class="value"></div></span>
    									   		<span class="size"><div class="value"></div></span>
    									   		<div class="price-box">
    							                    <h5 class="price"></h5>
    							                </div>
    							            </div>
    
    								   		<div class="learnmore">
    											<a href="' . esc_url( apply_filters( 'the_permalink', get_permalink( $id ) ) ) . '" class="blue">LEARN MORE</a>
    										</div>
    							   		</div>
    							   </div>';

    Again, I have wrapped the current thumbnail and title filters with my html. How would I go about condensing the following code to fit within the “bed” span class?

    <span class="bed"><div class="value"><?php echo get_post_meta($post->ID, 'locality_property_bedrooms', true); ?></div> <?php _e('Beds') ?></span>

    Again, really appreciate all the help. I really am trying to do this on my own but just not getting too far. Thanks again in advance!

    Thread Starter aronjeney

    (@aronjeney)

    Ok, I have been able to call the post meta by adding apply_filters(‘get_post_meta’…, HOWEVER, it is calling the post meta of the current post that is being viewed rather than the post meta of the manually added posts.

    See my code below..

    `<span class=”bed”><div class=”value”>’ . apply_filters( ‘get_post_meta’, get_post_meta($post->ID, ‘locality_property_bedrooms’, true) ) . ‘</div>Beds</span>’

    What am I doing wrong? :/

    Moderator bcworkz

    (@bcworkz)

    Sorry to hear you are struggling with this. I know how frustrating it is. Believe it or not, your efforts will pay off later if you continue with any kind of coding. It’s all a learning experience. Perhaps not the most efficient way to learn. When I learn this way, I tend to never forget what I learned though.

    You appear to be confused with how filters work. For one thing, there is no ‘get_post_meta’ filter AFAIK. Either way, there’s little point in creating your own filters (what you do with apply_filters()) unless you anticipate others extending your code and you wish to provide a clean way for them to latch into particular processes to modify the results. The huge number of hooks in WP is what makes it easily extendable, once you understand how it all works. Instead of apply_filters(), you could simply directly assign whatever you pass as the 2nd parameter of apply_filters().

    Of course, to hack WP, you use filters a lot, but never apply_filters(). You hook into the apply_filters() calls that WP uses with add_filter(). The function that you specify in add_filter() ends up being called (via call_user_func()) when the apply_filters() function is encountered in the WP core code. What value your filter function returns ends up being returned by the apply_filters() call.

    In other words, apply_filters() does not alter anything, it creates the opportunity for others to alter something. In order to alter something that uses apply_filters(), you must hook into it with add_filter(). Review Plugin_API#Filters for more detailed information. It’s specific for plugins, but the concept applies to themes and child themes as well.

    Maybe an example will make more sense. Let’s say we want to wrap the output of all permalinks in a <span> block for special styling from our CSS page. We could do this without filters of course, but there are many cases where hooking the filter is the only viable option. In addition, the filter I hook is not the best one to use, but it more clearly illustrates the process. Even though my example is unrealistic, it should illustrate the concept.

    Let’s say we have the following code on one of our templates somewhere:
    echo get_permalink($post->ID);

    If we check the source code of get_permalink(), we find the final line is:
    return apply_filters('post_link', $permalink, $post, $leavename);

    So now we know whatever is returned by apply_filters() is in turn returned by get_permalink(). And on our template, whatever is returned by get_permalink() is immediately echoed out to the user’s browser. Now we know if we hook ‘post_link’, what ever our filter function returns will be immediately echoed out to the browser. We can also see our filter function will be passed 3 different variables with useful data. As a rule, if we do nothing else, we should return the 2nd parameter $permalink to maintain the integrity of the filter stack. Remember that other plugins may be hooking the same filter. If we do not return anything, the chain is broken.

    To hook the ‘post_link’ filter, we write:
    add_filter('post_link', 'span_wrap_link', 10, 3);

    ‘span_wrap_link’ is a function we will define next. 10 is the priority of execution in the filter stack. 10 is the default value. Smaller priority numbers (i.e. 1) get executed first, larger numbers (i.e. 99) last. There is usually an advantage to being last so your function has final say on the outcome. 3 is the number of parameters passed to our filter function. If you only need the 2nd ($permalink) parameter and the default priority is fine, you can omit the last 2 parameters. Such is the case here, but we’ll accept all 3 parameters because we can. You could for example, check a field in the $post parameter (the $post object the link points to) and only apply the <span> block if the field is a certain value, say if the post is by a certain author.

    To simply wrap all links in a <span> block, we write:

    function span_wrap_link($permalink, $post, $leavename) {
       return "<span class="permalink">$permalink</span>";
    }

    Now, PHP inserts the value of $permalink into our <span> block string (because the variable reference is inside a double quoted string), which is returned to the apply_filters() function, apply_filters() in turn returns our modified link to get_permalink(), which in turn returns the link to our echo statement which results in the link being echoed out to the user.

    A very long way of saying get rid of the apply_filters(‘get_post_meta’) bit and just call get_post_meta() directly. As far as the meta coming from the current post instead of the manually added, this is coming from the $post->ID parameter. $post in WP loops is the current post object. You need to supply the ID of your manually added post to get the correct meta.

    Look forward to the HUGE sense of accomplishment you will get once you finally get this working 🙂

    Thread Starter aronjeney

    (@aronjeney)

    I really appreciate the time that you put into writing this post and your interest in helping me and others learn. Although I was able to accomplish what I needed by “hacking” the current filter, I really value this lesson and your willingness to help others. Thanks so much again for all your help so far. I’ve seen you around the support forum quite a bit so Im sure this wont be the last time we chat. Thanks again bcworkz!

Viewing 8 replies - 1 through 8 (of 8 total)
  • The topic ‘Adding custom loop to plugin’ is closed to new replies.