SearchResult::highlight()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
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;
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
79
        $categoryItems = BlogCategory::lists('name', 'id');
80
81
        return [
82
            'searchTerm' => [
83
                'title'       => 'Search Term',
84
                'description' => 'The value to determine what the user is searching for.',
85
                'type'        => 'string',
86
                'default'     => '{{ :search }}',
87
            ],
88
            'pageNumber' => [
89
                'title'       => 'rainlab.blog::lang.settings.posts_pagination',
90
                'description' => 'rainlab.blog::lang.settings.posts_pagination_description',
91
                'type'        => 'string',
92
                'default'     => '{{ :page }}',
93
            ],
94
            'disableUrlMapping' => [
95
                'title'       => 'Disable URL Mapping',
96
                'description' => 'If the url Mapping is disabled the search form uses the default GET Parameter q '
97
                                    . '(e.g. example.com/search?search=Foo instead of example.com/search/Foo)',
98
                'type'        => 'checkbox',
99
                'default'     => false,
100
                'showExternalParam' => false
101
            ],
102
            'hightlight' => [
103
                'title'       => 'Hightlight Matches',
104
                'type'        => 'checkbox',
105
                'default'     => false,
106
                'showExternalParam' => false
107
            ],
108
            'postsPerPage' => [
109
                'title'             => 'rainlab.blog::lang.settings.posts_per_page',
110
                'type'              => 'string',
111
                'validationPattern' => '^[0-9]+$',
112
                'validationMessage' => 'rainlab.blog::lang.settings.posts_per_page_validation',
113
                'default'           => '10',
114
            ],
115
            'noPostsMessage' => [
116
                'title'        => 'rainlab.blog::lang.settings.posts_no_posts',
117
                'description'  => 'rainlab.blog::lang.settings.posts_no_posts_description',
118
                'type'         => 'string',
119
                'default'      => 'No posts found',
120
                'showExternalParam' => false
121
            ],
122
            'sortOrder' => [
123
                'title'       => 'rainlab.blog::lang.settings.posts_order',
124
                'description' => 'rainlab.blog::lang.settings.posts_order_description',
125
                'type'        => 'dropdown',
126
                'default'     => 'published_at desc'
127
            ],
128
            'includeCategories' => [
129
                'title'       => 'Include Categories',
130
                'description' => 'Only Posts with selected categories are included in the search result',
131
                // 'type'        => $hasNewInspector ? 'set' : 'dropdown',
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
132
                'type'        => 'set',
133
                'items'       => $categoryItems,
134
                'group'       => 'Categories'
135
            ],
136
            'excludeCategories' => [
137
                'title'       => 'Exclude Categories',
138
                'description' => 'Posts with selected categories are excluded from the search result',
139
                // 'type'        => $hasNewInspector ? 'set' : 'dropdown',
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
140
                'type'        => 'set',
141
                'items'       => $categoryItems,
142
                'group'       => 'Categories'
143
            ],
144
            'categoryPage' => [
145
                'title'       => 'rainlab.blog::lang.settings.posts_category',
146
                'description' => 'rainlab.blog::lang.settings.posts_category_description',
147
                'type'        => 'dropdown',
148
                'default'     => 'blog/category',
149
                'group'       => 'Links',
150
            ],
151
            'postPage' => [
152
                'title'       => 'rainlab.blog::lang.settings.posts_post',
153
                'description' => 'rainlab.blog::lang.settings.posts_post_description',
154
                'type'        => 'dropdown',
155
                'default'     => 'blog/post',
156
                'group'       => 'Links',
157
            ],
158
        ];
159
    }
160
161
    /**
162
     * @return array
163
     */
164
    public function getIncludeCategoriesOptions()
165
    {
166
        return BlogCategory::lists('name', 'id');
167
    }
168
169
    /**
170
     * @return array
171
     */
172
    public function getExcludeCategoriesOptions()
173
    {
174
        return BlogCategory::lists('name', 'id');
175
    }
176
177
    /**
178
     * @see RainLab\Blog\Components\Posts::getCategoryPageOptions()
179
     * @return mixed
180
     */
181
    public function getCategoryPageOptions()
182
    {
183
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
184
    }
185
186
    /**
187
     * @see RainLab\Blog\Components\Posts::getPostPageOptions()
188
     * @return mixed
189
     */
190
    public function getPostPageOptions()
191
    {
192
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
193
    }
194
195
    /**
196
     * @see RainLab\Blog\Components\Posts::getSortOrderOptions()
197
     * @return mixed
198
     */
199
    public function getSortOrderOptions()
200
    {
201
        return BlogPost::$allowedSortingOptions;
202
    }
203
204
    /**
205
     * @see RainLab\Blog\Components\Posts::onRun()
206
     * @return mixed
207
     */
208
    public function onRun()
