如何为WP_QUERY创建灵活的抽象?

时间:2013-08-29 作者:localhost

我的问题是关于php的,但它涉及wordpress,因为我正在创建一个插件。我有5个问题,每个问题有6个选择,每个问题有一个选择。现在,这个人可以从每一个选项中选择任何一个,也可以从很少的选项中选择。我已经创造了现在让我发疯的if条件,因为它已经太长了,而且会做得更进一步,就像会做出近100个组合一样。我不想这样,我知道有一种多维数组的方法,但我不是wordpress的插件或php专家。所以如果有人能帮我整理一下。

$qs = $_POST[\'q1\'];
$q2 = $_POST[\'q2\'];
$q3 = $_POST[\'q3\'];
$q4 = $_POST[\'q4\'];
$q5 = $_POST[\'q5\'];
$q6 = $_POST[\'q6\'];



 $args = array(
  \'post_type\' => \'product\',
  \'posts_per_page\' => -1,
  \'tax_query\' => array(
    \'relation\' => \'AND\',
    array(
     \'taxonomy\' => \'product_cat\',
     \'field\' => \'slug\',
     \'terms\' => \'fashion-follower\'
    ),
//    array(
//     \'taxonomy\' => \'product_cat\',
//     \'field\' => \'slug\',
//     \'terms\' => \'cheap-and-cheerful\'
//    )
  )
);
 //The Fashionsia
 if (($qs ===\'party\') && ($q2 === \'clothes\') && ($q3 === \'shopping\') && ($q5 === \'Sunbathing\') && ($q6 === \'mini\')){

$query = new WP_Query( $args );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail(\'thumbnail\');

endwhile;
    endif;      
}

//second question loop

$args2 = array(
  \'post_type\' => \'product\',
  \'posts_per_page\' => -1,
  \'tax_query\' => array(
    \'relation\' => \'AND\',
    array(
     \'taxonomy\' => \'product_cat\',
     \'field\' => \'slug\',
     \'terms\' => \'the-homemaker\'
    ),
//    array(
//     \'taxonomy\' => \'product_cat\',
//     \'field\' => \'slug\',
//     \'terms\' => \'cheap-and-cheerful\'
//    )
  )
);
 //The homemaker
 if (($qs ===\'drink\') && ($q2 === \'candles\') && ($q3 === \'house\') && ($q4 === \'diy\')){

$query = new WP_Query( $args2 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail(\'thumbnail\');

endwhile;
    endif;      
}
//third loop

$args3 = array(
  \'post_type\' => \'product\',
  \'posts_per_page\' => -1,
  \'tax_query\' => array(
    \'relation\' => \'AND\',
    array(
     \'taxonomy\' => \'product_cat\',
     \'field\' => \'slug\',
     \'terms\' => \'entertainment\'
    ),
//    array(
//     \'taxonomy\' => \'product_cat\',
//     \'field\' => \'slug\',
//     \'terms\' => \'cheap-and-cheerful\'
//    )
  )
);
 //The Entertainer
 if (($qs ===\'party-babe\') && ($q2 === \'winer\')&& ($q4 === \'storm\') && ($q6 === \'limo\')){

$query = new WP_Query( $args3 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail(\'thumbnail\');

endwhile;
    endif;      
}
//fourth loop
$args4 = array(
  \'post_type\' => \'product\',
  \'posts_per_page\' => -1,
  \'tax_query\' => array(
    \'relation\' => \'AND\',
    array(
     \'taxonomy\' => \'product_cat\',
     \'field\' => \'slug\',
     \'terms\' => \'family-fanatic\'
    ),
//    array(
//     \'taxonomy\' => \'product_cat\',
//     \'field\' => \'slug\',
//     \'terms\' => \'cheap-and-cheerful\'
//    )
  )
);
 //The family-fanatic
 if (($qs ===\'movie\') && ($q2 === \'kids\')&& ($q6 === \'volvo\')){

$query = new WP_Query( $args4 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail(\'thumbnail\');

endwhile;
    endif;      
}
//fifth loop
//fourth loop
$args4 = array(
  \'post_type\' => \'product\',
  \'posts_per_page\' => -1,
  \'tax_query\' => array(
    \'relation\' => \'AND\',
    array(
     \'taxonomy\' => \'product_cat\',
     \'field\' => \'slug\',
     \'terms\' => \'family-fanatic\'
    ),
//    array(
//     \'taxonomy\' => \'product_cat\',
//     \'field\' => \'slug\',
//     \'terms\' => \'cheap-and-cheerful\'
//    )
  )
);
 //The romantic
 if (($qs ===\'Dinner-show\') && ($q5 === \'cruiser\')){

$query = new WP_Query( $args4 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail(\'thumbnail\');

endwhile;
    endif;      
}

1 个回复
最合适的回答,由SO网友:fuxia 整理而成

你的问题并不是关于WordPress,而是关于PHP和重构。但我们在这里看到了太多糟糕的代码,我将在下面解释的模式(MVC)可以帮助许多其他开发人员,所以我决定写一个小答案。请记住,我们的网络中有一个专门针对此类问题的网站:Code Review. 不幸的是,很少有WordPress开发人员活跃在那里。

如何重构代码删除无用代码。美化其余部分model (存储、提取、转换、解释),从输出中view (HTML、CSV等)

1。删除无用代码。美化其余部分

输出

您有这个重复片段:

if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

the_post_thumbnail(\'thumbnail\');

endwhile;
endif;
你开的车很贵the_post() 每次都可以获取帖子缩略图。但这并不需要,你可以打电话:

echo get_the_post_thumbnail( $post_id, \'post-thumbnail\' );
查询所以您只需要post ID,无需调用the_post().更好的是:您可以限制查询只获取ID。

一个简单的例子:

$post_ids = array();
$args     = array(
    \'post_type\'      => \'post\',
    \'posts_per_page\' => 10,
    \'fields\'         => \'ids\'
);
$query    = new WP_Query( $args );

if ( ! empty ( $query->posts ) )
    $post_ids = $query->posts; // just the post IDs
现在您有了ID,可以编写:

foreach ( $post_ids as $post_id )
    echo get_the_post_thumbnail( $post_id, \'post-thumbnail\' );
没有开销,您的代码已经更快、更容易阅读了。

语法请注意我是如何对齐=? 这有助于理解代码,因为人脑擅长于模式识别。支持这一点,我们就能做一些了不起的事情。制造混乱,我们很快就会陷入困境。

这也是我删除的原因endwhileendif. 这个alternative syntax 杂乱无章,难以阅读。另外,它使working in an IDE 难度更大:用大括号从表达式的开头到结尾折叠和跳跃更容易。

默认值$args 数组中的某些字段随处可见。创建一个默认数组,只写一次这些字段:

$args = array(
    \'post_type\'      => \'product\',
    \'posts_per_page\' => 100,
    \'fields\'         => \'ids\',
    \'tax_query\'      => array(
        array(
            \'taxonomy\' => \'product_cat\',
            \'field\'    => \'slug\',
        )
    )
);
再次注意对齐。还要注意我是如何改变posts_per_page 价值Never ask for -1. 当有一百万个匹配的帖子时会发生什么?您不想每次运行此查询时都终止数据库连接,是吗?谁应该读这些帖子?始终设定合理的限制。

现在你只需要改变场地$args[ \'tax_query\' ][ \'terms\' ]. 我们稍后将讨论这个问题。

2。找到所有重复的表达式并创建例程,我们已经清理了一些重复代码,现在是最困难的部分:post参数的计算。显然,您已经根据一些参数制作了一些标签。我建议将它们重命名为更容易理解的名称,但现在我们将使用您的命名方案。

将这些组与其余组分开,创建一个以后可以单独管理的阵列:

$groups = array(
    \'fashion-follower\' => array(
        \'q1\' => \'party\',
        \'q2\' => \'clothes\',
        \'q3\' => \'shopping\',
        \'q4\' => FALSE,
        \'q5\' => \'sunbathing\',
        \'q6\' => \'mini\',
    ),
    \'the-homemaker\' => array(
        \'q1\' => \'drink\',
        \'q2\' => \'candles\',
        \'q3\' => \'house\',
        \'q4\' => \'diy\',
        \'q5\' => FALSE,
        \'q6\' => FALSE,
    )
);
填补缺失terms 在默认数组中,运行$groups 数组,直到找到匹配项:

function get_query_term( $groups )
{
    foreach ( $groups as $term => $values )
    {
        if ( compare_group_values( $values ) )
            return $term;
    }

    return FALSE;
}

function compare_group_values( $values )
{
    foreach ( $values as $key => $value )
    {
        // Key not sent, but required
        if ( empty ( $_POST[ $key ] ) and ! empty ( $value ) )
            return FALSE;

        // Key sent, but wrong value
        if ( ! empty ( $_POST[ $key ] ) and $_POST[ $key ] !== $value )
            return FALSE;
    }

    // all keys matched the required values
    return TRUE;
}
我甚至将术语列表的运行和值的比较分开,因为这是不同的操作。代码的每一部分都应该只做一件事,并且必须保持缩进水平以提高可读性。

现在我们有了所有的部件,让我们把它们粘在一起。

3。组织:将模型从视图中分离出来当我编写模型和视图时,我想到了一件事:MVC方法。它代表Model View Controller,一种组织软件组件的著名模式。到目前为止,缺少的部分是控制器,我们稍后将看到如何使用它。

您说过,您对PHP了解不多,所以我希望您对输出了解更多。:)让我们从这个开始:

class Thumbnail_List
{
    protected $source;

    public function set_source( Post_Collector_Interface $source )
    {
        $this->source = $source;
    }

    public function render()
    {
        $post_ids = $this->source->get_post_ids();

        if ( empty ( $post_ids ) or ! is_array( $post_ids ) )
            return print \'Nothing found\';

        foreach ( $post_ids as $post_id )
            echo get_the_post_thumbnail( $post_id, \'post-thumbnail\' );
    }
}
简单明了:我们有两种方法:一种是设置帖子ID的源代码,另一种是渲染缩略图。

你可能想知道这是什么Post_Collector_Interface 是我们马上就要开始了。

现在是我们观点的来源,模型。

class Post_Collector implements Post_Collector_Interface
{
    protected $groups = array();

    public function set_groups( Array $groups )
    {
        $this->groups = $groups;
    }

    public function get_post_ids()
    {
        $term = $this->get_query_term();

        if ( ! $term )
            return array();

        return $this->query( $term );
    }

    protected function query( $term )
    {
        $args = array(
            \'post_type\'      => \'product\',
            \'posts_per_page\' => 100,
            \'fields\'         => \'ids\',
            \'tax_query\'      => array(
                array(
                    \'taxonomy\' => \'product_cat\',
                    \'field\'    => \'slug\',
                    \'terms\'    => $term
                )
            )
        );

        $query = new WP_Query( $args );

        if ( empty ( $query->posts ) )
            return array();

        return $query->posts;
    }

    protected function get_query_term()
    {
        foreach ( $this->groups as $term => $values )
        {
            if ( compare_group_values( $values ) )
                return $term;
        }

        return FALSE;
    }

    protected function compare_group_values( $values )
    {
        foreach ( $values as $key => $value )
        {
            // Key not sent, but required
            if ( empty ( $_POST[ $key ] ) and ! empty ( $value ) )
                return FALSE;

            // Kent sent, but wrong value
            if ( ! empty ( $_POST[ $key ] ) and $_POST[ $key ] !== $value )
                return FALSE;
        }

        // all keys matched the required values
        return TRUE;
    }
}
这已经不是那么微不足道了,但我们已经拥有了大部分的部分。这个protected 方法(函数)无法从外部访问,因为我们只需要用于内部逻辑。

这个public 方法很简单:首先得到$group 数组,第二个返回一个post id数组。我们再次遇到这个可疑的Post_Collector_Interface.

接口是合同。它可以由类签名(实现)。需要一个接口,就像我们的类Thumbnail_List does,意味着:该类需要使用这些公共方法的其他类。

让我们构建该接口。其实很简单:

interface Post_Collector_Interface
{
    public function set_groups( Array $groups );

    public function get_post_ids();
}
是的,仅此而已。代码很简单,不是吗?

我们在这里所做的:我们提出了我们的观点Thumbnail_List 独立于一个具体类,而我们仍然可以依赖于我们得到的类的方法$source.如果以后改变主意,可以编写一个新类来获取post ID或使用具有固定值的类。只要实现了该界面,视图就会得到满足。现在甚至可以使用模拟对象测试视图:

class Mock_Post_Collector implements Post_Collector_Interface
{
    public function set_groups( Array $groups ) {}

    public function get_post_ids()
    {
        return array ( 1 );
    }
}
这在您需要时非常有用test 视图。您不想同时测试这两个混凝土类,因为您看不到错误的来源。模拟对象对于错误来说过于简单,非常适合单元测试。

现在我们必须以某种方式组合我们的课程。这是控制器进入阶段的地方。

class Thumbnail_Controller
{
    protected $groups = array(
        \'fashion-follower\' => array(
            \'q1\' => \'party\',
            \'q2\' => \'clothes\',
            \'q3\' => \'shopping\',
            \'q4\' => FALSE,
            \'q5\' => \'sunbathing\',
            \'q6\' => \'mini\',
        ),
        \'the-homemaker\' => array(
            \'q1\' => \'drink\',
            \'q2\' => \'candles\',
            \'q3\' => \'house\',
            \'q4\' => \'diy\',
            \'q5\' => FALSE,
            \'q6\' => FALSE,
        )
    );
    public function __construct()
    {
        // not a post request
        if ( \'POST\' !== $_SERVER[ \'REQUEST_METHOD\' ] )
            return;

        // set up the model
        $model = new Post_Collector;
        $model->set_groups( $this->groups );

        // prepare the view
        $view = new Thumbnail_List;
        $view->set_source( $model );

        // finally render the tumbnails
        $view->render();
    }
}
控制器是应用程序的唯一部分;模型和视图可能会到处重复使用,甚至在完全不同的部分。但是控制器的存在只是为了这个目的,这就是为什么我们把$group 在这里

现在你只需要做一件事:

// Let the dogs out!
new Thumbnail_Controller;
在需要输出的任何地方调用此行。

您可以在下面的中找到此答案中的所有代码gist on GitHub.

结束