Completed
Push — master ( 350590...39166a )
by Pascal
02:14
created

SearchResult::getIncludeCategoriesOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php namespace PKleindienst\BlogSearch\Components;
2
3
use Cms\Classes\ComponentBase;
4
use Cms\Classes\Page;
5
use Input;
6
use RainLab\Blog\Models\Category as BlogCategory;
7
use RainLab\Blog\Models\Post as BlogPost;
8
use Redirect;
9
use System\Models\Parameters;
10
11
/**
12
 * Search Result component
13
 * @see RainLab\Blog\Components\Posts
14
 * @package PKleindienst\BlogSearch\Components
15
 */
16
class SearchResult extends ComponentBase
17
{
18
    /**
19
     * Parameter to use for the search
20
     * @var string
21
     */
22
    public $searchParam;
23
24
    /**
25
     * The search term
26
     * @var string
27
     */
28
    public $searchTerm;
29
30
    /**
31
     * A collection of posts to display
32
     * @var Collection
33
     */
34
    public $posts;
35
36
    /**
37
     * Parameter to use for the page number
38
     * @var string
39
     */
40
    public $pageParam;
41
42
    /**
43
     * Message to display when there are no messages.
44
     * @var string
45
     */
46
    public $noPostsMessage;
47
48
    /**
49
     * Reference to the page name for linking to posts.
50
     * @var string
51
     */
52
    public $postPage;
53
54
    /**
55
     * Reference to the page name for linking to categories.
56
     * @var string
57
     */
58
    public $categoryPage;
59
60
    /**
61
     * @return array
62
     */
63
    public function componentDetails()
64
    {
65
        return [
66
            'name'        => 'Search Result',
67
            'description' => 'Displays a list of blog posts that match the search term on the page.'
68
        ];
69
    }
70
71
    /**
72
     * @see RainLab\Blog\Components\Posts::defineProperties()
73
     * @return array
74
     */
75
    public function defineProperties()
76
    {
77
        // check build to add fallback to not supported inspector types if needed
78
        $hasNewInspector = Parameters::get('system::core.build') >= 306;
79
80
        return [
81
            'searchTerm' => [
82
                'title'       => 'Search Term',
83
                'description' => 'The value to determine what the user is searching for.',
84
                'type'        => 'string',
85
                'default'     => '{{ :search }}',
86
            ],
87
            'pageNumber' => [
88
                'title'       => 'rainlab.blog::lang.settings.posts_pagination',
89
                'description' => 'rainlab.blog::lang.settings.posts_pagination_description',
90
                'type'        => 'string',
91
                'default'     => '{{ :page }}',
92
            ],
93
            'disableUrlMapping' => [
94
                'title'       => 'Disable URL Mapping',
95
                'description' => 'If the url Mapping is disabled the search form uses the default GET Parameter q '
96
                                    . '(e.g. example.com/search?search=Foo instead of example.com/search/Foo)',
97
                'type'        => 'checkbox',
98
                'default'     => false,
99
                'showExternalParam' => false
100
            ],
101
            'hightlight' => [
102
                'title'       => 'Hightlight Matches',
103
                'type'        => 'checkbox',
104
                'default'     => false,
105
                'showExternalParam' => false
106
            ],
107
            'postsPerPage' => [
108
                'title'             => 'rainlab.blog::lang.settings.posts_per_page',
109
                'type'              => 'string',
110
                'validationPattern' => '^[0-9]+$',
111
                'validationMessage' => 'rainlab.blog::lang.settings.posts_per_page_validation',
112
                'default'           => '10',
113
            ],
114
            'noPostsMessage' => [
115
                'title'        => 'rainlab.blog::lang.settings.posts_no_posts',
116
                'description'  => 'rainlab.blog::lang.settings.posts_no_posts_description',
117
                'type'         => 'string',
118
                'default'      => 'No posts found',
119
                'showExternalParam' => false
120
            ],
121
            'sortOrder' => [
122
                'title'       => 'rainlab.blog::lang.settings.posts_order',
123
                'description' => 'rainlab.blog::lang.settings.posts_order_description',
124
                'type'        => 'dropdown',
125
                'default'     => 'published_at desc'
126
            ],
127
            'includeCategories' => [
128
                'title'       => 'Include Categories',
129
                'description' => 'Only Posts with selected categories are included in the search result',
130
                'type'        => $hasNewInspector ? 'set' : 'dropdown',
131
                'group'       => 'Categories'
132
            ],
133
            'excludeCategories' => [
134
                'title'       => 'Exclude Categories',
135
                'description' => 'Posts with selected categories are excluded from the search result',
136
                'type'        => $hasNewInspector ? 'set' : 'dropdown',
137
                'group'       => 'Categories'
138
            ],
139
            'categoryPage' => [
140
                'title'       => 'rainlab.blog::lang.settings.posts_category',
141
                'description' => 'rainlab.blog::lang.settings.posts_category_description',
142
                'type'        => 'dropdown',
143
                'default'     => 'blog/category',
144
                'group'       => 'Links',
145
            ],
146
            'postPage' => [
147
                'title'       => 'rainlab.blog::lang.settings.posts_post',
148
                'description' => 'rainlab.blog::lang.settings.posts_post_description',
149
                'type'        => 'dropdown',
150
                'default'     => 'blog/post',
151
                'group'       => 'Links',
152
            ],
153
        ];
154
    }
155
156
    /**
157
     * @return array
158
     */
159
    public function getIncludeCategoriesOptions()
160
    {
161
        return BlogCategory::lists('name', 'id');
162
    }
163
164
    /**
165
     * @return array
166
     */
167
    public function getExcludeCategoriesOptions()
168
    {
169
        return BlogCategory::lists('name', 'id');
170
    }
171
172
    /**
173
     * @see RainLab\Blog\Components\Posts::getCategoryPageOptions()
174
     * @return mixed
175
     */
176
    public function getCategoryPageOptions()
177
    {
178
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
179
    }
180
181
    /**
182
     * @see RainLab\Blog\Components\Posts::getPostPageOptions()
183
     * @return mixed
184
     */
185
    public function getPostPageOptions()
186
    {
187
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
188
    }
189
190
    /**
191
     * @see RainLab\Blog\Components\Posts::getSortOrderOptions()
192
     * @return mixed
193
     */
194
    public function getSortOrderOptions()
195
    {
196
        return BlogPost::$allowedSortingOptions;
197
    }
198
199
    /**
200
     * @see RainLab\Blog\Components\Posts::onRun()
201
     * @return mixed
202
     */
203
    public function onRun()
204
    {
205
        $this->prepareVars();
206
207
        // map get request to :search param
208
        $searchTerm = Input::get('search');
209
        if (!$this->property('disableUrlMapping') && \Request::isMethod('get') && $searchTerm) {
210
            // add ?cats[] query string
211
            $cats = Input::get('cat');
212
            $query = http_build_query(['cat' => $cats]);
213
            $query = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $query);
214
            $query = !empty($query) ? '?' . $query : '';
215
216
            return Redirect::to(
217
                $this->currentPageUrl([
218
                    $this->searchParam => urlencode($searchTerm)
219
                ])
220
                . $query
221
            );
222
        }
223
224
        // load posts
225
        $this->posts = $this->page[ 'posts' ] = $this->listPosts();
226
227
        /*
228
         * If the page number is not valid, redirect
229
         */
230
        if ($pageNumberParam = $this->paramName('pageNumber')) {
231
            $currentPage = $this->property('pageNumber');
232
233
            if ($currentPage > ($lastPage = $this->posts->lastPage()) && $currentPage > 1) {
234
                return Redirect::to($this->currentPageUrl([$pageNumberParam => $lastPage]));
235
            }
236
        }
237
    }
