/** * Lazy bind a relation. * * @param string $name The name of the relation (i.e. field name where it will be binded). * @param array $config The configuration that should be specified in the relationship. * See the `chaos\Relationship` class for more information. * @return boolean * @throws Exception Throws a `ChaosException` if the config has no type option defined. */ public function bind($name, $config = []) { $relationship = $this->_classes['relationship']; $config += ['type' => 'entity', 'from' => $this->model(), 'to' => null, 'link' => $relationship::LINK_KEY]; $config['embedded'] = strncmp($config['link'], 'key', 3) !== 0; if (!isset($config['relation']) || !isset($this->_classes[$config['relation']])) { throw new ChaosException("Unexisting binding relation `{$config['relation']}` for `'{$name}'`."); } if (!$config['from']) { throw new ChaosException("Binding requires `'from'` option to be set."); } if (!$config['to']) { if ($config['relation'] !== 'hasManyThrough') { throw new ChaosException("Binding requires `'to'` option to be set."); } } elseif (($pos = strrpos('\\', $config['to'])) !== false) { $from = $config['from']; $config['to'] = substr($from, 0, $pos + 1) . $config['to']; } $config['array'] = !!preg_match('~Many~', $config['relation']); $config['type'] = $config['array'] ? 'set' : $config['type']; if ($config['relation'] === 'hasManyThrough') { if (!isset($config['through'])) { throw new ChaosException("Missing `'through'` relation name."); } $config += ['using' => $this->_conventions->apply('usingName', $name)]; $config['type'] = 'through'; } $this->_relations[$name] = $config; $this->_relationships[$name] = null; return true; }
/** * Constructs an object that represents a relationship between two model classes. * * @param array $config The relationship's configuration, which defines how the two models in * question are bound. The available options are: * - `'name'` _string_ : The field name used for accessing the related data. * For example, in the case of `Post` hasMany `Comment`, the name defaults to `'comments'`. * - `'keys'` _mixed_ : Mathing keys definition, where the key is the key in the originating model, * and the value is the key in the target model (i.e. `['fromId' => 'toId']`). * - `'from'` _string_ : The fully namespaced class name this relationship originates. * - `'to'` _string_ : The fully namespaced class name this relationship targets. * - `'link'` _string_ : A constant specifying how the object bound to the originating * model is linked to the object bound to the target model. For relational * databases, the only valid value is `LINK_KEY`, which means a foreign * key in one object matches another key (usually the primary key) in the other. * For document-oriented and other non-relational databases, different types of * linking, including key lists or even embedding. * - `'fields'` _mixed_ : An array of the subset of fields that should be selected * from the related object(s) by default. If set to `true` (the default), all * fields are selected. * - `'conventions'` _object_ : The naming conventions instance to use. */ public function __construct($config = []) { $defaults = ['name' => null, 'keys' => null, 'from' => null, 'to' => null, 'link' => static::LINK_KEY, 'fields' => true, 'conventions' => null]; $config += $defaults; foreach (['from', 'to'] as $value) { if (!$config[$value]) { throw new ChaosException("The relationship `'{$value}'` option can't be empty."); } } $this->_conventions = $config['conventions'] ?: new Conventions(); if (!$config['keys']) { $primaryKey = $this->_conventions->apply('primaryKey'); $config['keys'] = [$primaryKey => $this->_conventions->apply('foreignKey', $config['from'])]; } if (!$config['name']) { $config['name'] = $this->_conventions->apply('fieldName', $config['to']); } $this->_name = $config['name']; $this->_from = $config['from']; $this->_to = $config['to']; $this->_keys = $config['keys']; $this->_link = $config['link']; $this->_fields = $config['fields']; $pos = strrpos(static::class, '\\'); $this->_type = lcfirst(substr(static::class, $pos !== false ? $pos + 1 : 0)); }
/** * Configures the meta for use. * * @param array $config Possible options are: * - `'connection'` _object_ : The connection instance (defaults to `null`). * - `'source'` _string_ : The source name (defaults to `null`). * - `'model'` _string_ : The fully namespaced model class name (defaults to `null`). * - `'locked'` _boolean_: set the ability to dynamically add/remove fields (defaults to `false`). * - `'key'` _string_ : The primary key value (defaults to `id`). * - `'columns' _array_ : array of field definition where keys are field names and values are arrays * with the following keys. All properties are optionnal except the `'type'`: * - `'type'` _string_ : the type of the field. * - `'default'` _mixed_ : the default value (default to '`null`'). * - `'null'` _boolean_: allow null value (default to `'null'`). * - `'length'` _integer_: the length of the data (default to `'null'`). * - `'precision'` _integer_: the precision (for decimals) (default to `'null'`). * - `'use'` _string_ : the database type to override the associated type for * this type (default to `'null'`). * - `'serial'` _string_ : autoincremented field (default to `'null'`). * - `'primary'` _boolead_: primary key (default to `'null'`). * - `'unique'` _boolead_: unique key (default to `'null'`). * - `'reference'` _string_ : foreign key (default to `'null'`). * - `'meta'` _array_ : array of meta definitions for the schema. The definitions are related to * the datasource. For the MySQL adapter the following options are available: * - `'charset'` _string_: the charset value to use for the table. * - `'collate'` _string_: the collate value to use for the table. * - `'engine'` _stirng_: the engine value to use for the table. * - `'tablespace'` _string_: the tablespace value to use for the table. * - `'handlers'` _array_ : casting handlers. * - `'conventions'` _object_ : The naming conventions instance. * - `'classes'` _array_ : The class dependencies. */ public function __construct($config = []) { $defaults = ['connection' => null, 'source' => null, 'model' => Document::class, 'locked' => true, 'columns' => [], 'meta' => [], 'handlers' => [], 'conventions' => null, 'classes' => $this->_classes]; $config = Set::merge($defaults, $config); $this->_classes = $config['classes']; $this->_connection = $config['connection']; $this->_locked = $config['locked']; $this->_meta = $config['meta']; $this->_handlers = Set::merge($config['handlers'], $this->_handlers()); $this->_conventions = $config['conventions'] ?: new Conventions(); $config += ['key' => $this->_conventions->apply('key')]; $this->_columns = $config['columns']; $this->_source = $config['source']; $this->_model = $config['model']; $this->_key = $config['key']; foreach ($config['columns'] as $key => $value) { $this->_columns[$key] = $this->_initColumn($value); } if ($this->_connection) { $this->_formatters = $this->_connection->formatters(); } $handlers = $this->_handlers; $this->formatter('array', 'id', $handlers['array']['integer']); $this->formatter('array', 'serial', $handlers['array']['integer']); $this->formatter('array', 'integer', $handlers['array']['integer']); $this->formatter('array', 'float', $handlers['array']['float']); $this->formatter('array', 'decimal', $handlers['array']['string']); $this->formatter('array', 'date', $handlers['array']['date']); $this->formatter('array', 'datetime', $handlers['array']['datetime']); $this->formatter('array', 'boolean', $handlers['array']['boolean']); $this->formatter('array', 'null', $handlers['array']['null']); $this->formatter('array', '_default_', $handlers['array']['string']); $this->formatter('cast', 'integer', $handlers['cast']['integer']); $this->formatter('cast', 'float', $handlers['cast']['float']); $this->formatter('cast', 'decimal', $handlers['cast']['decimal']); $this->formatter('cast', 'date', $handlers['cast']['datetime']); $this->formatter('cast', 'datetime', $handlers['cast']['datetime']); $this->formatter('cast', 'boolean', $handlers['cast']['boolean']); $this->formatter('cast', 'null', $handlers['cast']['null']); $this->formatter('cast', 'string', $handlers['cast']['string']); if ($this->_connection) { $this->_formatters = Set::merge($this->_formatters, $this->_connection->formatters()); } }
/** * Loads the Channel module and runs its entries() method * * @access private * @return void */ private function _channel_entries() { // -------------------------------------- // Make sure the following params are set // -------------------------------------- $set_params = array('dynamic' => 'no', 'paginate' => 'bottom'); foreach ($set_params as $key => $val) { if (!ee()->TMPL->fetch_param($key)) { $this->params->apply($key, $val); } } // -------------------------------------- // Take care of related entries // -------------------------------------- if (version_compare(APP_VER, '2.6.0', '<')) { // We must do this, 'cause the template engine only does it for // channel:entries or search:search_results. The bastard. ee()->TMPL->tagdata = ee()->TMPL->assign_relationship_data(ee()->TMPL->tagdata); // Add related markers to single vars to trigger replacement foreach (ee()->TMPL->related_markers as $var) { ee()->TMPL->var_single[$var] = $var; } } // -------------------------------------- // Get channel module // -------------------------------------- $this->_log('Calling the channel module'); if (!class_exists('channel')) { require_once PATH_MOD . 'channel/mod.channel' . EXT; } // -------------------------------------- // Create new Channel instance // -------------------------------------- $channel = new Channel(); // -------------------------------------- // Let the Channel module do all the heavy lifting // -------------------------------------- return $channel->entries(); }