Completed
Pull Request — master (#88)
by Nic
04:59
created

Locator_Controller::index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 2
1
<?php
2
3
class Locator extends Page
4
{
5
    private static $db = array(
6
        'AutoGeocode' => 'Boolean',
7
        'ModalWindow' => 'Boolean',
8
        'Unit' => 'Enum("m,km","m")',
9
    );
10
11
    private static $many_many = array(
12
        'Categories' => 'LocationCategory',
13
    );
14
15
    private static $defaults = array(
16
        'AutoGeocode' => true,
17
    );
18
19 1
    private static $singular_name = 'Locator';
20
    private static $plural_name = 'Locators';
21 1
    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...
22
23
    /**
24 1
     * @return FieldList
25 1
     */
26 1
    public function getCMSFields()
27
    {
28
        $fields = parent::getCMSFields();
29 1
30 1
        // Settings
31
        $fields->addFieldsToTab('Root.Settings', array(
32
            HeaderField::create('DisplayOptions', 'Display Options', 3),
33 1
            OptionsetField::create('Unit', 'Unit of measure', array('m' => 'Miles', 'km' => 'Kilometers')),
34 1
            CheckboxField::create('AutoGeocode', 'Auto Geocode - Automatically filter map results based on user location')
35 1
                ->setDescription('Note: if any locations are set as featured, the auto geocode is automatically disabled.'),
36 1
            CheckboxField::create('ModalWindow', 'Modal Window - Show Map results in a modal window'),
37 1
        ));
38 1
39 1
        // Filter categories
40
        $config = GridFieldConfig_RelationEditor::create();
41 1
        if (class_exists('GridFieldAddExistingSearchButton')) {
42
            $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
43 1
            $config->addComponent(new GridFieldAddExistingSearchButton());
44
        }
45
        $categories = $this->Categories();
46 3
        $categoriesField = GridField::create('Categories', 'Categories', $categories, $config)
47
            ->setDescription('only show locations from the selected category');
48 3
49
        // Filter
50 3
        $fields->addFieldsToTab('Root.Filter', array(
51 3
            HeaderField::create('CategoryOptionsHeader', 'Location Filtering', 3),
52 3
            $categoriesField,
53 3
        ));
54
55
        $this->extend('updateCMSFields', $fields);
56 1
57
        return $fields;
58 1
    }
59
60
    /**
61 1
     * @param array $filter
62
     * @param array $filterAny
63 1
     * @param array $exclude
64
     * @param null $filterByCallback
65
     * @return ArrayList
66
     */
67
    public static function locations(
68
        $filter = array(),
69
        $filterAny = array(),
70
        $exclude = array(),
71
        $filterByCallback = null
72
    )
73
    {
74
        $locationsList = ArrayList::create();
75
76
        // filter by ShowInLocator
77
        $filter['ShowInLocator'] = 1;
78
79
        $locations = Location::get()->filter($filter);
80
81
        if (!empty($filterAny)) {
82
            $locations = $locations->filterAny($filterAny);
83
        }
84
        if (!empty($exclude)) {
85
            $locations = $locations->exclude($exclude);
86
        }
87
88
        if ($filterByCallback !== null && is_callable($filterByCallback)) {
89
            $locations = $locations->filterByCallback($filterByCallback);
90
        }
91
92
        if ($locations->exists()) {
93
            $locationsList->merge($locations);
94
        }
95
96
        return $locationsList;
97
    }
98
99
    /**
100
     * @return DataList
101
     */
102
    public static function getAllCategories()
103
    {
104
        return LocationCategory::get();
105
    }
106
107
    /**
108
     * @param null $id
109
     * @return bool
110
     */
111
    public static function getPageCategories($id = null)
112
    {
113
        if ($id) {
114
            if ($locator = self::get()->byID($id)) {
115
                return $locator->Categories();
116
            }
117
118
            return false;
119
        }
120
121
        return false;
122
    }
123
124
125
}
126
127
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...
128
{
129
    /**
130
     * @var array
131
     */
132
    private static $allowed_actions = array(
133
        'xml',
134
    );
135
136
    /**
137
     * Set Requirements based on input from CMS
138
     */
139
    public function init()
140
    {
141
        parent::init();
142
143
        $themeDir = SSViewer::get_theme_folder();
144
145
        // google maps api key
146
        $key = Config::inst()->get('GoogleGeocoding', 'google_api_key');
147
148
        $locations = $this->Items($this->request);
149
150
        Requirements::javascript('framework/thirdparty/jquery/jquery.js');
151
        if ($locations) {
152
            Requirements::javascript('http://maps.google.com/maps/api/js?key=' . $key);
153
            Requirements::javascript('locator/thirdparty/handlebars/handlebars-v1.3.0.js');
154
            Requirements::javascript('locator/thirdparty/jquery-store-locator/js/jquery.storelocator.js');
155
        }
156
157
        Requirements::css('locator/css/map.css');
158
159
        $featuredInList = ($locations->filter('Featured', true)->count() > 0);
160
161
        $featured = $featuredInList
162
            ? 'featuredLocations: true'
163
            : 'featuredLocations: false';
164
165
        // map config based on user input in Settings tab
166
        // AutoGeocode or Full Map
167
        if ($this->data()->AutoGeocode) {
168 1
            $load = $featuredInList
169
                ? 'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,'
170 1
                : 'autoGeocode: true, fullMapStart: false,';
171 1
        } else {
172 1
            $load = 'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,';
173 1
        }
174
175 1
        /*$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...
176 1
            'autoGeocode: true, fullMapStart: false,' :
177 1
            'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,';*/
178 1
179
        $base = Director::baseFolder();
180 1
        $themePath = $base . '/' . $themeDir;
181
182 1
        $listTemplatePath = (file_exists($themePath . '/templates/location-list-description.html')) ?
183
            $themeDir . '/templates/location-list-description.html' :
184 1
            'locator/templates/location-list-description.html';
185
        $infowindowTemplatePath = (file_exists($themePath . '/templates/infowindow-description.html')) ?
186
            $themeDir . '/templates/infowindow-description.html' :
187
            'locator/templates/infowindow-description.html';
188
189
        // in page or modal
190
        $modal = ($this->data()->ModalWindow) ? 'modalWindow: true' : 'modalWindow: false';
191
192 1
        $kilometer = ($this->data()->Unit == 'km') ? 'lengthUnit: "km"' : 'lengthUnit: "m"';
193
194 1
        // pass GET variables to xml action
195 1
        $vars = $this->request->getVars();
196 1
        unset($vars['url']);
197
        unset($vars['action_index']);
198 1
        $url = '';
199
        if (count($vars)) {
200
            $url .= '?' . http_build_query($vars);
201 1
        }
202
        $link = $this->Link() . 'xml.xml' . $url;
203
204
        // init map
205
        if ($locations) {
206
            Requirements::customScript("
207
                $(function($) {
208
                    $('#map-container').storeLocator({
209
                        " . $load . "
210
                        dataLocation: '" . $link . "',
211
                        listTemplatePath: '" . $listTemplatePath . "',
212
                        infowindowTemplatePath: '" . $infowindowTemplatePath . "',
213
                        originMarker: true,
214
                        " . $modal . ',
215
                        ' . $featured . ",
216
                        slideMap: false,
217
                        zoomLevel: 0,
218
                        noForm: true,
219
                        formID: 'Form_LocationSearch',
220
                        inputID: 'Form_LocationSearch_Address',
221
                        categoryID: 'Form_LocationSearch_category',
222
                        distanceAlert: -1,
223
                        " . $kilometer . '
224
                    });
225
                });
226
            ');
227
        }
228
    }
229
230
    /**
231
     * @param SS_HTTPRequest $request
232
     *
233
     * @return ViewableData_Customised
234
     */
235
    public function index(SS_HTTPRequest $request)
236
    {
237
        $locations = $this->Items($request);
238
239
        return $this->customise(array(
240
            'Locations' => $locations,
241
        ));
242
    }
243
244
    /**
245
     * Return a XML feed of all locations marked "show in locator"
246
     *
247
     * @param SS_HTTPRequest $request
248
     * @return HTMLText
249
     */
250
    public function xml(SS_HTTPRequest $request)
251
    {
252
        $locations = $this->Items($request);
253
254
        return $this->customise(array(
255
            'Locations' => $locations,
256
        ))->renderWith('LocationXML');
257
    }
258
259
    /**
260
     * @param SS_HTTPRequest $request
261
     *
262
     * @return ArrayList
263
     */
264
    public function Items(SS_HTTPRequest $request)
265
    {
266
        $request = ($request) ? $request : $this->request;
267
268
        $filter = array();
269
        $filterAny = array();
270
        $exclude = ['Lat' => 0.00000, 'Lng' => 0.00000];
271
272
        // only show locations marked as ShowInLocator
273
        $filter['ShowInLocator'] = 1;
274
275
        // search across all address related fields
276
        $address = ($request->getVar('Address')) ? $request->getVar('Address') : false;
277
        if ($address && $this->data()->AutoGeocode == 0) {
278
            $filterAny['Address:PartialMatch'] = $address;
279
            $filterAny['Suburb:PartialMatch'] = $address;
280
            $filterAny['State:PartialMatch'] = $address;
281
            $filterAny['Postcode:PartialMatch'] = $address;
282
            $filterAny['Country:PartialMatch'] = $address;
283
        } else {
284
            unset($filter['Address']);
285
        }
286
287
        // search for category from form, else categories from Locator
288
        $category = ($request->getVar('CategoryID')) ? $request->getVar('CategoryID') : false;
289
        if ($category) {
290
            $filter['CategoryID:ExactMatch'] = $category;
291
        } elseif ($this->Categories()->exists()) {
292
            $categories = $this->Categories();
293
            $categoryArray = array();
294
            foreach ($categories as $category) {
295
                array_push($categoryArray, $category->ID);
296
            }
297
            $filter['CategoryID'] = $categoryArray;
298
        }
299
300
        $locations = Locator::locations($filter, $filterAny, $exclude);
301
302
        return $locations;
303
    }
304
305
    /**
306
     * LocationSearch form.
307
     *
308
     * Search form for locations, updates map and results list via AJAX
309
     *
310
     * @return Form
311
     */
312
    public function LocationSearch()
313
    {
314
        $fields = FieldList::create(
315
            $address = TextField::create('Address', '')
316
                ->setAttribute('placeholder', 'address or zip code')
317
        );
318
319
        $filterCategories = Locator::getPageCategories($this->ID);
320
        $allCategories = Locator::getAllCategories();
321
322
        if ($allCategories->Count() > 0) {
323
            $categories = ArrayList::create();
324
            if ($filterCategories->Count() > 0) {
0 ignored issues
show
Bug introduced by
The method Count cannot be called on $filterCategories (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
325
                if ($filterCategories->Count() != 1) {
0 ignored issues
show
Bug introduced by
The method Count cannot be called on $filterCategories (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
326
                    $categories = $filterCategories;
327
                }
328
            } else {
329
                $categories = $allCategories;
330
            }
331
332
            if ($categories->count() > 0) {
0 ignored issues
show
Bug introduced by
The method count does only exist in DataList, but not in Locator_Controller.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
333
                $fields->push(
334
                    DropdownField::create(
335
                        'CategoryID',
336
                        '',
337
                        $categories->map()
0 ignored issues
show
Bug introduced by
The method map does only exist in DataList, but not in Locator_Controller.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
338
                    )->setEmptyString('All Categories'));
339
            }
340
        }
341
342
        $actions = FieldList::create(
343
            FormAction::create('index', 'Search')
344
        );
345
346
        if (class_exists('BootstrapForm')) {
347
            $form = BootstrapForm::create($this, 'LocationSearch', $fields, $actions);
348
        } else {
349
            $form = Form::create($this, 'LocationSearch', $fields, $actions);
350
        }
351
352
        return $form
353
            ->setFormMethod('GET')
354
            ->setFormAction($this->Link())
355
            ->disableSecurityToken()
356
            ->loadDataFrom($this->request->getVars());
357
    }
358
}
359