238
239
    /**
240
     * @see RainLab\Blog\Components\Posts::prepareVars()
241
     */
242
    protected function prepareVars()
243
    {
244
        $this->pageParam = $this->page[ 'pageParam' ] = $this->paramName('pageNumber');
245
        $this->searchParam = $this->page[ 'searchParam' ] = $this->paramName('searchTerm');
246
        $this->searchTerm = $this->page[ 'searchTerm' ] = urldecode($this->property('searchTerm'));
247
        $this->noPostsMessage = $this->page[ 'noPostsMessage' ] = $this->property('noPostsMessage');
248
249
        if ($this->property('disableUrlMapping')) {
250
            $this->searchTerm = $this->page[ 'searchTerm' ] = urldecode(Input::get('search'));
251
        }
252
253
        /*
254
         * Page links
255
         */
256
        $this->postPage = $this->page[ 'postPage' ] = $this->property('postPage');
257
        $this->categoryPage = $this->page[ 'categoryPage' ] = $this->property('categoryPage');
258
    }
259
260
    /**
261
     * @see RainLab\Blog\Components\Posts::prepareVars()
262
     * @return mixed
263
     */
264
    protected function listPosts()
265
    {
266
        // get posts in excluded category
267
        $blockedPosts = [];
268
        $categories = BlogCategory::with(['posts' => function ($q) {
269
            $q->select('post_id');
270
        }])
271
            ->whereIn('id', $this->property('excludeCategories'))
272
            ->get();
273
274
        $categories->each(function ($item) use (&$blockedPosts) {
275
            $item->posts->each(function ($item) use (&$blockedPosts) {
276
                $blockedPosts[] = $item->post_id;
277
            });
278
        });
279
280
        // get only posts from included categories
281
        $allowedPosts = [];
282
        $categories = BlogCategory::with(['posts' => function ($q) {
283
            $q->select('post_id');
284
        }])
285
            ->whereIn('id', $this->property('includeCategories'))
286
            ->get();
287
288
        $categories->each(function ($item) use (&$allowedPosts) {
289
            $item->posts->each(function ($item) use (&$allowedPosts) {
290
                $allowedPosts[] = $item->post_id;
291
            });
292
        });
293
294
        // Filter posts
295
        $posts = BlogPost::with(['categories' => function ($q) {
296
            $q->whereNotIn('id', $this->property('excludeCategories'));
297
            $q->whereIn('id', $this->property('includeCategories'));
298
        }])
299
            ->whereNotIn('id', $blockedPosts)
300
            ->whereIn('id', $allowedPosts)
301
            ->where(function ($q) {
302
                $q->where('title', 'LIKE', "%{$this->searchTerm}%")
303
                    ->orWhere('content', 'LIKE', "%{$this->searchTerm}%")
304
                    ->orWhere('excerpt', 'LIKE', "%{$this->searchTerm}%");
305
            });
306
307
        // filter categories
308
        $cat = Input::get('cat');
309
        if ($cat) {
310
            $cat = is_array($cat) ? $cat : [$cat];
311
            $posts->filterCategories($cat);
312
        }
313
314
        // List all the posts that match search terms, eager load their categories
315
        $posts = $posts->listFrontEnd([
316
            'page'       => $this->property('pageNumber'),
317
            'sort'       => $this->property('sortOrder'),
318
            'perPage'    => $this->property('postsPerPage'),
319
        ]);
320
321
        /*
322
         * Add a "url" helper attribute for linking to each post and category
323
         */
324
        $posts->each(function ($post) {
325
            $post->setUrl($this->postPage, $this->controller);
326
327
            $post->categories->each(function ($category) {
328
                $category->setUrl($this->categoryPage, $this->controller);
329
            });
330
331
            // apply highlight of search result
332
            if ($this->property('hightlight')) {
333
                $searchTerm = preg_quote($this->searchTerm, '|');
334
335
                // apply highlight
336
                $post->title = preg_replace('|(' . $searchTerm . ')|i', '<mark>$1</mark>', $post->title);
337
                $post->excerpt = preg_replace('|(' . $searchTerm . ')|i', '<mark>$1</mark>', $post->excerpt);
338
339
                $post->content_html = preg_replace(
340
                    '~(?![^<>]*>)(' . $searchTerm . ')~ism',
341
                    '<mark>$1</mark>',
342
                    $post->content_html
343
                );
344
            }
345
        });
346
347
        return $posts;
348
    }
349
}
350