209
    {
210
        $this->prepareVars();
211
212
        // map get request to :search param
213
        $searchTerm = Input::get('search');
214
        if (!$this->property('disableUrlMapping') && \Request::isMethod('get') && $searchTerm) {
215
            // add ?cats[] query string
216
            $cats = Input::get('cat');
217
            $query = http_build_query(['cat' => $cats]);
218
            $query = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $query);
219
            $query = !empty($query) ? '?' . $query : '';
220
221
            return Redirect::to(
222
                $this->currentPageUrl([
223
                    $this->searchParam => urlencode($searchTerm)
224
                ])
225
                . $query
226
            );
227
        }
228
229
        // load posts
230
        $this->posts = $this->page[ 'posts' ] = $this->listPosts();
231
232
        /*
233
         * If the page number is not valid, redirect
234
         */
235
        if ($pageNumberParam = $this->paramName('pageNumber')) {
236
            $currentPage = $this->property('pageNumber');
237
238
            if ($currentPage > ($lastPage = $this->posts->lastPage()) && $currentPage > 1) {
239
                return Redirect::to($this->currentPageUrl([$pageNumberParam => $lastPage]));
240
            }
241
        }
242
    }
243
244
    /**
245
     * @see RainLab\Blog\Components\Posts::prepareVars()
246
     */
247
    protected function prepareVars()
248
    {
249
        $this->pageParam = $this->page[ 'pageParam' ] = $this->paramName('pageNumber');
250
        $this->searchParam = $this->page[ 'searchParam' ] = $this->paramName('searchTerm');
251
        $this->searchTerm = $this->page[ 'searchTerm' ] = urldecode($this->property('searchTerm'));
252
        $this->noPostsMessage = $this->page[ 'noPostsMessage' ] = $this->property('noPostsMessage');
253
254
        if ($this->property('disableUrlMapping')) {
255
            $this->searchTerm = $this->page[ 'searchTerm' ] = urldecode(Input::get('search'));
256
        }
257
258
        /*
259
         * Page links
260
         */
261
        $this->postPage = $this->page[ 'postPage' ] = $this->property('postPage');
262
        $this->categoryPage = $this->page[ 'categoryPage' ] = $this->property('categoryPage');
263
    }
264
265
    /**
266
     * @see RainLab\Blog\Components\Posts::prepareVars()
267
     * @return mixed
268
     */
269
    protected function listPosts()
270
    {
271
        // Filter posts
272
        $posts = BlogPost::where(function ($q) {
273
            $q->where('title', 'LIKE', "%{$this->searchTerm}%")
274
                ->orWhere('content', 'LIKE', "%{$this->searchTerm}%")
275
                ->orWhere('excerpt', 'LIKE', "%{$this->searchTerm}%");
276
        });
277
        
278
        if (!is_null($this->property('includeCategories'))) {
279
            $posts = $posts->whereHas('categories', function ($q) {
280
                $q->whereIn('id', $this->property('includeCategories'));
281
            });
282
        }
283
284
        if (!is_null($this->property('excludeCategories'))) {
285
            $posts = $posts->whereDoesntHave('categories', function ($q) {
286
                $q->whereIn('id', $this->property('excludeCategories'));
287
            });
288
        }
289
290
        // filter categories
291
        $cat = Input::get('cat');
292
        if ($cat) {
293
            $cat = is_array($cat) ? $cat : [$cat];
294
            $posts->filterCategories($cat);
295
        }
296
297
        // List all the posts that match search terms, eager load their categories
298
        $posts = $posts->listFrontEnd([
299
            'page'    => $this->property('pageNumber'),
300
            'sort'    => $this->property('sortOrder'),
301
            'perPage' => $this->property('postsPerPage'),
302
        ]);
303
304
        /*
305
         * Add a "url" helper attribute for linking to each post and category
306
         */
307
        $posts->each(function ($post) {
308
            $post->setUrl($this->postPage, $this->controller);
309
310
            $post->categories->each(function ($category) {
311
                $category->setUrl($this->categoryPage, $this->controller);
312
            });
313
314
            // apply highlight of search result
315
            $this->highlight($post);
316
        });
317
318
        return $posts;
319
    }
320
321
    /**
322
     * @param \RainLab\Blog\Models\Post $post
323
     */
324
    protected function highlight(BlogPost $post)
325
    {
326
        if ($this->property('hightlight')) {
327
            $searchTerm = preg_quote($this->searchTerm, '|');
328
329
            // apply highlight
330
            $post->title = preg_replace('|(' . $searchTerm . ')|iu', '<mark>$1</mark>', $post->title);
331
            $post->excerpt = preg_replace('|(' . $searchTerm . ')|iu', '<mark>$1</mark>', $post->excerpt);
332
333
            $post->content_html = preg_replace(
334
                '~(?![^<>]*>)(' . $searchTerm . ')~ismu',
335
                '<mark>$1</mark>',
336
                $post->content_html
337
            );
338
        }
339
    }
340
}
341