Arbit - project tracking

PHPillow - PHP CouchDB connector

Browse source code

File: / src/ classes/ view.php

Type
text/plain text/plain
Last Author
hco
Version
176
Line Rev. Author Source
1 1 kore <?php
2 kore /**
3 2 kore * phpillow CouchDB backend
4 1 kore *
5 2 kore * This file is part of phpillow.
6 1 kore *
7 3 kore * phpillow is free software; you can redistribute it and/or modify it under
8 kore * the terms of the GNU Lesser General Public License as published by the Free
9 kore * Software Foundation; version 3 of the License.
10 1 kore *
11 3 kore * phpillow is distributed in the hope that it will be useful, but WITHOUT ANY
12 kore * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 kore * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
14 kore * more details.
15 1 kore *
16 3 kore * You should have received a copy of the GNU Lesser General Public License
17 kore * along with phpillow; if not, write to the Free Software Foundation, Inc., 51
18 kore * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 1 kore *
20 kore * @package Core
21 4 kore * @version $Revision: 176 $
22 3 kore * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPL
23 1 kore */
24 kore
25 kore /**
26 kore * Wrapper base for views in the database
27 kore *
28 kore * @package Core
29 4 kore * @version $Revision: 176 $
30 3 kore * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPL
31 1 kore */
32 4 kore abstract class phpillowView extends phpillowDocument
33 1 kore {
34 kore /**
35 kore * List of required properties. For each required property, which is not
36 kore * set, a validation exception will be thrown on save.
37 151 seldaek *
38 1 kore * @var array
39 kore */
40 kore protected $requiredProperties = array(
41 kore 'language',
42 kore 'views'
43 kore );
44 kore
45 kore /**
46 kore * View functions to be registered on the server
47 kore *
48 kore * @var array
49 kore */
50 kore protected $viewDefinitions = array();
51 kore
52 kore /**
53 31 kore * Reduce function for a view function.
54 kore *
55 kore * A reduce function may be used to aggregate / reduce the results
56 kore * calculated by a view function. See the CouchDB documentation for more
57 kore * results: @TODO: Not yet documented.
58 kore *
59 kore * Each view reduce function MUST have a view definition with the same
60 kore * name, otherwise there is nothing to reduce.
61 151 seldaek *
62 31 kore * @var array
63 kore */
64 kore protected $viewReduces = array();
65 kore
66 kore /**
67 1 kore * Construct new document
68 151 seldaek *
69 1 kore * Construct new document
70 151 seldaek *
71 1 kore * @return void
72 kore */
73 kore public function __construct()
74 kore {
75 kore $this->properties = array(
76 6 kore 'language' => new phpillowRegexpValidator( '(^(?:javascript)$)' ),
77 3 kore 'views' => new phpillowArrayValidator(),
78 1 kore );
79 kore
80 kore parent::__construct();
81 kore
82 6 kore $this->language = 'javascript';
83 1 kore $this->views = $this->viewDefinitions;
84 kore }
85 kore
86 kore /**
87 kore * Get name of view
88 151 seldaek *
89 1 kore * Get name of view
90 151 seldaek *
91 1 kore * @return string
92 kore */
93 94 kore protected function getViewName()
94 1 kore {
95 2 kore throw new phpillowRuntimeException(
96 159 kore 'This method should be considered abstract, but PHP does not allow this.'
97 1 kore );
98 kore }
99 kore
100 kore /**
101 kore * Get document ID from object ID
102 kore *
103 kore * Composes the document ID out of the document type and the generated ID
104 kore * for the current document.
105 151 seldaek *
106 seldaek * @param string $type
107 seldaek * @param string $id
108 1 kore * @return string
109 kore */
110 94 kore protected function getDocumentId( $type, $id )
111 1 kore {
112 kore return '_design/' . $id;
113 kore }
114 kore
115 kore /**
116 94 kore * Return document type name
117 kore *
118 kore * This method is required to be implemented to return the document type
119 kore * for PHP versions lower then 5.2. When only using PHP 5.3 and higher you
120 kore * might just implement a method which does "return static:$type" in a base
121 kore * class.
122 151 seldaek *
123 94 kore * @return void
124 kore */
125 kore protected function getType()
126 kore {
127 kore return '_view';
128 kore }
129 kore
130 kore /**
131 1 kore * Get ID from document
132 kore *
133 kore * The ID normally should be calculated on some meaningful / unique
134 kore * property for the current ttype of documents. The returned string should
135 kore * not be too long and should not contain multibyte characters.
136 151 seldaek *
137 1 kore * @return string
138 kore */
139 kore protected function generateId()
140 kore {
141 94 kore return $this->stringToId( $this->getViewName() );
142 1 kore }
143 kore
144 kore /**
145 kore * Wrapper for more convenient view queries
146 kore *
147 kore * Wrap all static calls to a extended view class, instantiate it and then
148 kore * call query method on the view object, reusing the called method name to
149 kore * query the view.
150 94 kore *
151 159 kore * This convenient method only works with PHP in version 5.3 or greater.
152 94 kore * Otherwise just call the query method directly on an instantiated view.
153 kore *
154 151 seldaek * @param string $method
155 seldaek * @param array $parameters
156 3 kore * @return phpillowResultArray
157 1 kore */
158 kore public static function __callStatic( $method, $parameters )
159 kore {
160 94 kore $class = get_called_class();
161 kore $view = new $class();
162 1 kore
163 kore // Check if options were set
164 kore $options = ( isset( $parameters[0] ) ? $parameters[0] : array() );
165 kore
166 kore // Execute query in normal manner
167 kore return $view->query( $method, $options );
168 kore }
169 kore
170 kore /**
171 kore * Build view query string from options
172 kore *
173 159 kore * Validates and transformed passed options to limit the view data, to fit
174 1 kore * the specifications in the HTTP view API, documented at:
175 141 rmehner * http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
176 151 seldaek *
177 seldaek * @param array $options
178 1 kore * @return string
179 kore */
180 kore protected function buildViewQuery( array $options )
181 kore {
182 kore // Return empty query string, if no options has been passed
183 kore if ( $options === array() )
184 kore {
185 kore return '';
186 kore }
187 kore
188 kore $queryString = '?';
189 kore foreach ( $options as $key => $value )
190 kore {
191 kore switch ( $key )
192 kore {
193 kore case 'key':
194 kore case 'startkey':
195 kore case 'endkey':
196 kore // These values has to be valid JSON encoded strings, so we
197 kore // just encode the passed data, whatever it is, as CouchDB
198 kore // may use complex datatypes as a key, like arrays or
199 kore // objects.
200 kore $queryString .= $key . '=' . urlencode( json_encode( $value ) );
201 kore break;
202 kore
203 kore case 'startkey_docid':
204 159 kore // The docidstartkey is handled differently then the other
205 1 kore // keys and is just passed as a string, because it always
206 kore // is and can only be a string.
207 kore $queryString .= $key . '=' . urlencode( (string) $value );
208 kore break;
209 kore
210 144 kore case 'reduce':
211 53 kore case 'group':
212 1 kore case 'update':
213 kore case 'descending':
214 158 kore case 'include_docs':
215 1 kore // These two values may only contain boolean values, passed
216 kore // as "true" or "false". We just perform a typical PHP
217 kore // boolean typecast to transform the values.
218 kore $queryString .= $key . '=' . ( $value ? 'true' : 'false' );
219 kore break;
220 kore
221 91 kore case 'skip':
222 53 kore case 'group_level':
223 1 kore // Theses options accept integers defining the limits of
224 kore // the query. We try to typecast to int.
225 kore $queryString .= $key . '=' . ( (int) $value );
226 kore break;
227 kore
228 93 jan case 'count': // CouchDB 0.8. compat
229 88 kore case 'limit':
230 kore // Theses options accept integers defining the limits of
231 kore // the query. We try to typecast to int.
232 kore $queryString .= 'limit=' . ( (int) $value );
233 kore break;
234 kore
235 1 kore default:
236 3 kore throw new phpillowNoSuchPropertyException( $key );
237 1 kore }
238 kore
239 kore $queryString .= '&';
240 kore }
241 kore
242 kore // Return query string, but remove appended '&' first.
243 kore return substr( $queryString, 0, -1 );
244 kore }
245 kore
246 kore /**
247 kore * Query a view
248 kore *
249 kore * Query the specified view to get a set of results. You may optionally use
250 159 kore * the view query options as additional parameters to limit the returns
251 1 kore * values, specified at:
252 141 rmehner * http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
253 151 seldaek *
254 seldaek * @param string $view
255 seldaek * @param array $options
256 3 kore * @return phpillowResultArray
257 1 kore */
258 kore public function query( $view, array $options = array() )
259 kore {
260 kore // Build query string, just as a normal HTTP GET query string
261 176 hco $url = $this->getDatabase() .
262 93 jan '_design/' . $this->getViewName() . '/_view/' . $view;
263 1 kore $url .= $this->buildViewQuery( $options );
264 kore
265 kore // Get database connection, because we directly execute a query here.
266 176 hco $db = $this->getConnection();
267 1 kore
268 kore try
269 kore {
270 kore // Try to execute query, a failure most probably means, the view
271 kore // has not been added, yet.
272 kore $response = $db->get( $url );
273 kore }
274 3 kore catch ( phpillowResponseErrorException $e )
275 1 kore {
276 kore // Ensure view has been created properly and then try to execute
277 kore // the query again. If it still fails, there is most probably a
278 kore // real problem.
279 kore $this->verifyView();
280 kore $response = $db->get( $url );
281 kore }
282 kore
283 kore return $response;
284 kore }
285 kore
286 kore /**
287 kore * Verify stored views
288 kore *
289 kore * Check if the views stored in the database equal the view definitions
290 159 kore * specified by the vew classes. If the implementation differs update to the
291 1 kore * view specifications in the class.
292 151 seldaek *
293 1 kore * @return void
294 kore */
295 kore public function verifyView()
296 kore {
297 kore // Fetch view definition from database
298 kore try
299 kore {
300 94 kore $view = self::fetchById( '_design/' . $this->getViewName() );
301 1 kore }
302 89 kore catch ( phpillowResponseNotFoundErrorException $e )
303 1 kore {
304 94 kore // If the view does not exist yet, recreate it from current view
305 kore $view = $this;
306 1 kore }
307 151 seldaek
308 1 kore // Force setting of view definitions
309 6 kore $views = array();
310 kore foreach ( $this->viewDefinitions as $name => $function )
311 kore {
312 kore $views[$name]['map'] = $function;
313 31 kore
314 kore // Check if there is also a reduce function for the given view
315 kore // function.
316 kore if ( isset( $this->viewReduces[$name] ) )
317 kore {
318 kore $views[$name]['reduce'] = $this->viewReduces[$name];
319 kore }
320 6 kore }
321 kore
322 kore $view->views = $views;
323 1 kore $view->save();
324 kore }
325 kore }
326 kore