3
\$\begingroup\$

Plugin is a small editor that allows to create a filter such as a price slider, checkboxes of "brands", a drop-down of "seasons".

An example of how plugin work can be seen on page: http://demo-product-filters.anexter.net/

Such plugins are used in many stores, and they need to be easily scalable.

Element classes is main classes of plugin but they has too many responsibilities, such as

  • generate_controls method - creating controls for editor in admin panel (pic. https://i.sstatic.net/JDH3z.png)
  • render_template method - show element for front end with which user will interact
  • apply_filter_to_query method - applying selected options to product request. For example, if customer chose category "cat1" in element, then it is necessary to change query to database so that there are only products of this category

As well as methods that return a list of options that will be displayed in front end, such as a list of categories, brands or other

Base element class that has two abstractions (creating controls for editor in admin panel and rendering element in front end)


abstract class Abstract_Element extends Component {
    protected $project;

    protected $wp_post;

    public function get_project() {
        return $this->project;
    }

    public function set_project( Project $project ) {
        $this->project = $project;
    }

    public function get_wp_post() {
        return $this->wp_post;
    }

    public function set_wp_post( \WP_Post $post ) {
        $this->wp_post = $post;
    }

    public function get_id() {
        return $this->wp_post->ID;
    }

    public function get_title() {
        return $this->wp_post->post_title;
    }

    public function get_options() {
        return get_post_meta( $this->get_id(), 'wcpf_options', [] );
    }

    public function set_options( array $options ) {
        update_post_meta( $this->get_id(), 'wcpf_options', $options );
    }

    public function get_option( $index, $default_value = null ) {
        $options = $this->get_options();

        return array_key_exists( $index, $options ) ? $options[ $index ] : $default_value;
    }

    public function set_option( $index, $value ) {
        $this->set_options( array_merge( $this->get_options(), [ $index => $value ] ) );
    }

    public function has_option( $index ) {
        $options = $this->get_options();

        return array_key_exists( $index, $options );
    }

    public function remove_option( $index ) {
        $options = $this->get_options();

        if ( array_key_exists( $index, $options ) ) {
            unset( $options[ $index ] );

            $this->set_options( $options );
        }
    }

    public function get_default_options() {
        return [];
    }

    public abstract function get_element_slug();

    public abstract function get_element_title();

    public abstract function generate_controls();

    public abstract function render_template();
}

Interface for elements that allows to add ability filtering for element

interface Filtering_Element_Interface {
    function get_filter_value( $default_value = null );

    function set_filter_value( $filter_value );

    function get_url_key();

    function apply_filter_to_query( \WP_Query $product_query, $filter_value );
}

abstract class Abstract_Filtering_Element extends Abstract_Element implements Filtering_Element_Interface {
    protected $filter_value = null;

    public function get_filter_value( $default_value = null ) {
        return is_null( $this->filter_value ) ? $default_value : $this->filter_value;
    }

    public function set_filter_value( $filter_value ) {
        $this->filter_value = $filter_value;
    }
}

Class dropdown element

class Drop_Down_Element extends Abstract_Filtering_Element {
    public function get_element_slug() {
        return 'drop-down';
    }

    public function get_element_title() {
        return __( 'Drop Down' );
    }

    public function generate_controls() {
        $editor_panel = new Tabs_Layout( [
            'title' => $this->get_element_title(),
            'tabs' => [
                'general' => [
                    'label' => __( 'General', 'wcpf' ),
                    'controls' => [
                        new Control\Text_Control( [
                            'key' => 'postTitle',
                            'control_source' => 'post',
                            'label' => __( 'Title', 'wcpf' ),
                            'placeholder' => __( 'Title', 'wcpf' ),
                            'required' => true
                        ] ),
                        new Control\Text_Control( [
                            'key' => 'optionKey',
                            'label' => __( 'URL key', 'wcpf' ),
                            'placeholder' => __( 'option-key', 'wcpf' ),
                            'control_description' => __( 'The “URL key” is the URL-friendly version of the title. It is usually all lowercase and contains only letters, numbers, and hyphens', 'wcpf' ),
                            'required' => true
                        ] ),
                        new Control\Select_Control( [
                            'key' => 'itemsSource',
                            'label' => __( 'Source of options', 'wcpf' ),
                            'control_description' => __( 'Select source of options, that will be using to filter products', 'wcpf' ),
                            'options' => [
                                'attribute' => __( 'Attribute', 'wcpf' ),
                                'category' => __( 'Category', 'wcpf' ),
                                'tag' => __( 'Tag', 'wcpf' ),
                                'taxonomy' => __( 'Taxonomy', 'wcpf' )
                            ],
                            'default_value' => 'attribute'
                        ] ),
                        new Control\Select_Control( [
                            'key' => 'itemsSourceAttribute',
                            'label' => __( 'Attribute', 'wcpf' ),
                            'options' => wcpf_get_attribute_taxonomies(),
                            'control_description' => __( 'Choose one of the attributes created in “Products > Attributes”', 'wcpf' ),
                            'display_rules' => [
                                [
                                    'optionKey' => 'itemsSource',
                                    'operation' => '==',
                                    'value' => [
                                        'attribute'
                                    ]
                                ]
                            ],
                            'required' => true
                        ] ),
                        new Control\Select_Control( [
                            'key' => 'itemsSourceCategory',
                            'label' => __( 'Category', 'wcpf' ),
                            'options' => wcpf_get_categories(),
                            'control_description' => __( 'Choose one of the categories created in “Products > Categories”', 'wcpf' ),
                            'default_value' => 'all',
                            'display_rules' => [
                                [
                                    'optionKey' => 'itemsSource',
                                    'operation' => '==',
                                    'value' => [
                                        'category'
                                    ]
                                ]
                            ],
                            'required' => true
                        ] ),
                        new Control\Select_Control( [
                            'key' => 'itemsSourceTaxonomy',
                            'label' => __( 'Taxonomy', 'wcpf' ),
                            'options' => wcpf_get_taxonomies(),
                            'control_description' => __( 'The “Taxonomy” is a grouping mechanism for posts. For example, "product tags" and "product attributes" are also taxonomies', 'wcpf' ),
                            'display_rules' => [
                                [
                                    'optionKey' => 'itemsSource',
                                    'operation' => '==',
                                    'value' => [
                                        'taxonomy'
                                    ]
                                ]
                            ],
                            'required' => true
                        ] ),
                    ],
                ],
                'visual' => [
                    'label' => __( 'Visual', 'wcpf' ),
                    'controls' => [
                        new Control\Text_Control( [
                            'key' => 'cssClass',
                            'label' => __( 'CSS Class', 'wcpf' ),
                            'placeholder' => __( 'class-name', 'wcpf' )
                        ] )
                    ]
                ]
            ]
        ]);

        return $editor_panel;
    }

    public function get_url_key() {
        return $this->get_option( 'url-key' );
    }

    public function apply_filter_to_query( \WP_Query $product_query, $filter_value ) {
        $items_source = $this->get_option( 'itemsSource' );

        $taxonomy = false;

        if ( $items_source == 'attribute' ) {
            $taxonomy = wc_attribute_taxonomy_name( $this->get_option( 'itemsSourceAttribute' ) );
        } else if ( $items_source == 'tag' ) {
            $taxonomy = 'product_tag';
        } else if ( $items_source == 'category' ) {
            $taxonomy = 'product_cat';
        } else if ( $items_source == 'taxonomy' ) {
            $taxonomy = $this->get_option( 'itemsSourceTaxonomy' );
        }

        $tax_query_item = [
            'taxonomy' => $taxonomy,
            'field' => 'slug',
            'terms' => $filter_value,
            'operator' => 'OR'
        ];

        $product_query->set(
            'tax_query',
            array_merge(
                $product_query->get( 'tax_query', [] ),
                [ $this->get_url_key() => $tax_query_item ]
            )
        );
    }

    public function render_template() {
        $this->get_teplate_loader()->render_template( 'elements/drop-down.php', [
            'element' => $this,
            'items' => $this->get_items()
        ] );
    }

    protected function get_items() {
        $items = [];

        $items_source = $this->get_option( 'itemsSource' );

        $taxonomy = false;

        if ( $items_source == 'attribute' ) {
            $taxonomy = wc_attribute_taxonomy_name( $this->get_option( 'itemsSourceAttribute' ) );
        } else if ( $items_source == 'tag' ) {
            $taxonomy = 'product_tag';
        } else if ( $items_source == 'category' ) {
            $taxonomy = 'product_cat';
        } else if ( $items_source == 'taxonomy' ) {
            $taxonomy = $this->get_option( 'itemsSourceTaxonomy' );
        }

        if ( ! taxonomy_exists( $taxonomy ) ) {
            return [];
        }

        $terms = get_terms( [
            'taxonomy' => $taxonomy,
            'hierarchical' => true,
            'menu_order' => 'asc',
            'order' => 'asc'
        ] );

        foreach ( $terms as $term ) {
            $key = urldecode( $term->slug );

            $items[] = [
                'key' => $key,
                'title' => $term->name,
            ];
        }

        return $items;
    }
}

Update

I have an idea to realize something similar to a facade.

What do you think?

Example:

class Drop_Down_Element {
    protected $elementFiltering;

    protected $elementDisplay;

    protected $elementControls;

    public function get_element_slug() {
        return 'drop-down';
    }

    public function get_element_title() {
        return __( 'Drop Down' );
    }

    public function __construct() {
        $this->elementControls = new Drop_Down_Controls();

        $this->elementFiltering = new Drop_Down_Filtering();

        $this->elementDisplay = new Drop_Down_Display();
    }

    public function generate_controls() {
        $this->elementControls->generate_controls();
    }

    public function apply_filter_to_query( \WP_Query $product_query, $filter_value ) {
        $this->elementFiltering->apply_filter_to_query( $product_query, $filter_value );
    }

    public function render_template() {
        $this->elementDisplay->render();
    }
}
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.