Making Milos’s comment into an answer….
The problem you are facing is the result of many wordpress template oriented functions expect some global variables. In WP_Query
based loops setting those variable is done by calling the the_post()
method of the WP_Query
object, but with get_posts
you need to call setup_postdata()
for that.
My personal preference is to just try to avoid functions like the_title
in favor of functions like get_the_title
, that accept an explicit post id, whenever possible.