/** * Constructs the PostgreSQL adapter and sets the default port to 5432. * * @param array $config Configuration options for this class. Available options * defined by this class: * - `'host'` : _string_ The IP or machine name where PostgreSQL is running, * followed by a colon, followed by a port number or socket. * Defaults to `'localhost:5432'`. * - `'schema'` : _string_ The name of the database schema to use. Defaults to 'public' * - `'timezone'`: _stirng_ The database timezone. Defaults to `null`. */ public function __construct($config = []) { $defaults = ['host' => 'localhost:5432', 'schema' => 'public', 'timezone' => null, 'classes' => ['dialect' => 'sql\\dialect\\PostgreSql'], 'handlers' => ['cast' => ['boolean' => function ($value, $options = []) { return $value === 't'; }], 'datasource' => ['boolean' => function ($value, $options = []) { return $value ? 'true' : 'false'; }, 'array' => function ($data) { $data = (array) $data; $result = []; foreach ($data as $value) { if (is_array($value)) { $result[] = $this->_handlers['datasource']['array']($value); } else { $value = str_replace('"', '\\"', $value); if (!is_numeric($value)) { $value = '"' . $value . '"'; } $result[] = $value; } } return '{' . join(",", $result) . '}'; }]]]; $config = Set::merge($defaults, $config); parent::__construct($config + $defaults); $this->formatter('datasource', 'array', $this->_handlers['datasource']['array']); }
/** * Configures the schema class. * * @param array $config Possible options are: * - `'conventions'` _array_: Allow to override the default convention rules for generating * primary or foreign key as well as for table/collection names * from an entity class name. */ public function config($config = []) { $defaults = ['conventions' => ['source' => function ($class) { $basename = substr(strrchr($class, '\\'), 1); return Inflector::underscore($basename); }, 'primaryKey' => function () { return 'id'; }, 'foreignKey' => function ($class) { $pos = strrpos($class, '\\'); $basename = substr($class, $pos !== false ? $pos + 1 : 0); return Inflector::underscore(Inflector::singularize($basename)) . '_id'; }, 'fieldName' => function ($class) { $pos = strrpos($class, '\\'); $basename = substr($class, $pos !== false ? $pos + 1 : 0); return Inflector::underscore(Inflector::singularize($basename)); }, 'usingName' => function ($name) { return Inflector::singularize($name); }, 'getter' => function ($name) { return 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $name))); }, 'setter' => function ($name) { return 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $name))); }]]; $config = Set::merge($defaults, $config); $this->_conventions = $config['conventions']; }
/** * Constructor. * * @param array $config Possible options are: * - `'classes'` _array_ : The class dependencies. * - `'connection'` _object_ : The connection instance. */ public function __construct($config = []) { $defaults = ['classes' => ['schema' => 'chaos\\database\\Schema', 'model' => 'chaos\\Model'], 'connection' => null, 'fixtures' => []]; $config = Set::merge($defaults, $config); $this->_classes = $config['classes']; $this->_connection = $config['connection']; $this->_fixtures = $config['fixtures']; }
/** * Constructor. * * @param array $config Possible options are: * - `'connection'` _object_ : The connection instance. */ public function __construct($config = []) { $defaults = ['connection' => null, 'model' => $this->_model, 'meta' => [], 'alters' => $this->_alters]; $config = Set::merge($defaults, $config); $this->_connection = $config['connection']; $this->_meta = $config['meta']; $this->_alters = $config['alters']; $this->_model = $config['model']; $model = $this->_model; $model::config(['connection' => $this->connection()]); }
/** * Constructor * * @param array $config The config array. Possible values are: * - `'handlers'` _array_ : Some custom handlers. */ public function __construct($config = []) { $defaults = ['meta' => [], 'handlers' => [], 'classes' => $this->_classes, 'error' => function ($name, $options, $meta = []) { return Text::insert($options['message'] ?: $this->message($name), $options); }]; $config = Set::merge($defaults, $config); $this->_classes = $config['classes']; $this->set($config['handlers']); $this->error($config['error']); $this->meta($config['meta']); }
/** * Creates the database object and set default values for it. * * Options defined: * - `'dns'` : _string_ The full dsn connection url. Defaults to `null`. * - `'database'` : _string_ Name of the database to use. Defaults to `null`. * - `'host'` : _string_ Name/address of server to connect to. Defaults to 'localhost'. * - `'username'` : _string_ Username to use when connecting to server. Defaults to 'root'. * - `'password'` : _string_ Password to use when connecting to server. Defaults to `''`. * - `'encoding'` : _string_ The database character encoding. * - `'persistent'`: _boolean_ If true a persistent connection will be attempted, provided the * adapter supports it. Defaults to `true`. * - `'dialect'` : _object_ A SQL dialect adapter * * @param $config array Array of configuration options. * @return Database object. */ public function __construct($config = []) { $defaults = ['classes' => ['cursor' => 'chaos\\database\\Cursor', 'schema' => 'chaos\\database\\Schema', 'dialect' => 'sql\\Dialect'], 'client' => null, 'connect' => true, 'meta' => ['key' => 'id', 'locked' => true], 'persistent' => true, 'host' => 'localhost', 'username' => 'root', 'password' => '', 'database' => null, 'encoding' => null, 'dsn' => null, 'options' => [], 'dialect' => null, 'handlers' => []]; $config = Set::merge($defaults, $config); $this->_config = $config; $this->_classes = $this->_config['classes'] + $this->_classes; $this->_client = $this->_config['client']; unset($this->_config['client']); $this->_dialect = $config['dialect']; unset($this->_config['dialect']); $this->_handlers = Set::merge($this->_handlers(), $config['handlers']); if ($this->_dialect === null) { $dialect = $this->_classes['dialect']; $this->_dialect = new $dialect(['quoter' => function ($string) { return $this->quote($string); }, 'caster' => function ($value, $states = []) { $type = isset($states['type']) ? $states['type'] : gettype($value); if (is_array($type)) { $type = call_user_func($type, $states['name']); } return $this->format('datasource', $type, $value); }]); } if ($this->_config['connect']) { $this->connect(); } $handlers = $this->_handlers; $this->formatter('cast', 'id', $handlers['cast']['integer']); $this->formatter('cast', 'serial', $handlers['cast']['integer']); $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']['date']); $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']); $this->formatter('cast', '_default_', $handlers['cast']['string']); $this->formatter('datasource', 'id', $handlers['datasource']['string']); $this->formatter('datasource', 'serial', $handlers['datasource']['string']); $this->formatter('datasource', 'integer', $handlers['datasource']['string']); $this->formatter('datasource', 'float', $handlers['datasource']['string']); $this->formatter('datasource', 'decimal', $handlers['datasource']['string']); $this->formatter('datasource', 'date', $handlers['datasource']['date']); $this->formatter('datasource', 'datetime', $handlers['datasource']['datetime']); $this->formatter('datasource', 'boolean', $handlers['datasource']['boolean']); $this->formatter('datasource', 'null', $handlers['datasource']['null']); $this->formatter('datasource', 'string', $handlers['datasource']['quote']); $this->formatter('datasource', 'object', $handlers['datasource']['object']); $this->formatter('datasource', '_default_', $handlers['datasource']['quote']); }
/** * Expands a collection of entities by adding their related data. * * @param mixed $collection The collection to expand. * @return array The collection of related entities. */ public function embed(&$collection, $options = []) { $options = Set::merge(['fetchOptions' => ['collector' => $this->_collector($collection)]], $options); $name = $this->name(); $through = $this->through(); $using = $this->using(); $from = $this->from(); $relThrough = $from::relation($through); $middle = $relThrough->embed($collection, $options); $pivot = $relThrough->to(); $relUsing = $pivot::schema()->relation($using); $related = $relUsing->embed($middle, $options); $this->_cleanup($collection); $fromKey = $this->keys('from'); $indexes = $this->_index($related, $this->keys('to')); foreach ($collection as $index => $entity) { if (is_object($entity)) { foreach ($entity->{$through} as $key => $item) { if (isset($item->{$using})) { $value = $item->{$using}; if ($entity instanceof Model) { $entity->__get($name); // Too Many Magic Kill The Magic. } else { $entity->{$name}[] = $value; } } } } else { foreach ($entity[$through] as $key => $item) { if (isset($indexes[$item[$fromKey]])) { $collection[$index][$name][] = $related[$indexes[$item[$fromKey]]]; $collection[$index][$through][$key][$using] = $related[$indexes[$item[$fromKey]]]; } } } } return $related; }
/** * Expands a collection of entities by adding their related data. * * @param mixed $collection The collection to expand. * @param array $options The embedging options. * @return array The collection of related entities. */ public function embed(&$collection, $options = []) { $indexes = $this->_index($collection, $this->keys('from')); $related = $this->_find(array_keys($indexes), Set::merge(['fetchOptions' => ['collector' => $this->_collector($collection)]], $options)); $name = $this->name(); $this->_cleanup($collection); foreach ($related as $index => $entity) { if (is_object($entity)) { $value = $entity->{$this->keys('to')}; if (isset($indexes[$value])) { $source = $collection[$indexes[$value]]; $source->{$name}[] = $entity; } } else { $value = $entity[$this->keys('to')]; if (isset($indexes[$value])) { $collection[$indexes[$value]][$name][] = $entity; } } } return $related; }
/** * Returns a nested tree representation of `'embed'` option. * * @return array The corresponding nested tree representation. */ public function treeify($embed) { if (!$embed) { return []; } if ($embed === true) { $embed = $this->relations(); } $embed = Set::expand(array_fill_keys(array_keys(Set::normalize((array) $embed)), null)); $result = []; foreach ($embed as $relName => $value) { if (!isset($this->_relations[$relName])) { continue; } if ($this->_relations[$relName]['relation'] === 'hasManyThrough') { $rel = $this->relation($relName); $result[$rel->through()] = [$rel->using() => $value]; } $result[$relName] = $value; } return $result; }
/** * Constructs the MySQL adapter and sets the default port to 3306. * * @param array $config Configuration options for this class. Available options * defined by this class: * - `'host'`: _string_ The IP or machine name where MySQL is running, * followed by a colon, followed by a port number or socket. * Defaults to `'localhost:3306'`. */ public function __construct($config = []) { $defaults = ['host' => 'localhost:3306', 'classes' => ['dialect' => 'sql\\dialect\\MySql'], 'handlers' => []]; $config = Set::merge($defaults, $config); parent::__construct($config); }
/** * Gets all entities attached to a collection en entities. * * @param mixed $id An id or an array of ids. * @return object A collection of items matching the id/ids. */ protected function _find($id, $options = []) { $defaults = ['query' => [], 'fetchOptions' => []]; $options += $defaults; $fetchOptions = $options['fetchOptions']; unset($options['fetchOptions']); if ($this->link() !== static::LINK_KEY) { throw new ChaosException("This relation is not based on a foreign key."); } $to = $this->to(); if (!$id) { $collector = isset($fetchOptions['collector']) ? $fetchOptions['collector'] : null; return $to::create([], ['type' => 'set', 'collector' => $collector]); } if (is_array($id) && count($id) === 1) { $id = reset($id); } $query = Set::merge($options['query'], ['conditions' => [$this->keys('to') => $id]]); return $to::all($query, $fetchOptions); }
protected function _applyHas() { $tree = Set::expand(array_fill_keys(array_keys($this->has()), false)); $this->_applyJoins($this->model(), $tree, '', $this->alias()); foreach ($this->has() as $path => $conditions) { $this->where($conditions, $this->alias($path)); } }
/** * Instantiates a new record or document object, initialized with any data passed in. For example: * * ```php * $post = Posts::create(['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: * * ```php * $post = Posts::create(['id' => $id, 'moreData' => 'foo'], ['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. * - `'type'` _string_ : can be `'entity'` or `'set'`. `'set'` is used if the passed data represent a collection * of entities. Default to `'entity'`. * - `'exists'` _mixed_ : corresponds whether the entity is present in the datastore or not. * - `'autoreload'` _boolean_: sets the specific behavior when exists is `null`. A '`true`' value will perform a * reload of the entity from the datasource. Default to `'true'`. * - `'defaults'` _boolean_: indicates whether the entity needs to be populated with their defaults values on creation. * - `'model'` _string_ : the model to use for instantiating the entity. Can be useful for implementing * som Single Table Inheritance. * @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 the schema. */ public static function create($data = [], $options = []) { $defaults = ['type' => 'entity', 'exists' => false, 'model' => static::class]; $options += $defaults; $options['defaults'] = !$options['exists']; if ($options['defaults'] && $options['type'] === 'entity') { $data = Set::merge(Set::expand(static::schema()->defaults()), $data); } $type = $options['type']; $class = $type === 'entity' ? $options['model'] : static::$_classes[$options['type']]; $options = ['data' => $data] + $options; return new $class($options); }
/** * Constructs the Sqlite adapter and sets the default port to 3306. * * @param array $config Configuration options for this class. Available options: * - `'database'` _string_ : database path. Defaults to `':memory:'`. */ public function __construct($config = []) { $defaults = ['database' => ':memory:', 'classes' => ['dialect' => 'sql\\dialect\\Sqlite'], 'handlers' => []]; $config = Set::merge($defaults, $config); parent::__construct($config); }