GinoPane/oc-blogtaxonomy-plugin

View on GitHub
components/RelatedPosts.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace GinoPane\BlogTaxonomy\Components;

use DB;
use Cms\Classes\Page;
use RainLab\Blog\Models\Post;
use GinoPane\BlogTaxonomy\Plugin;
use GinoPane\BlogTaxonomy\Models\Tag;
use October\Rain\Database\Collection;
use GinoPane\BlogTaxonomy\Models\ModelAbstract;
use GinoPane\BlogTaxonomy\Classes\PostListAbstract;
use GinoPane\BlogTaxonomy\Classes\ComponentAbstract;
use GinoPane\BlogTaxonomy\Classes\TranslateArrayTrait;
use GinoPane\BlogTaxonomy\Classes\PostListFiltersTrait;

/**
 * Class RelatedPosts
 *
 * @package GinoPane\BlogTaxonomy\Components
 */
class RelatedPosts extends ComponentAbstract
{
    const NAME = 'relatedPosts';

    use TranslateArrayTrait;
    use PostListFiltersTrait;

    /**
     * @var Collection | array
     */
    public $posts = [];

    /**
     * Reference to the page name for linking to posts
     *
     * @var string
     */
    public $postPage;

    /**
     * If the post list should be ordered by another attribute
     *
     * @var string
     */
    public $orderBy;

    /**
     * Limits the number of records to display
     *
     * @var int
     */
    public $limit;

    /**
     * Component Registration
     *
     * @return  array
     */
    public function componentDetails(): array
    {
        return [
            'name'        => Plugin::LOCALIZATION_KEY . 'components.related_posts.name',
            'description' => Plugin::LOCALIZATION_KEY . 'components.related_posts.description'
        ];
    }

    /**
     * Component Properties
     *
     * @return  array
     */
    public function defineProperties(): array
    {
        return array_merge([
            'slug' => [
                'title'             => Plugin::LOCALIZATION_KEY . 'components.related_posts.post_slug_title',
                'description'       => Plugin::LOCALIZATION_KEY . 'components.related_posts.post_slug_description',
                'default'           => '{{ :slug }}',
                'type'              => 'string'
            ],

            'limit' => [
                'title'             => Plugin::LOCALIZATION_KEY . 'components.related_posts.limit_title',
                'description'       => Plugin::LOCALIZATION_KEY . 'components.related_posts.limit_description',
                'type'              => 'string',
                'default'           => '0',
                'validationPattern' => '^[0-9]+$',
                'validationMessage' => Plugin::LOCALIZATION_KEY . 'components.related_posts.limit_validation_message',
                'showExternalParam' => false
            ],

            'orderBy' => [
                'title'       => 'rainlab.blog::lang.settings.posts_order',
                'description' => 'rainlab.blog::lang.settings.posts_order_description',
                'type'        => 'dropdown',
                'default'     => 'published_at asc',
                'showExternalParam' => false
            ],

            'postPage' => [
                'group'       => Plugin::LOCALIZATION_KEY . 'components.related_posts.links_group',
                'title'       => 'rainlab.blog::lang.settings.posts_post',
                'description' => 'rainlab.blog::lang.settings.posts_description',
                'type'        => 'dropdown',
                'default'     => 'blog/post',
            ],
        ], $this->getPostFilterProperties());
    }

    /**
     * @see PostListAbstract::$postAllowedSortingOptions
     *
     * @return mixed
     */
    public function getOrderByOptions()
    {
        $order = $this->translate(
            array_merge(
                [
                    'relevance asc' => Plugin::LOCALIZATION_KEY . 'order_options.relevance_asc',
                    'relevance desc' => Plugin::LOCALIZATION_KEY . 'order_options.relevance_desc'
                ],
                PostListAbstract::$postAllowedSortingOptions
            )
        );

        asort($order);

        return $order;
    }

    private function prepareVars()
    {
        $this->orderBy = $this->page['orderBy'] = $this->property('orderBy');

        // Page links
        $this->postPage = $this->page['postPage' ] = $this->property('postPage');

        // Exceptions
        $this->populateFilters();
    }

    public function getPostPageOptions()
    {
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
    }

    /**
     * Load post and start building query for related posts
     */
    public function onRun()
    {
        //Prepare vars
        $this->prepareVars();

        $this->posts = $this->loadRelatedPosts();
    }

    /**
     * Load related posts by common tags
     *
     * @return mixed
     */
    private function loadRelatedPosts()
    {
        $post = Post::with('tags');

        ModelAbstract::whereTranslatableProperty($post, 'slug', $this->property('slug'));

        $post = $post->first();

        if (!$post || (!$tagIds = $post->tags->lists('id'))) {
            return null;
        }

        $query = Post::isPublished()
            ->where('id', '<>', $post->id)
            ->whereHas('tags', function ($tag) use ($tagIds) {
                $tag->whereIn('id', $tagIds);
            })
            ->with('tags');

        $this->handlePostFilters($query);
        $this->queryOrderBy($query, $tagIds);

        if ($take = (int)$this->property('limit')) {
            $query->take($take);
        }

        $posts = $query->get();

        $this->setPostUrls($posts);

        return $posts;
    }

    /**
     * @param $query
     * @param $tagIds
     */
    private function queryOrderBy($query, $tagIds)
    {
        if (array_key_exists($this->orderBy, $this->getOrderByOptions())) {
            if ($this->orderBy === 'random') {
                $query->inRandomOrder();
            } else {
                list($sortField, $sortDirection) = explode(' ', $this->orderBy);

                if ($sortField === 'relevance') {
                    $sortField = DB::raw(
                        sprintf(
                            '(
                                select count(*)
                                from `%1$s`
                                where
                                `%1$s`.`%2$s_type` = \'RainLab\Blog\Models\Post\'
                                and `%1$s`.`%2$s_id` = `rainlab_blog_posts`.`id`
                                and `%1$s`.`tag_id` in (\'%3$s\')
                            )',
                            Tag::PIVOT_TABLE,
                            Tag::PIVOT_COLUMN,
                            implode('\', \'', $tagIds)
                        )
                    );
                }

                $query->orderBy($sortField, $sortDirection);
            }
        }
    }
}