This method can be thought of as a hybrid between PHP's array_merge()
and array_merge_recursive(). The difference to the two is that if an
array key contains another array then the function behaves recursive
(unlike array_merge()) but does not do if for keys containing strings
(unlike array_merge_recursive()). Please note: This function will work
with an unlimited amount of arguments and typecasts non-array parameters
into arrays.
/** * Sets up the adapter with the configuration assigned by the `Session` class. * * @param array $config Available configuration options for this adapter: * - `'config'` _string_: The name of the model that this adapter should use. */ public function __construct(array $config = array()) { $this->config = Set::merge($this->config, $config); if (empty($this->config['model']) || !class_exists($this->config['model'])) { throw new ConfigException("No valid model \"{$this->config['model']}\" available to use for Session interaction"); } elseif (empty($this->config['entityManager']) && (!method_exists($this->config['model'], 'getEntityManager') || !is_callable($this->config['model'] . '::getEntityManager'))) { throw new ConfigException("The session model {$this->config['model']} must define a getEntityManager() static method, or you must set the entityManager session config variable"); } $reflection = new \ReflectionClass($this->config['model']); if (!$reflection->implementsInterface('li3_doctrine2\\models\\ISession')) { throw new ConfigException("The model {$this->config['model']} must implement ISession"); } $this->entityManager = $this->config['entityManager'] ?: call_user_func($this->config['model'] . '::getEntityManager'); if (!isset($this->entityManager) || !$this->entityManager instanceof EntityManager) { throw new ConfigException('Not a valid entity manager'); } $this->repository = $this->entityManager->getRepository($this->config['model']); foreach ($this->config['ini'] as $key => $config) { if (isset($this->config['ini'][$key]) && ini_set("session.{$key}", $this->config['ini'][$key]) === false) { throw new ConfigException("Could not initialize the session variable {$key}"); } } session_set_save_handler(array(&$this, '_open'), array(&$this, '_close'), array(&$this, '_read'), array(&$this, '_write'), array(&$this, '_destroy'), array(&$this, '_gc')); register_shutdown_function('session_write_close'); if ($this->config['start']) { $this->_startup(); } }
/** * */ public function __construct($config = array()) { $defaults = array('proxy' => array('auto' => true, 'path' => LITHIUM_APP_PATH . '/resources/tmp/cache/Doctrine/Proxies', 'namespace' => 'Doctrine\\Proxies'), 'useModelDriver' => true, 'mapping' => array('class' => null, 'path' => LITHIUM_APP_PATH . '/models'), 'configuration' => null, 'eventManager' => null, 'utf-8' => true); $config = Set::merge($defaults, $config); $configuration = $config['configuration'] ?: new Configuration(); $eventManager = $config['eventManager'] ?: new EventManager(); $configuration->setProxyDir($config['proxy']['path']); $configuration->setProxyNamespace($config['proxy']['namespace']); $configuration->setAutoGenerateProxyClasses($config['proxy']['auto']); $configuration->setMetadataCacheImpl(new ArrayCache()); // Annotation Driver $driver = $configuration->newDefaultAnnotationDriver(array(LITHIUM_APP_PATH . '/models')); $configuration->setMetadataDriverImpl($driver); // TODO: Not sure if this is required or not! // if (!$config['useModelDriver']) { // $configuration->setMetadataDriverImpl(new ModelDriver()); // } $configuration->setSqlLogger(new SqlLogger()); $mapping = array('adapter' => 'driver', 'login' => 'user', 'database' => 'dbname'); foreach ($mapping as $source => $dest) { if (isset($config[$source]) && !isset($config[$dest])) { $config[$dest] = $config[$source]; unset($config[$source]); } } $this->_em = EntityManager::create($config, $configuration, $eventManager); $this->_sm = $this->_em->getConnection()->getSchemaManager(); if ($this->_em->getConnection()->getDriver() instanceof Driver\PDOMySql\Driver && $config['utf-8']) { $this->_em->getEventManager()->addEventSubscriber(new MysqlSessionInit('utf8', 'utf8_unicode_ci')); } parent::__construct($config); }
public function testArrayConfiguration() { $result = Configurations::create(Set::merge($this->_default, array('type' => 'array', 'value' => 'foo=bar'))); $this->assertEqual(array('foo' => 'bar'), $result->val()); $this->assertEqual('bar', $result->val('foo')); $result = Configurations::create(Set::merge($this->_default, array('type' => 'array', 'value' => "foo.bar=baz\nfoo.baz=bar"))); $this->assertEqual(array('foo' => array('bar' => 'baz', 'baz' => 'bar')), $result->val()); $this->assertEqual(array('bar' => 'baz', 'baz' => 'bar'), $result->val('foo')); $this->assertEqual('baz', $result->val('foo.bar')); }
/** * Object constructor. * Instantiates the Memcached object, adds appropriate servers to the pool, * and configures any optional settings passed. * * @see lithium\storage\Cache::config() * @param array $config Configuration parameters for this cache adapter. * These settings are indexed by name and queryable * through `Cache::config('name')`. * @return void */ public function __construct(array $config = array()) { $defaults = array('prefix' => '', 'expiry' => '+1 hour', 'servers' => array(array('127.0.0.1', 11211, 100))); if (is_null(static::$connection)) { static::$connection = new \Memcached(); } $configuration = Set::merge($defaults, $config); parent::__construct($configuration); static::$connection->addServers($this->_config['servers']); }
/** * Create `RecaptchaOptions` javascript object if you have additional options configured * @param array $options `'key' => 'value'` pairs to be converted to javascript * object as `RecaptchaOptions` * @see https://developers.google.com/recaptcha/docs/customization * @return null|string Return null if you don't have additional options * or script with `RecaptchaOptions` object */ protected function _recaptchaOptions(array $options = array()) { $defaults = Libraries::get('li3_recaptcha', 'options'); if ($defaults) { $options = Set::merge($defaults, $options); } if (empty($options)) { return null; } $script = '<script type="text/javascript">'; $script .= 'var RecaptchaOptions = ' . json_encode($options) . ';'; $script .= '</script>'; return $script; }
/** * Pulls request data from superglobals. * * @return void */ protected function _init() { parent::_init(); $mobile = array('iPhone', 'MIDP', 'AvantGo', 'BlackBerry', 'J2ME', 'Opera Mini', 'DoCoMo', 'NetFront', 'Nokia', 'PalmOS', 'PalmSource', 'portalmmm', 'Plucker', 'ReqwirelessWeb', 'iPod', 'SonyEricsson', 'Symbian', 'UP\\.Browser', 'Windows CE', 'Xiino', 'Android'); if (!empty($this->_config['detectors']['mobile'][1])) { $mobile = array_merge($mobile, (array) $this->_config['detectors']['mobile'][1]); } $this->_detectors['mobile'][1] = $mobile; $this->_env += (array) $_SERVER + (array) $_ENV + array('REQUEST_METHOD' => 'GET'); $envs = array('isapi' => 'IIS', 'cgi' => 'CGI', 'cgi-fcgi' => 'CGI'); $this->_env['PLATFORM'] = isset($envs[PHP_SAPI]) ? $envs[PHP_SAPI] : null; $this->_base = isset($this->_base) ? $this->_base : $this->_base(); $this->url = '/'; if (isset($this->_config['url'])) { $this->url = rtrim($this->_config['url'], '/'); } elseif (!empty($_GET['url'])) { $this->url = rtrim($_GET['url'], '/'); unset($_GET['url']); } if (!empty($this->_config['query'])) { $this->query = $this->_config['query']; } if (isset($_GET)) { $this->query += $_GET; } if (!empty($this->_config['data'])) { $this->data = $this->_config['data']; } elseif (isset($_POST)) { $this->data += $_POST; } if (isset($this->data['_method'])) { $this->_env['HTTP_X_HTTP_METHOD_OVERRIDE'] = strtoupper($this->data['_method']); unset($this->data['_method']); } if (!empty($this->_env['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $this->_env['REQUEST_METHOD'] = $this->_env['HTTP_X_HTTP_METHOD_OVERRIDE']; } $method = strtoupper($this->_env['REQUEST_METHOD']); if (($method == 'POST' || $method == 'PUT') && !$this->data) { if (($type = $this->type()) && $type !== 'html') { $this->_stream = $this->_stream ?: fopen('php://input', 'r'); $media = $this->_classes['media']; $this->data = (array) $media::decode($type, stream_get_contents($this->_stream)); fclose($this->_stream); } } if (isset($_FILES) && $_FILES) { $result = array(); $normalize = function ($key, $value) use($result, &$normalize) { foreach ($value as $param => $content) { foreach ($content as $num => $val) { if (is_numeric($num)) { $result[$key][$num][$param] = $val; continue; } if (is_array($val)) { foreach ($val as $next => $one) { $result[$key][$num][$next][$param] = $one; } continue; } $result[$key][$num][$param] = $val; } } return $result; }; foreach ($_FILES as $key => $value) { if (isset($value['name'])) { if (is_string($value['name'])) { $result[$key] = $value; continue; } if (is_array($value['name'])) { $result += $normalize($key, $value); } } } $this->data = Set::merge((array) $this->data, $result); } }
/** * Instantiates a new record or document object, initialized with any data passed in. For * example: * * {{{ * $post = Posts::create(array('title' => 'New post')); * echo $post->title; // echoes 'New post' * $success = $post->save(); * }}} * * Note that while this method creates a new object, there is no effect on the database until * the `save()` method is called. * * In addition, this method can be used to simulate loading a pre-existing object from the * database, without actually querying the database: * * {{{ * $post = Posts::create(array('id' => $id, 'moreData' => 'foo'), array('exists' => true)); * $post->title = 'New title'; * $success = $post->save(); * }}} * * This will create an update query against the object with an ID matching `$id`. Also note that * only the `title` field will be updated. * * @param array $data Any data that this object should be populated with initially. * @param array $options Options to be passed to item. * @return object Returns a new, _un-saved_ record or document object. In addition to the values * passed to `$data`, the object will also contain any values assigned to the * `'default'` key of each field defined in `$_schema`. * @filter */ public static function create(array $data = array(), array $options = array()) { return static::_filter(__FUNCTION__, compact('data', 'options'), function ($self, $params) { $data = Set::merge(Set::expand($self::schema()->defaults()), $params['data']); return $self::connection()->item($self, $data, $params['options']); }); }
public function testMerge() { $result = Set::merge(array('foo'), array()); $this->assertIdentical($result, array('foo')); $result = Set::merge((array) 'foo', (array) 'bar'); $this->assertIdentical($result, array('foo', 'bar')); $result = Set::merge((array) 'foo', array('user' => 'bob', 'no-bar')); $this->assertIdentical($result, array('foo', 'user' => 'bob', 'no-bar')); $a = array('foo', 'foo2'); $b = array('bar', 'bar2'); $this->assertIdentical(Set::merge($a, $b), array('foo', 'foo2', 'bar', 'bar2')); $a = array('foo' => 'bar', 'bar' => 'foo'); $b = array('foo' => 'no-bar', 'bar' => 'no-foo'); $this->assertIdentical(Set::merge($a, $b), array('foo' => 'no-bar', 'bar' => 'no-foo')); $a = array('users' => array('bob', 'jim')); $b = array('users' => array('lisa', 'tina')); $this->assertIdentical( Set::merge($a, $b), array('users' => array('bob', 'jim', 'lisa', 'tina')) ); $a = array('users' => array('jim', 'bob')); $b = array('users' => 'none'); $this->assertIdentical(Set::merge($a, $b), array('users' => 'none')); $a = array('users' => array('lisa' => array('id' => 5, 'pw' => 'secret')), 'lithium'); $b = array('users' => array('lisa' => array('pw' => 'new-pass', 'age' => 23)), 'ice-cream'); $this->assertIdentical( Set::merge($a, $b), array( 'users' => array('lisa' => array('id' => 5, 'pw' => 'new-pass', 'age' => 23)), 'lithium', 'ice-cream' ) ); $c = array( 'users' => array( 'lisa' => array('pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog') ), 'chocolate' ); $expected = array( 'users' => array( 'lisa' => array( 'id' => 5, 'pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog' ) ), 'lithium', 'ice-cream', 'chocolate' ); $this->assertIdentical($expected, Set::merge(Set::merge($a, $b), $c)); $this->assertIdentical($expected, Set::merge(Set::merge($a, $b), Set::merge(array(), $c))); $result = Set::merge($a, Set::merge($b, $c)); $this->assertIdentical($expected, $result); $a = array('Tree', 'CounterCache', 'Upload' => array( 'folder' => 'products', 'fields' => array( 'image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id' ) )); $b = array( 'Cacheable' => array('enabled' => false), 'Limit', 'Bindable', 'Validator', 'Transactional' ); $expected = array('Tree', 'CounterCache', 'Upload' => array( 'folder' => 'products', 'fields' => array( 'image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id' )), 'Cacheable' => array('enabled' => false), 'Limit', 'Bindable', 'Validator', 'Transactional' ); $this->assertIdentical(Set::merge($a, $b), $expected); $expected = array('Tree' => null, 'CounterCache' => null, 'Upload' => array( 'folder' => 'products', 'fields' => array( 'image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id' )), 'Cacheable' => array('enabled' => false), 'Limit' => null, 'Bindable' => null, 'Validator' => null, 'Transactional' => null ); $this->assertIdentical(Set::normalize(Set::merge($a, $b)), $expected); }
/** * Initialize request object, pulling request data from superglobals. * * Defines an artificial `'PLATFORM'` environment variable as either * `'IIS'`, `'CGI'` or `null` to allow checking for the SAPI in a * normalized way. * * @return void */ protected function _init() { parent::_init(); $mobile = array('iPhone', 'MIDP', 'AvantGo', 'BlackBerry', 'J2ME', 'Opera Mini', 'DoCoMo', 'NetFront', 'Nokia', 'PalmOS', 'PalmSource', 'portalmmm', 'Plucker', 'ReqwirelessWeb', 'iPod', 'SonyEricsson', 'Symbian', 'UP\\.Browser', 'Windows CE', 'Xiino', 'Android'); if (!empty($this->_config['detectors']['mobile'][1])) { $mobile = array_merge($mobile, (array) $this->_config['detectors']['mobile'][1]); } $this->_detectors['mobile'][1] = $mobile; $defaults = array('REQUEST_METHOD' => 'GET', 'CONTENT_TYPE' => 'text/html'); $this->_env += (array) $_SERVER + (array) $_ENV + $defaults; $envs = array('isapi' => 'IIS', 'cgi' => 'CGI', 'cgi-fcgi' => 'CGI'); $this->_env['PLATFORM'] = isset($envs[PHP_SAPI]) ? $envs[PHP_SAPI] : null; $this->_base = $this->_base(); $this->url = $this->_url(); if (!empty($this->_config['query'])) { $this->query = $this->_config['query']; } if (isset($_GET)) { $this->query += $_GET; } if (!empty($this->_config['data'])) { $this->data = $this->_config['data']; } if (isset($_POST)) { $this->data += $_POST; } if (isset($this->data['_method'])) { $this->_env['HTTP_X_HTTP_METHOD_OVERRIDE'] = strtoupper($this->data['_method']); unset($this->data['_method']); } if (!empty($this->_env['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $this->_env['REQUEST_METHOD'] = $this->_env['HTTP_X_HTTP_METHOD_OVERRIDE']; } $type = $this->type($this->_env['CONTENT_TYPE']); $this->method = $method = strtoupper($this->_env['REQUEST_METHOD']); if (!$this->data && ($method == 'POST' || $method == 'PUT')) { if ($type !== 'html') { $this->_stream = $this->_stream ?: fopen('php://input', 'r'); $media = $this->_classes['media']; $this->data = (array) $media::decode($type, stream_get_contents($this->_stream)); fclose($this->_stream); } } $this->data = Set::merge((array) $this->data, $this->_parseFiles()); }
/** * Instantiates a new record or document object, initialized with any data passed in. For * example: * * {{{ * $post = Posts::create(array("title" => "New post")); * echo $post->title; // echoes "New post" * $success = $post->save(); * }}} * * Note that while this method creates a new object, there is no effect on the database until * the `save()` method is called. * * In addition, this method can be used to simulate loading a pre-existing object from the * database, without actually querying the database: * * {{{ * $post = Posts::create(array("id" => $id, "moreData" => "foo"), array("exists" => true)); * $post->title = "New title"; * $success = $post->save(); * }}} * * This will create an update query against the object with an ID matching `$id`. Also note that * only the `title` field will be updated. * * @param array $data Any data that this object should be populated with initially. * @param array $options Options to be passed to item. * @return object Returns a new, _un-saved_ record or document object. In addition to the values * passed to `$data`, the object will also contain any values assigned to the * `'default'` key of each field defined in `$_schema`. * @filter */ public static function create(array $data = array(), array $options = array()) { $self = static::_object(); $params = compact('data', 'options'); return static::_filter(__FUNCTION__, $params, function ($self, $params) { $data = $params['data']; $options = $params['options']; $defaults = array(); foreach ((array) $self::schema() as $field => $config) { if (isset($config['default'])) { $defaults[$field] = $config['default']; } } $data = Set::merge(Set::expand($defaults), $data); return $self::connection()->item($self, $data, $options); }); }
protected static function _appendObject($changes, $path, $key, $object) { $options = array('finalize' => false); if ($object->exists()) { return Set::merge($changes, static::_update($object->export())); } $changes['update']["{$path}{$key}"] = static::_create($object->export(), $options); return $changes; }
/** * Adds an Access rule. This works much like the Validator class. * All rules should be anonymous functions and will be passed * $request, $resource, and $options which will contain the entire * rule array which contains its own name plus other data that * could be used to determine access. * * @param string $name The rule name. * @param function $rule The closure for the rule, which has to return true or false. */ public static function add($name, $rule = null) { if (!is_array($name)) { $name = array($name => $rule); } self::$_rules = Set::merge(self::$_rules, $name); }
/** * The manage method serves as both an index listing of all galleries as well * as a management tool for items within the gallery. * * If no $id is passed, then an indexed listing of galleries (with links) will appear. * Clicking on one of these listed galleries will then return to this method with * and $id value present. * * When an $id is present, the user will be able to add existing gallery items to * the gallery as well as upload new gallery items to be associated with the gallery. * * @param string $id The Page id for the gallery * @return */ public function manage($id = null) { // TODO: Use Minerva's access system (li3_access) // Bigger todo: update li3_acess (Nate's changes) and redo Minerva's access system completely. $user = Auth::check('minerva_user'); if ($user['role'] != 'administrator' && $user['role'] != 'content_editor') { $this->redirect('/'); return; } if (empty($id)) { // Default options for pagination, merge with URL parameters $defaults = array('page' => 1, 'limit' => 10, 'order' => 'created.desc'); $params = Set::merge($defaults, $this->request->params); if (isset($params['page']) && $params['page'] == 0) { $params['page'] = 1; } list($limit, $page, $order) = array($params['limit'], $params['page'], $params['order']); // Never allow a limit of 0 $limit = $limit < 0 ? 1 : $limit; // Only pull back minerva_gallery pages $conditions = array('document_type' => 'minerva_gallery'); // For search queries if (isset($this->request->query['q']) && !empty($this->request->query['q'])) { $search_schema = Page::searchSchema(); $search_conditions = array(); // For each searchable field, adjust the conditions to include a regex foreach ($search_schema as $k => $v) { $search_regex = new \MongoRegex('/' . $this->request->query['q'] . '/i'); $conditions['$or'][] = array($k => $search_regex); } } // Get the documents and the total $documents = Page::find('all', array('conditions' => $conditions, 'limit' => (int) $limit, 'offset' => ((int) $page - 1) * (int) $limit, 'order' => $params['order'])); $total = Page::find('count', array('conditions' => $conditions)); $page_number = (int) $page; $total_pages = (int) $limit > 0 ? ceil($total / $limit) : 0; // Use the manage_index template in this case $this->_render['template'] = 'manage_index'; // Set data for the view template $this->set(compact('documents', 'limit', 'page_number', 'total_pages', 'total')); } else { // Only pull the latest 30 gallery items from the entire system... // Because it's reasonable. There could be thousands of items and paging // through is an option, but not practical for the design and user experience. // 30 of the latest is enough and the user can make a search to find what // they are after. The point of this listing of items is to allow the user to // associate an existing item in the system with the current gallery. // It's not going to be as common as adding brand new items instead. // Well, unless the user really goes back to share items across multiple // galleries on a regular basis...I don't think it's common, but possible. // So showing 30 plus a search is plenty. $conditions = array('published' => true, '_galleries' => array('$nin' => array($id))); // For search queries for items if (isset($this->request->query['q']) && !empty($this->request->query['q'])) { $search_schema = Item::searchSchema(); $search_conditions = array(); // For each searchable field, adjust the conditions to include a regex foreach ($search_schema as $k => $v) { $search_regex = new \MongoRegex('/' . $this->request->query['q'] . '/i'); $conditions['$or'][] = array($k => $search_regex); } } // Find the unassociated gallery items $items = Item::find('all', array('conditions' => $conditions, 'limit' => 30, 'order' => array('created' => 'desc'))); // Find all items for the current gallery $gallery_items = Item::find('all', array('conditions' => array('_galleries' => $id))); // Find the gallery document itself $document = Page::find('first', array('conditions' => array('_id' => $id))); // Order those gallery items based on the gallery document's gallery_item_order field (if set) if (isset($document->gallery_item_order) && !empty($document->gallery_item_order)) { // This sort() method is the awesome. $ordering = $document->gallery_item_order->data(); // data() must be called so that the iterator loads up all the documents... // Something that has to be fixed I guess. Then data() doesn't need to be called. $gallery_items->data(); $gallery_items->sort(function ($a, $b) use($ordering) { if ($a['_id'] == $b['_id']) { return strcmp($a['_id'], $b['_id']); } $cmpa = array_search($a['_id'], $ordering); $cmpb = array_search($b['_id'], $ordering); return $cmpa > $cmpb ? 1 : -1; }); } $this->set(compact('document', 'items', 'gallery_items')); } }
/** * Strategies used to query related objects, indexed by key. */ protected function _strategies() { return array(static::LINK_EMBEDDED => function ($object, $relationship) { $fieldName = $relationship->fieldName(); return $object->{$fieldName}; }, static::LINK_CONTAINED => function ($object, $relationship) { $isArray = $relationship->type() === "hasMany"; return $isArray ? $object->parent()->parent() : $object->parent(); }, static::LINK_KEY => function ($object, $relationship, $options) { $model = $relationship->to(); if (!($query = $relationship->query($object))) { return; } $method = $relationship->type() === "hasMany" ? 'all' : 'first'; return $model::$method(Set::merge((array) $query, (array) $options)); }, static::LINK_KEY_LIST => function ($object, $relationship, $options) { $model = $relationship->to(); $query = $relationship->query($object); return $model::all(Set::merge($query, $options)); }); }
/** * Maps a type name to a particular content-type (or multiple types) with a set of options, or * retrieves information about a type that has been defined. * * Examples: * {{{ embed:lithium\tests\cases\net\http\MediaTest::testMediaTypes(1-2) }}} * * {{{ embed:lithium\tests\cases\net\http\MediaTest::testMediaTypes(19-23) }}} * * {{{ embed:lithium\tests\cases\net\http\MediaTest::testMediaTypes(43-44) }}} * * Alternatively, can be used to detect the type name of a registered content type: * {{{ * Media::type('application/json'); // returns 'json' * Media::type('application/javascript'); // returns 'javascript' * Media::type('text/javascript'); // also returns 'javascript' * * Media::type('text/html'); // returns 'html' * Media::type('application/xhtml+xml'); // also returns 'html' * }}} * * #### Content negotiation * * When creating custom media types, specifying which content-type(s) to match isn't always * enough. For example, if you wish to serve a different set of templates to mobile web * browsers, you'd still want those templates served as HTML. You might add something like this: * * {{{ * Media::type('mobile', array('application/xhtml+xml', 'text/html')); * }}} * * However, this would cause _all_ requests for HTML content to be interpreted as * `'mobile'`-type requests. Instead, we can use _content negotiation_ to granularly specify how * to match a particular type. Content negotiation is the process of examining the HTTP headers * provided in the request (including the content-types listed in the `Accept` header, and * optionally other things as well, like the `Accept-Language` or `User-Agent` headers), in * order to produce the best representation of the requested resource for the client; in other * words, the resource that most closely matches what the client is asking for. * * Content negotiation with media types is made possible through the `'conditions'` key of the * `$options` parameter, which contains an array of assertions made against the `Request` * object. Each assertion (array key) can be one of three different things: * * - `'type'` _boolean_: In the default routing, some routes have `{:type}` keys, which are * designed to match file extensions in URLs. These values act as overrides for the HTTP * `Accept` header, allowing different formats to be served with the same content type. For * example, if you're serving [ JSONP](http://en.wikipedia.org/wiki/JSON#JSONP), you'll want * to serve it with the same content-type as JavaScript (since it is JavaScript), but you * probably won't want to use the same template(s) or other settings. Therefore, when serving * JSONP content, you can specify that the extension defined in the type must be present in * the URL: * {{{ * Media::type('jsonp', array('text/html'), array( * // template settings... * 'conditions' => array('type' => true) * )); * }}} * Then, JSONP content will only ever be served when the request URL ends in `.jsonp`. * * - `'<prefix>:<key>'` _string_: This type of assertion can be used to match against arbitrary * information in the request, including headers (i.e. `'http:user_agent'`), environment * variables (i.e. `'env:home'`), GET and POST data (i.e. `'query:foo'` or `'data:foo'`, * respectively), and the HTTP method (`'http:method'`) of the request. For more information * on possible keys, see `lithium\action\Request::get()`. * * - `'<detector>'` _boolean_: Uses detector checks added to the `Request` object to make * boolean assertions against the request. For example, if a detector called `'iPhone'` is * attached, you can add `'iPhone' => true` to the `'conditions'` array in order to filter for * iPhone requests only. See `lithium\action\Request::detect()` for more information on adding * detectors. * * @see lithium\net\http\Media::$_types * @see lithium\net\http\Media::$_handlers * @see lithium\net\http\Media::negotiate() * @see lithium\action\Request::get() * @see lithium\action\Request::is() * @see lithium\action\Request::detect() * @see lithium\util\String::insert() * @param string $type A file-extension-style type name, i.e. `'txt'`, `'js'`, or `'atom'`. * Alternatively, a mapped content type, i.e. `'text/html'`, * `'application/atom+xml'`, etc.; in which case, the matching type name (i.e. * '`html'` or `'atom'`) will be returned. * @param mixed $content Optional. A string or array containing the content-type(s) that * `$type` should map to. If `$type` is an array of content-types, the first one listed * should be the "primary" type, and will be used as the `Content-type` header of any * `Response` objects served through this type. * @param array $options Optional. The handling options for this media type. Possible keys are: * - `'view'` _string_: Specifies the view class to use when rendering this content. * Note that no `'view'` class is specified by default. If you want to * render templates using Lithium's default view class, use * `'lithium\template\View'` * - `'decode'` _mixed_: A (string) function name or (object) closure that handles * decoding or unserializing content from this format. * - `'encode'` _mixed_: A (string) function name or (object) closure that handles * encoding or serializing content into this format. * - `'cast'` _boolean_: Used with `'encode'`. If `true`, all data passed into the * specified encode function is first cast to array structures. * - `'paths'` _array_: Optional key/value pairs mapping paths for * `'template'`, `'layout'`, and `'element'` template files. Any keys ommitted * will use the default path. The values should be `String::insert()`-style * paths or an array of `String::insert()`-style paths. If it is an array, * each path will be tried in the order specified until a template is found. * This is useful for allowing custom templates while falling back on * default templates if no custom template was found. If you want to * render templates without a layout, use a `false` value for `'layout'`. * - `'conditions'` _array_: Optional key/value pairs used as assertions in content * negotiation. See the above section on **Content Negotiation**. * @return mixed If `$content` and `$options` are empty, returns an array with `'content'` and * `'options'` keys, where `'content'` is the content-type(s) that correspond to * `$type` (can be a string or array, if multiple content-types are available), and * `'options'` is the array of options which define how this content-type should be * handled. If `$content` or `$options` are non-empty, returns `null`. */ public static function type($type, $content = null, array $options = array()) { $defaults = array('view' => false, 'paths' => array('template' => '{:library}/views/{:controller}/{:template}.{:type}.php', 'layout' => '{:library}/views/layouts/{:layout}.{:type}.php', 'element' => '{:library}/views/elements/{:template}.{:type}.php'), 'encode' => false, 'decode' => false, 'cast' => true, 'conditions' => array()); if ($content === false) { unset(static::$_types[$type], static::$_handlers[$type]); } if (!$content && !$options) { if (!($content = static::_types($type))) { return; } if (strpos($type, '/')) { return $content; } if (is_array($content) && isset($content['alias'])) { return static::type($content['alias']); } return compact('content') + array('options' => static::_handlers($type)); } if ($content) { static::$_types[$type] = (array) $content; } static::$_handlers[$type] = $options ? Set::merge($defaults, $options) : array(); }
/** * Instantiates a new record or document object, initialized with any data passed in. For * example: * * ``` * $post = Posts::create(array('title' => 'New post')); * echo $post->title; // echoes 'New post' * $success = $post->save(); * ``` * * Note that while this method creates a new object, there is no effect on the database until * the `save()` method is called. * * In addition, this method can be used to simulate loading a pre-existing object from the * database, without actually querying the database: * * ``` * $post = Posts::create(array('id' => $id, 'moreData' => 'foo'), array('exists' => true)); * $post->title = 'New title'; * $success = $post->save(); * ``` * * This will create an update query against the object with an ID matching `$id`. Also note that * only the `title` field will be updated. * * @param array $data Any data that this object should be populated with initially. * @param array $options Options to be passed to item. * @return object Returns a new, _un-saved_ record or document object. In addition to the values * passed to `$data`, the object will also contain any values assigned to the * `'default'` key of each field defined in `$_schema`. * @filter */ public static function create(array $data = array(), array $options = array()) { $defaults = array('defaults' => true, 'class' => 'entity'); $options += $defaults; return static::_filter(__FUNCTION__, compact('data', 'options'), function ($self, $params) { $class = $params['options']['class']; unset($params['options']['class']); if ($class === 'entity' && $params['options']['defaults']) { $data = Set::merge(Set::expand($self::schema()->defaults()), $params['data']); } else { $data = $params['data']; } $options = array('model' => $self, 'data' => $data) + $params['options']; return $self::invokeMethod('_instance', array($class, $options)); }); }
/** * Creates a new instance of the Pagination class. * * Options can be configured by passing them through the $config array. * * @see app\extensions\helper\Pagination::_init() * @param array $config An array of options that can be configured during construction. * They allow for the easy alteration of text used on prev/next links. * Valid options are: * * @return object An instance of the Pagination class being constructed. */ public function __construct(array $config = []) { parent::__construct(Set::merge(static::$_defaults, $config)); }
/** * allows merging of data from context with given data * * @param array $data additional data to be put into view * @param array $options additional options * - `merge`: set to false to disable merging view data with context * @return array */ private function _data($data = array(), array $options = array()) { $defaults = array('merge' => true); $options += $defaults; if ($options['merge'] === false) { return $data; } if (!empty($data)) { return Set::merge($this->_context->data(), $data); } return $this->_context->data(); }
<?php use lithium\core\Libraries; use lithium\core\ConfigException; use lithium\template\View; use lithium\net\http\Media; use lithium\util\Set; // Define path to plugin and other constants defined('SMARTY_VERSION') or define('SMARTY_VERSION', '3.1.10'); // Allows us to test different versions without breaking things defined('LI3_SMARTY_PATH') or define('LI3_SMARTY_PATH', dirname(__DIR__)); defined('LI3_SMARTY_LIB') or define('LI3_SMARTY_LIB', dirname(__DIR__) . "/libraries/Smarty/" . SMARTY_VERSION . "/libs/"); Libraries::add('Smarty', array("path" => LI3_SMARTY_LIB, "bootstrap" => "Smarty.class.php")); $defaults = array('view' => '\\lithium\\template\\View', 'renderer' => '\\li3_smarty\\template\\view\\adapter\\Smarty', 'paths' => array('template' => array(LITHIUM_APP_PATH . '/views/{:controller}/{:template}.{:type}.tpl', '{:library}/views/{:controller}/{:template}.{:type}.tpl'), 'element' => array(LITHIUM_APP_PATH . '/views/elements/{:template}.html.tpl', '{:library}/views/elements/{:template}.html.tpl'), 'layout' => false), 'compile_dir' => LITHIUM_APP_PATH . '/resources/templates_c', 'cache_dir' => LITHIUM_APP_PATH . '/resources/cache', 'template_dir' => array(LITHIUM_APP_PATH . "/views", LITHIUM_APP_PATH . "/views/pages"), 'plugin_dir' => array(LI3_SMARTY_PATH . "/plugins", LITHIUM_APP_PATH . "/extensions/plugins")); $keys = array_intersect_key($config, $defaults); foreach ($keys as $key => $val) { if (is_array($defaults[$key])) { $defaults[$key] = Set::merge($defaults[$key], $config[$key]); } else { $defaults[$key] = $val; } } /** * Map to the new renderer */ Media::type('default', null, $defaults);
/** * Constructor. Instantiates the `Memcached` object, adds appropriate servers to the pool, * and configures any optional settings passed (see the `_init()` method). When adding * servers, the following formats are valid for the `'host'` key: * * - `'127.0.0.1'` * Configure the adapter to connect to one Memcache server on the default port. * * - `'127.0.0.1:11222'` * Configure the adapter to connect to one Memcache server on a custom port. * * - `array('167.221.1.5:11222' => 200, '167.221.1.6')` * Connect to one server on a custom port with a high selection weight, and * a second server on the default port with the default selection weight. * * @see lithium\storage\Cache::config() * @param array $config Configuration for this cache adapter. These settings are queryable * through `Cache::config('name')`. The available options are as follows: * - `'scope'` _string_: Scope which will prefix keys; per default not set. * - `'expiry'` _mixed_: The default expiration time for cache values, if no value * is otherwise set. Can be either a `strtotime()` compatible tring or TTL in * seconds. To indicate items should not expire use `Cache::PERSIST`. Defaults * to `+1 hour`. * - `'host'` _mixed_: Specifies one or more Memcache servers to connect to, with * optional server selection weights. See above for example values. * @return void */ public function __construct(array $config = array()) { $defaults = array('scope' => null, 'expiry' => '+1 hour', 'host' => '127.0.0.1'); parent::__construct(Set::merge($defaults, $config)); }
/** * Creates, modifies or switches to an existing environment configuration. To create a new * configuration, or to update an existing configuration, pass an environment name and an array * that defines its configuration: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(1-1) }}} * * You can then add to an existing configuration by calling the `set()` method again with the * same environment name: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(6-6) }}} * * The settings for the environment will then be the aggregate of all `set()` calls: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(7-7) }}} * * By passing an array to `$env`, you can assign the same configuration to multiple * environments: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetMultipleEnvironments(5-7) }}} * * The `set()` method can also be called to manually set which environment to operate in: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetAndGetCurrentEnvironment(5-5) }}} * * Finally, `set()` can accept a `Request` object, to automatically detect the correct * environment. * * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testEnvironmentDetection(9-10) }}} * * For more information on defining custom rules to automatically detect your application's * environment, see the documentation for `Environment::is()`. * * @see lithium\action\Request * @see lithium\core\Environment::is() * @param mixed $env The name(s) of the environment(s) you wish to create, update or switch to * (string/array), or a `Request` object or `$_SERVER` / `$_ENV` array used to * detect (and switch to) the application's current environment. * @param array $config If creating or updating a configuration, accepts an array of settings. * If the environment name specified in `$env` already exists, the values in * `$config` will be recursively merged with any pre-existing settings. * @return array If creating or updating a configuration, returns an array of the environment's * settings. If updating an existing configuration, this will be the newly-applied * configuration merged with the pre-existing values. If setting the environment * itself (i.e. `$config` is unspecified), returns `null`. */ public static function set($env, $config = null) { if ($config === null) { if (is_object($env) || is_array($env)) { static::$_current = static::_detector()->__invoke($env); } elseif (isset(static::$_configurations[$env])) { static::$_current = $env; } return; } if (is_array($env)) { foreach ($env as $name) { static::set($name, $config); } return; } $env = $env === true ? static::$_current : $env; $base = isset(static::$_configurations[$env]) ? static::$_configurations[$env] : array(); return static::$_configurations[$env] = Set::merge($base, $config); }
/** * Handles appending nested objects to document changesets. * * @param array $changes The full set of changes to be made to the database. * @param string $key The key of the field to append, which may be a dot-separated path if the * value is or is contained within a nested object. * @param mixed $value The value to append to the changeset. Can be a scalar value, array, a * nested object, or part of a nested object. * @param string $change The type of change, as to whether update/remove or rename etc. * @return array Returns the value of `$changes`, with any new changed values appended. */ protected static function _append($changes, $key, $value, $change) { $options = array('finalize' => false); if (!is_object($value) || !method_exists($value, 'export')) { $changes[$change][$key] = $change == 'update' ? $value : true; return $changes; } if ($value->exists()) { if ($change == 'update') { $export = $value->export(); $export['key'] = $key; return Set::merge($changes, static::_update($export)); } $children = static::_update($value->export()); if (!empty($children)) { return Set::merge($changes, $children); } $changes[$change][$key] = true; return $changes; } $changes['update'][$key] = static::_create($value->export(), $options); return $changes; }
/** * Initialize request object * * Defines an artificial `'PLATFORM'` environment variable as either `'IIS'`, `'CGI'` or `null` * to allow checking for the SAPI in a normalized way. */ protected function _init() { parent::_init(); $mobile = array('iPhone', 'MIDP', 'AvantGo', 'BlackBerry', 'J2ME', 'Opera Mini', 'DoCoMo', 'NetFront', 'Nokia', 'PalmOS', 'PalmSource', 'portalmmm', 'Plucker', 'ReqwirelessWeb', 'iPod', 'SonyEricsson', 'Symbian', 'UP\\.Browser', 'Windows CE', 'Xiino', 'Android'); if (!empty($this->_config['detectors']['mobile'][1])) { $mobile = array_merge($mobile, (array) $this->_config['detectors']['mobile'][1]); } $this->_detectors['mobile'][1] = $mobile; $this->data = $this->_config['data']; if (isset($this->data['_method'])) { $this->_computed['HTTP_X_HTTP_METHOD_OVERRIDE'] = strtoupper($this->data['_method']); unset($this->data['_method']); } $type = $this->type($this->_config['type'] ?: $this->env('CONTENT_TYPE')); $this->method = $method = strtoupper($this->env('REQUEST_METHOD')); $hasBody = in_array($method, array('POST', 'PUT', 'PATCH')); if (!$this->body && $hasBody && $type !== 'html') { $this->_stream = $this->_stream ?: fopen('php://input', 'r'); $this->body = stream_get_contents($this->_stream); fclose($this->_stream); } if (!$this->data && $this->body) { $this->data = $this->body(null, array('decode' => true, 'encode' => false)); } $this->body = $this->data; $this->data = Set::merge((array) $this->data, $this->_parseFiles()); }
/** * Allows you to configure a default set of options which are included on a per-method basis, * and configure method template overrides. * * To force all `<label />` elements to have a default `class` attribute value of `"foo"`, * simply do the following: * * {{{ * $this->form->config(array('label' => array('class' => 'foo'))); * }}} * * @param array $config An associative array where the keys are `Form` method names, and the * values are arrays of configuration options to be included in the `$options` * parameter of each method specified. * @return array Returns an array containing the currently set per-method configurations, and * an array of the currently set template overrides (in the `'templates'` array key). * @see lithium\template\helper\Form::$_templateMap */ public function config($config = array()) { if (empty($config)) { return array('templates' => $this->_templateMap) + array_intersect_key($this->_config, array('base' => '', 'text' => '', 'textarea' => '')); } if (isset($config['templates'])) { $this->_templateMap = $config['templates'] + $this->_templateMap; unset($config['templates']); } return ($this->_config = Set::merge($this->_config, $config)) + array('templates' => $this->_templateMap); }
/** * Executes a query against a model based on a resource mapping configuration. * * @param array $options The query configuration for a single resource parameter. Available * options are: * - `'name'` _string_: The name of the resource parameter being queried. * - `'binding'` _string_: The fully-namespaced class name of the model to query * for the resource parameter. * - `'call'` _mixed_: A string indicating the name of the method to call (i.e. * `'find'` or `'first'`), or an array where the first item is the name of the * method to call, and other available keys are `'conditions'` and `'order'`. * See the `find()`/`first()` and `sort()` methods of `data\Collection` for * information on valid values. Defaults to `'first'`. * - `'required'` _boolean_: Boolean indicating whether this resource parameter is * required. If `true`, and returning a single object, that object must * exist. If `true` and returning a collection, that collection must contain * one or more items. Defaults to `true`. * - `'data'` _array_: The keyed array of other resources that have already been * queried and are to be returned as the final result of `all()`. Primarily used * in conjunction with the `'in'` option. * - `'in'` _string_: A key representing a field in an existing resource in * `'data'` that has already been queried. The typical use case is a * @return object Returns the result of a model query, usually either an `Entity` or * `Collection`. * @filter */ public static function get($request, array $options = array()) { $defaults = array('name' => null, 'binding' => null, 'call' => 'first', 'required' => true, 'data' => array(), 'params' => null, 'in' => null); $options += $defaults; $func = __FUNCTION__; return static::_filter($func, compact('request', 'options'), function ($self, $params) { $options = $params['options']; $request = $params['request']; if ($options['in']) { return $self::queryCollection($options, $query); } $query = $self::mapQuery($options, $request); $options['call'] = Set::merge($query, (array) $options['call']); if (!$options['call']['conditions'] && $options['params']) { return; } $options['binding'] = call_user_func($self::handlers('binding'), $request, $options); $func = $self::handlers($options['name']) ?: $self::handlers('default'); if (is_array($func)) { $key = isset($options['call'][0]) ? $options['call'][0] : 'all'; $func = is_string($key) && isset($func[$key]) ? $func[$key] : $func[0]; } return $func($request, $options); }); }
/** * Adds an Access rule. This works much like the Validator class. * All rules should be anonymous functions and will be passed * $user, $request, and $options which will contain the entire * rule array which contains its own name plus other data that * could be used to determine access. * * @param string $name The rule name. * @param function $rule The closure for the rule, which has to return true or false. */ public function add($name, $rule = null) { $this->_rules = Set::merge($this->_rules, is_array($name) ? $name : array($name => $rule)); }
/** * Creates, modifies or switches to an existing environment configuration. To create a new * configuration, or to update an existing configuration, pass an environment name and an array * that defines its configuration: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(1-1) }}} * * You can then add to an existing configuration by calling the `set()` method again with the * same environment name: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(6-6) }}} * * The settings for the environment will then be the aggregate of all `set()` calls: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(7-7) }}} * * The `set()` method can also be called to manually set which environment to operate in: * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetAndGetCurrentEnvironment(5-5) }}} * * Finally, `set()` can accept a `Request` object, or the `$_SERVER` or `$_ENV` superglobals, to * automatically detect the correct environment. * * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testEnvironmentDetection(9-10) }}} * * For more information on defining custom rules to automatically detect your application's * environment, see the documentation for `Environment::is()`. * * @see lithium\http\Request * @see lithium\core\Environment::is() * @param mixed $env The name of the environment you wish to create, update or switch to * (string), or a `Request` object or `$_SERVER` / `$_ENV` array used to detect * (and switch to) the application's current environment. * @param array $config If creating or updating a configuration, accepts an array of settings. * If the environment name specified in `$env` already exists, the values in * `$config` will be recursively merged with any pre-existing settings. * @return array If creating or updating a configuration, returns an array of the environment's * settings. If updating an existing configuration, this will be the newly-applied * configuration merged with the pre-existing values. If setting the environment * itself (i.e. `$config` is unspecified), returns `null`. */ public static function set($env, $config = null) { if (is_null($config)) { switch (true) { case is_object($env) || is_array($env): static::$_current = static::_detector()->__invoke($env); break; case isset(static::$_configurations[$env]): static::$_current = $env; break; } return; } $env = $env === true ? static::$_current : $env; $base = isset(static::$_configurations[$env]) ? static::$_configurations[$env] : array(); return static::$_configurations[$env] = Set::merge($base, $config); }
/** * Adds to or replaces built-in validation rules specified in `Validator::$_rules`. Any new * validation rules created are automatically callable as validation methods. * * For example: * {{{ * Validator::add('zeroToNine', '/^[0-9]$/'); * $isValid = Validator::isZeroToNine("5"); // true * $isValid = Validator::isZeroToNine("20"); // false * }}} * * Alternatively, the first parameter may be an array of rules expressed as key/value pairs, * as in the following: * {{{ * Validator::add(array( * 'zeroToNine' => '/^[0-9]$/', * 'tenToNineteen' => '/^1[0-9]$/', * )); * }}} * * In addition to regular expressions, validation rules can also be defined as full anonymous * functions: * {{{ * use app\models\Account; * * Validator::add('accountActive', function($value) { * $value = is_int($value) ? Account::find($value) : $value; * return (boolean) $value->is_active; * }); * * $testAccount = Account::create(array('is_active' => false)); * Validator::isAccountActive($testAccount); // returns false * }}} * * These functions can take up to 3 parameters: * - `$value` _mixed_: This is the actual value to be validated (as in the above example). * - `$format` _string_: Often, validation rules come in multiple "formats", for example: * postal codes, which vary by country or region. Defining multiple formats allows you to * retian flexibility in how you validate data. In cases where a user's country of origin * is known, the appropriate validation rule may be selected. In cases where it is not * known, the value of `$format` may be `'any'`, which should pass if any format matches. * In cases where validation rule formats are not mutually exclusive, the value may be * `'all'`, in which case all must match. * - `$options` _array_: This parameter allows a validation rule to implement custom * options. * * @see lithium\util\Validator::$_rules * @param mixed $name The name of the validation rule (string), or an array of key/value pairs * of names and rules. * @param string $rule If $name is a string, this should be a string regular expression, or a * closure that returns a boolean indicating success. Should be left blank if * `$name` is an array. * @param array $options The default options for validating this rule. An option which applies * to all regular expression rules is `'contains'` which, if set to true, allows * validated values to simply _contain_ a match to a rule, rather than exactly * matching it in whole. * @return void */ public static function add($name, $rule = null, array $options = array()) { if (!is_array($name)) { $name = array($name => $rule); } static::$_rules = Set::merge(static::$_rules, $name); if (!empty($options)) { $options = array_combine(array_keys($name), array_fill(0, count($name), $options)); static::$_options = Set::merge(static::$_options, $options); } }
/** * Generic index() action that returns a list of paginated documents. * The trick here is that $this->calling_class and $this->calling_method will hold a string * reference for which extended class called this index() method. We need that in order to * get the proper records and access. */ public function index() { // get the "_type" ... page_type, user_type, or block_type $controller = $this->request->params['controller']; $controller_pieces = explode('.', $controller); if(count($controller_pieces) > 1) { $controller = end($controller_pieces); } $model = Inflector::classify(Inflector::singularize($controller)); $modelClass = 'minerva\models\\'.$model; $x_type = strtolower($model) . '_type'; // or set it to "all" if there wasn't a param passed $type = ((isset($this->request->params[$x_type])) && (in_array($x_type, $this->library_fields))) ? $this->request->params[$x_type]:'all'; // Default options for pagination, merge with URL parameters $defaults = array('page' => 1, 'limit' => 10, 'order' => 'created.desc'); $params = Set::merge($defaults, $this->request->params); if((isset($params['page'])) && ($params['page'] == 0)) { $params['page'] = 1; } list($limit, $page, $order) = array($params['limit'], $params['page'], $params['order']); // If there's a page/user/block_type passed, add it to the conditions, 'all' will show all pages. if($type != 'all') { $conditions = array($type => $this->request->params[$type]); } else { $conditions = array(); } // If a search query was provided, search all "searchable" fields (any field in the model's $search_schema property) // NOTE: the values within this array for "search" include things like "weight" etc. and are not yet fully implemented...But will become more robust and useful. // Possible integration with Solr/Lucene, etc. if((isset($this->request->query['q'])) && (!empty($this->request->query['q']))) { $search_schema = $modelClass::search_schema(); // If the "*_type" is set to "all" then we want to get all the model type's schemas, merge them into $schema if($type == 'all') { foreach(Util::list_types($model, array('exclude_minerva' => true)) as $library) { $extendedModelClass = 'minerva\libraries\\' . $library; $search_schema += $extendedModelClass::search_schema(); } } $search_conditions = array(); // For each searchable field, adjust the conditions to include a regex foreach($search_schema as $k => $v) { // TODO: possibly factor in the weighting later. also maybe note the "type" to ensure our regex is going to work or if it has to be adjusted (string data types, etc.) //var_dump($k); $search_regex = new \MongoRegex('/' . $this->request->query['q'] . '/i'); $conditions['$or'][] = array($k => $search_regex); } } // Get the documents and the total $documents = array(); if((int)$params['limit'] > 0) { $documents = $this->getDocument(array( 'action' => $this->calling_method, 'request' => $this->request, 'find_type' => 'all', 'conditions' => $conditions, 'limit' => (int)$params['limit'], 'offset' => ($params['page'] - 1) * $limit, 'order' => $params['order'] )); } // Get some handy numbers $total = $modelClass::find('count', array( 'conditions' => $conditions )); $page_number = $params['page']; $total_pages = ((int)$params['limit'] > 0) ? ceil($total / $params['limit']):0; // Set data for the view template $this->set(compact('documents', 'limit', 'page_number', 'total_pages', 'total')); }