Completed
Pull Request — master (#110)
by Jason
03:22
created

Locator_Controller::LocationSearch()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 0 Features 0
Metric Value
c 9
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 2
eloc 10
nc 2
nop 0
1
<?php
2
3
/**
4
 * Class Locator
5
 *
6
 * @property bool $AutoGeocode
7
 * @property bool $ModalWindow
8
 * @property string $Unit
9
 * @method Categories|ManyManyList $Categories
10
 */
11
class Locator extends Page
12
{
13
14
    /**
15
     * @var array
16
     */
17
    private static $db = array(
18
        'AutoGeocode' => 'Boolean',
19
        'ModalWindow' => 'Boolean',
20
        'Unit' => 'Enum("m,km","m")',
21
    );
22
23
    /**
24
     * @var array
25
     */
26
    private static $many_many = array(
27
        'Categories' => 'LocationCategory',
28
    );
29
30
    /**
31
     * @var array
32
     */
33
    private static $defaults = array(
34
        'AutoGeocode' => true,
35
    );
36
37
    /**
38
     * @var string
39
     */
40
    private static $singular_name = 'Locator';
41
    /**
42
     * @var string
43
     */
44
    private static $plural_name = 'Locators';
45
    /**
46
     * @var string
47
     */
48
    private static $description = 'Find locations on a map';
1 ignored issue
show
Unused Code introduced by
The property $description is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
49
50
    /**
51
     * @return FieldList
52
     */
53
    public function getCMSFields()
54
    {
55
        $fields = parent::getCMSFields();
56
57
        // Settings
58
        $fields->addFieldsToTab('Root.Settings', array(
59
            HeaderField::create('DisplayOptions', 'Display Options', 3),
60
            OptionsetField::create('Unit', 'Unit of measure', array('m' => 'Miles', 'km' => 'Kilometers')),
61
            CheckboxField::create('AutoGeocode', 'Auto Geocode - Automatically filter map results based on user location')
62
                ->setDescription('Note: if any locations are set as featured, the auto geocode is automatically disabled.'),
63
            CheckboxField::create('ModalWindow', 'Modal Window - Show Map results in a modal window'),
64
        ));
65
66
        // Filter categories
67
        $config = GridFieldConfig_RelationEditor::create();
68
        if (class_exists('GridFieldAddExistingSearchButton')) {
69
            $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
70
            $config->addComponent(new GridFieldAddExistingSearchButton());
71
        }
72
        $categories = $this->Categories();
73
        $categoriesField = GridField::create('Categories', 'Categories', $categories, $config)
74
            ->setDescription('only show locations from the selected category');
75
76
        // Filter
77
        $fields->addFieldsToTab('Root.Filter', array(
78
            HeaderField::create('CategoryOptionsHeader', 'Location Filtering', 3),
79
            $categoriesField,
80
        ));
81
82
        $this->extend('updateCMSFields', $fields);
83
84
        return $fields;
85
    }
86
87
    /**
88
     * @param array $filter
89
     * @param array $filterAny
90
     * @param array $exclude
91
     * @param null|callable $callback
92
     * @return DataList|ArrayList
93
     */
94
    public static function get_locations(
95
        $filter = [],
96
        $filterAny = [],
97
        $exclude = [],
98
        $callback = null
99
    )
100
    {
101
        $locations = Location::get()->filter($filter)->exclude($exclude);
102
103
        if (!empty($filterAny)) {
104
            $locations = $locations->filterAny($filterAny);
105
        }
106
        if (!empty($exclude)) {
107
            $locations = $locations->exclude($exclude);
108
        }
109
110
        if ($callback !== null && is_callable($callback)) {
111
            $locations->filterByCallback($callback);
112
        }
113
114
        return $locations;
115
    }
116
117
    /**
118
     * @return DataList
119
     */
120
    public static function get_all_categories()
121
    {
122
        return LocationCategory::get();
123
    }
124
125
    /**
126
     * @return bool
127
     */
128
    public function getPageCategories()
129
    {
130
        return self::locator_categories_by_locator($this->ID);
131
    }
132
133
    /**
134
     * @param int $id
135
     * @return bool|
136
     */
137
    public static function locator_categories_by_locator($id = 0)
138
    {
139
        if ($id == 0) {
140
            return false;
141
        }
142
143
        return Locator::get()->byID($id)->Categories();
144
    }
145
146
147
}
148
149
/**
150
 * Class Locator_Controller
151
 */
152
class Locator_Controller extends Page_Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
153
{
154
    /**
155
     * @var array
156
     */
157
    private static $allowed_actions = array(
158
        'xml',
159
    );
160
161
    /**
162
     * @var array
163
     */
164
    private static $base_filter = [
0 ignored issues
show
Unused Code introduced by
The property $base_filter is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
165
        'ShowInLocator' => true,
166
    ];
167
168
    /**
169
     * @var array
170
     */
171
    private static $base_exclude = [
0 ignored issues
show
Unused Code introduced by
The property $base_exclude is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
172
        'Lat' => 0,
173
        'Lng' => 0,
174
    ];
175
176
    /**
177
     * @var array
178
     */
179
    private static $base_filter_any = [];
0 ignored issues
show
Unused Code introduced by
The property $base_filter_any is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
180
181
    /**
182
     * @var string
183
     */
184
    private static $list_template_path = 'locator/templates/location-list-description.html';
0 ignored issues
show
Unused Code introduced by
The property $list_template_path is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
185
186
    /**
187
     * @var string
188
     */
189
    private static $info_window_template_path = 'locator/templates/infowindow-description.html';
0 ignored issues
show
Unused Code introduced by
The property $info_window_template_path is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
190
191
    /**
192
     * @var DataList|ArrayList
193
     */
194
    private $locations;
195
196
    /**
197
     * Set Requirements based on input from CMS
198
     */
199
    public function init()
200
    {
201
        parent::init();
202
        $themeDir = SSViewer::get_theme_folder();
203
        // google maps api key
204
        $key = Config::inst()->get('GoogleGeocoding', 'google_api_key');
205
        $locations = $this->getLocations();
206
        Requirements::block('framework/thirdparty/jquery/jquery.js');
207
        if ($locations) {
208
            Requirements::javascript('https://code.jquery.com/jquery-3.0.0.min.js');
209
            Requirements::javascript('locator/thirdparty/locator-plugin/libs/handlebars/handlebars-v4.0.5.js');
210
            Requirements::javascript('https://maps.googleapis.com/maps/api/js?key=' . $key);
211
            Requirements::javascript('locator/thirdparty/locator-plugin/src/js/jquery.storelocator.js');
212
        }
213
        Requirements::css('locator/css/map.css');
214
        Requirements::css('locator/thirdparty/locator-plugin/src/css/storelocator.css');
215
        $featuredInList = ($locations->filter('Featured', true)->count() > 0);
216
        $featured = $featuredInList
217
            ? 'featuredLocations: true'
218
            : 'featuredLocations: false';
219
        // map config based on user input in Settings tab
220
        // AutoGeocode or Full Map
221
        if ($this->data()->AutoGeocode) {
222
            $load = $featuredInList
223
                ? 'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,'
224
                : 'autoGeocode: true, fullMapStart: false,';
225
        } else {
226
            $load = 'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,';
227
        }
228
        /*$load = ($this->data()->AutoGeocode) ?
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
229
            'autoGeocode: true, fullMapStart: false,' :
230
            'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,';*/
231
        $base = Director::baseFolder();
232
        $themePath = $base . '/' . $themeDir;
233
        $listTemplatePath = (file_exists($themePath . '/templates/location-list-description.html')) ?
234
            $themeDir . '/templates/location-list-description.html' :
235
            'locator/templates/location-list-description.html';
236
        $infowindowTemplatePath = (file_exists($themePath . '/templates/infowindow-description.html')) ?
237
            $themeDir . '/templates/infowindow-description.html' :
238
            'locator/templates/infowindow-description.html';
239
        // in page or modal
240
        $modal = ($this->data()->ModalWindow) ? 'modalWindow: true' : 'modalWindow: false';
241
        $kilometer = ($this->data()->Unit == 'km') ? 'lengthUnit: "km"' : 'lengthUnit: "m"';
242
        // pass GET variables to xml action
243
        $vars = $this->request->getVars();
244
        unset($vars['url']);
245
        unset($vars['action_index']);
246
        $url = '';
247
        if (count($vars)) {
248
            $url .= '?' . http_build_query($vars);
249
        }
250
        $link = $this->AbsoluteLink() . 'xml.xml' . $url;
251
252
        // init map
253
        if ($locations) {
254
            Requirements::customScript("
255
                $(function() {
256
					$('#bh-sl-map-container').storeLocator({
257
					    " . $load . "
258
                        dataLocation: '" . $link . "',
259
                        listTemplatePath: '" . $listTemplatePath . "',
260
                        infowindowTemplatePath: '" . $infowindowTemplatePath . "',
261
                        originMarker: true,
262
                        " . $modal . ',
263
                        ' . $featured . ",
264
                        slideMap: false,
265
                        zoomLevel: 0,
266
                        noForm: true,
267
                        formID: 'Form_LocationSearch',
268
                        inputID: 'Form_LocationSearch_Address',
269
                        categoryID: 'Form_LocationSearch_category',
270
                        distanceAlert: -1,
271
                        " . $kilometer . "
272
                    });
273
                });
274
            ");
275
        }
276
    }
277
278
    /**
279
     * @param SS_HTTPRequest $request
280
     *
281
     * @return ViewableData_Customised
282
     */
283 View Code Duplication
    public function index(SS_HTTPRequest $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284
    {
285
        $locations = $this->getLocations();
286
287
        if ($locations->canSortBy('distance')) {
288
            $locations = $locations->sort('distance');
289
        }
290
291
        return $this->customise(array(
292
            'Locations' => $locations,
293
        ));
294
    }
295
296
    /**
297
     * Return a XML feed of all locations marked "show in locator"
298
     *
299
     * @param SS_HTTPRequest $request
300
     * @return HTMLText
301
     */
302 View Code Duplication
    public function xml(SS_HTTPRequest $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
303
    {
304
        $locations = $this->getLocations();
305
306
        if ($locations->canSortBy('distance')) {
307
            $locations = $locations->sort('distance');
308
        }
309
310
        return $this->customise(array(
311
            'Locations' => $locations,
312
        ))->renderWith('LocationXML');
313
    }
314
315
    /**
316
     * @return ArrayList|DataList
317
     */
318
    public function getLocations()
319
    {
320
        if (!$this->locations) {
321
            $this->setLocations($this->request);
322
        }
323
        return $this->locations;
324
    }
325
326
    /**
327
     * @param SS_HTTPRequest|null $request
328
     * @return $this
329
     */
330
    public function setLocations(SS_HTTPRequest $request = null)
331
    {
332
333
        if ($request === null) {
334
            $request = $this->request;
335
        }
336
        $filter = $this->config()->get('base_filter');
337
338
        if ($request->getVar('CategoryID')) {
339
            $filter['CategoryID'] = $request->getVar('CategoryID');
340
        }
341
342
        $this->extend('updateLocatorFilter', $filter, $request);
343
344
        $filterAny = $this->config()->get('base_filter_any');
345
        $this->extend('updateLocatorFilterAny', $filterAny, $request);
346
347
        $exclude = $this->config()->get('base_exclude');
348
        $this->extend('updateLocatorExclude', $exclude, $request);
349
350
        $callback = null;
351
        $this->extend('updateLocatorCallback', $callback, $request);
352
353
        $locations = Locator::get_locations($filter, $filterAny, $exclude, $callback);
354
        $locations = DataToArrayListHelper::to_array_list($locations);
0 ignored issues
show
Bug introduced by
It seems like $locations defined by \DataToArrayListHelper::to_array_list($locations) on line 354 can also be of type object<ArrayList>; however, DataToArrayListHelper::to_array_list() does only seem to accept object<DataList>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
355
356
        $this->locations = $locations;
357
        return $this;
358
359
    }
360
361
    /**
362
     * @return bool|string
363
     */
364
    public function getAddressSearchCoords()
365
    {
366
        if (!$this->request->getVar('Address')) {
367
            return false;
368
        }
369
        $coords = GoogleGeocoding::address_to_point(Controller::curr()->getRequest()->getVar('Address'));
370
371
        $lat = $coords['lat'];
372
        $lng = $coords['lng'];
373
374
        return "defaultLat: {$lat}, defaultLng: {$lng},";
375
    }
376
377
    /**
378
     * LocationSearch form.
379
     *
380
     * Search form for locations, updates map and results list via AJAX
381
     *
382
     * @return Form
383
     */
384
    public function LocationSearch()
385
    {
386
        if (class_exists('BootstrapForm')) {
387
            $form = LocatorBootstrapForm::create($this, 'LocationSearch');
388
        } else {
389
            $form = LocatorForm::create($this, 'LocationSearch');
390
        }
391
392
        return $form
393
            ->setFormMethod('GET')
394
            ->setFormAction($this->Link())
395
            ->disableSecurityToken()
396
            ->loadDataFrom($this->request->getVars());
397
    }
398
399
}
400