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

Locator::get_locations()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 8.6737
cc 5
eloc 13
nc 8
nop 4
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
                        inputID: 'Form_LocationSearch_Address',
268
                        categoryID: 'Form_LocationSearch_category',
269
                        distanceAlert: -1,
270
                        " . $kilometer . "
271
                    });
272
                });
273
            ");
274
        }
275
    }
276
277
    /**
278
     * @param SS_HTTPRequest $request
279
     *
280
     * @return ViewableData_Customised
281
     */
282 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...
283
    {
284
        $locations = $this->getLocations();
285
286
        if ($locations->canSortBy('distance')) {
287
            $locations = $locations->sort('distance');
288
        }
289
290
        return $this->customise(array(
291
            'Locations' => $locations,
292
        ));
293
    }
294
295
    /**
296
     * Return a XML feed of all locations marked "show in locator"
297
     *
298
     * @param SS_HTTPRequest $request
299
     * @return HTMLText
300
     */
301 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...
302
    {
303
        $locations = $this->getLocations();
304
305
        if ($locations->canSortBy('distance')) {
306
            $locations = $locations->sort('distance');
307
        }
308
309
        return $this->customise(array(
310
            'Locations' => $locations,
311
        ))->renderWith('LocationXML');
312
    }
313
314
    /**
315
     * @return ArrayList|DataList
316
     */
317
    public function getLocations()
318
    {
319
        if (!$this->locations) {
320
            $this->setLocations($this->request);
321
        }
322
        return $this->locations;
323
    }
324
325
    /**
326
     * @param SS_HTTPRequest|null $request
327
     * @return $this
328
     */
329
    public function setLocations(SS_HTTPRequest $request = null)
330
    {
331
332
        if ($request === null) {
333
            $request = $this->request;
334
        }
335
        $filter = $this->config()->get('base_filter');
336
337
        if ($request->getVar('CategoryID')) {
338
            $filter['CategoryID'] = $request->getVar('CategoryID');
339
        }
340
341
        $this->extend('updateLocatorFilter', $filter, $request);
342
343
        $filterAny = $this->config()->get('base_filter_any');
344
        $this->extend('updateLocatorFilterAny', $filterAny, $request);
345
346
        $exclude = $this->config()->get('base_exclude');
347
        $this->extend('updateLocatorExclude', $exclude, $request);
348
349
        $callback = null;
350
        $this->extend('updateLocatorCallback', $callback, $request);
351
352
        $locations = Locator::get_locations($filter, $filterAny, $exclude, $callback);
353
        $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 353 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...
354
355
        $this->locations = $locations;
356
        return $this;
357
358
    }
359
360
    /**
361
     * @return bool|string
362
     */
363
    public function getAddressSearchCoords()
364
    {
365
        if (!$this->request->getVar('Address')) {
366
            return false;
367
        }
368
        $coords = GoogleGeocoding::address_to_point(Controller::curr()->getRequest()->getVar('Address'));
369
370
        $lat = $coords['lat'];
371
        $lng = $coords['lng'];
372
373
        return "defaultLat: {$lat}, defaultLng: {$lng},";
374
    }
375
376
    /**
377
     * LocationSearch form.
378
     *
379
     * Search form for locations, updates map and results list via AJAX
380
     *
381
     * @return Form
382
     */
383
    public function LocationSearch()
384
    {
385
        if (class_exists('BootstrapForm')) {
386
            $form = LocatorBootstrapForm::create($this, 'LocationSearch');
387
        } else {
388
            $form = LocatorForm::create($this, 'LocationSearch');
389
        }
390
391
        return $form
392
            ->setFormMethod('GET')
393
            ->setFormAction($this->Link())
394
            ->disableSecurityToken()
395
            ->loadDataFrom($this->request->getVars());
396
    }
397
398
}
399