adding example apps, fixing powerup issues
[feisty_meow.git] / production / example_apps / zippy_maps / src / Model / Table / LocationsTable.php
1 <?php
2 namespace App\Model\Table;
3
4 use Cake\Log\Log;
5 use Cake\ORM\Query;
6 use Cake\ORM\RulesChecker;
7 use Cake\ORM\Table;
8 use Cake\Validation\Validator;
9
10 /**
11  * Locations Model
12  *
13  * @method \App\Model\Entity\Location get($primaryKey, $options = [])
14  * @method \App\Model\Entity\Location newEntity($data = null, array $options = [])
15  * @method \App\Model\Entity\Location[] newEntities(array $data, array $options = [])
16  * @method \App\Model\Entity\Location|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
17  * @method \App\Model\Entity\Location patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
18  * @method \App\Model\Entity\Location[] patchEntities($entities, array $data, array $options = [])
19  * @method \App\Model\Entity\Location findOrCreate($search, callable $callback = null, $options = [])
20  *
21  * @mixin \Cake\ORM\Behavior\TimestampBehavior
22  */
23 class LocationsTable extends Table
24 {
25
26     /**
27      * Initialize method
28      *
29      * @param array $config The configuration for the Table.
30      * @return void
31      */
32     public function initialize(array $config)
33     {
34         parent::initialize($config);
35
36         $this->setTable('locations');
37         $this->setDisplayField('name');
38         $this->setPrimaryKey('id');
39
40         $this->addBehavior('Timestamp');
41         
42         $this->belongsToMany('Categories',
43                         [
44                                         'targetForeignKey' => 'category_id',
45                                         'foreignKey' => 'location_id',
46                                         'joinTable' => 'categories_locations',
47                                         'through' => 'CategoriesLocations',
48                         ]);
49         
50         
51     }
52
53     /**
54      * Default validation rules.
55      *
56      * @param \Cake\Validation\Validator $validator Validator instance.
57      * @return \Cake\Validation\Validator
58      */
59     public function validationDefault(Validator $validator)
60     {
61         $validator
62             ->integer('id')
63             ->allowEmpty('id', 'create');
64
65         $validator
66             ->requirePresence('name', 'create')
67             ->notEmpty('name')
68             ->add('name', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']);
69
70         $validator->requirePresence ( 'location', 'create' )->notEmpty ( 'location' );
71                 
72                 $validator
73             ->allowEmpty('lat');
74         $validator
75             ->allowEmpty('lng');
76             
77         return $validator;
78     }
79
80     /**
81      * Returns a rules checker object that will be used for validating
82      * application integrity.
83      *
84      * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
85      * @return \Cake\ORM\RulesChecker
86      */
87     public function buildRules(RulesChecker $rules)
88     {
89         $rules->add($rules->isUnique(['name']));
90
91         return $rules;
92     }
93     
94     
95     /**
96      * returns a query that will find the category records associated with a location 'id'
97      * as a list.
98      */
99     public function getSelectedCategories($id)
100     {
101         // find the categories that are chosen for this item, if any.
102         $selectedCategories = $this->CategoriesLocations->find ( 'list', [
103                         'keyField' => 'category_id',
104                         'valueField' => 'location_id',
105                         'conditions' => [
106                                         'location_id' => $id
107                         ],
108                         'contain' => [
109                                         'Categories'
110                         ]
111         ] );            
112         return $selectedCategories;
113     }
114         
115         /**
116          * returns a query that will find all of the locations within the bounding box.
117          */
118         public function getLocationsInBox($sw_lat, $sw_long, $ne_lat, $ne_long, $start = null, $end = null) {
119                 $locationsInRange = $this->find ( 'all', [ 
120                         'contain' => ['Categories'],
121                 ] );
122                 
123                 Log::Debug ( 'bounds=' . $sw_lat . ', ' . $sw_long . ' to ' . $ne_lat . ', ' . $ne_long );
124                 
125                 $bounds = [ 
126                                 $sw_lat,
127                                 $sw_long,
128                                 $ne_lat,
129                                 $ne_long 
130                 ];
131                 
132                 if (! $bounds) {
133                         Log::Debug ( "failed to calculate the bounding box!" );
134                 } else {
135                         Log::Debug ( "bounding box: " . var_export ( $bounds, true ) );
136                 }
137                 
138                 // use the boundaries to restrict the lookup so we aren't crushed.
139                 // order: min_lat, min_long, max_lat, max_long.
140                 $locationsInRange = $locationsInRange->where ( function ($exp) use ($bounds) {
141                         return $exp->gte ( 'lat', $bounds [0] )->gte ( 'lng', $bounds [1] )->lte ( 'lat', $bounds [2] )->lte ( 'lng', $bounds [3] );
142                 } );
143                 
144                 if (($start !== null) && ($end !== null)) {
145                         Log::debug('start of range = ' . $start . ' and end = ' . $end);
146                         $locationsInRange = $locationsInRange->order(['lat desc', 'lng desc']);
147                         $chunk = $end - $start + 1;
148                         $locationsInRange = $locationsInRange->limit($chunk);
149                         $page = 1 + (int)($start / $chunk);
150                         $locationsInRange = $locationsInRange->page($page);
151                         Log::debug('page = ' . $page . ' and chunk = ' . $chunk);
152                 }
153                 
154                 return $locationsInRange;
155         }
156         
157         /**
158          * retrieves all of the locations within the bounding box as a list of partial location data.
159          */
160         public function getChewedLocationsInBox($sw_lat, $sw_long, $ne_lat, $ne_long, $start = null, $end = null) {
161                 $locationsInRange = $this->getLocationsInBox($sw_lat, $sw_long, $ne_lat, $ne_long, $start, $end);
162                 
163                 // track locations we've already added and do not add ones at exactly the same lat/long.
164                 // if we did add these, google maps screws up in representing them.
165                 $locationsToSerialize = [ ];
166                 
167                 foreach ( $locationsInRange as $location ) {
168                         $lat_and_long = $location->lat . ',' . $location->lng;
169                         if (array_key_exists ( $lat_and_long, $locationsToSerialize )) {
170                                 continue; // already got it.
171                         }
172
173 // Log::debug('dumping location row: ' . var_export($location, true));
174                         
175                         // we don't include lat and lng below since they are encoded as array key.
176                         $locationsToSerialize [$lat_and_long] = [ 
177                                         'name' => $location->name,
178                                         'loc' => $location->location,
179                                         'id' => $location->id 
180                         ];
181                         
182                         // only try to add the image if the location actually has a category membership (at least one).
183                         if ($location->categories) {
184                                 $locationsToSerialize [$lat_and_long]['icon'] = $location->categories[0]->image;
185                         }                                               
186                 }
187                 
188         return $locationsToSerialize;
189     }
190     
191 }