From fbb92187c4b52e119dd7af93a87c4cda6d78b2d1 Mon Sep 17 00:00:00 2001 From: r1k0 Date: Wed, 20 Apr 2016 19:44:43 +0200 Subject: [PATCH] Real initial commit --- Helper/HelperInterface.php | 45 +++++++++++++++ Helper/Sluggable.php | 111 +++++++++++++++++++++++++++++++++++++ Helper/Timestampable.php | 78 ++++++++++++++++++++++++++ Logger.php | 106 +++++++++++++++++++++++++++++++++++ Model/BaseModel.php | 16 ++++++ Model/ModelHelper.php | 69 +++++++++++++++++++++++ README.md | 66 +++++++++++++++++++++- RedBean.php | 33 +++++++++++ RedBeanServiceProvider.php | 83 +++++++++++++++++++++++++++ composer.json | 21 +++++++ 10 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 Helper/HelperInterface.php create mode 100644 Helper/Sluggable.php create mode 100644 Helper/Timestampable.php create mode 100644 Logger.php create mode 100644 Model/BaseModel.php create mode 100644 Model/ModelHelper.php create mode 100644 RedBean.php create mode 100644 RedBeanServiceProvider.php create mode 100644 composer.json diff --git a/Helper/HelperInterface.php b/Helper/HelperInterface.php new file mode 100644 index 0000000..e826a79 --- /dev/null +++ b/Helper/HelperInterface.php @@ -0,0 +1,45 @@ + array( + 'update' => array() + ), + 'options' => array( + 'fieldName' => 'slug' + ) + ); + + /** + * {@inheritdoc} + */ + public function configure($defaults = array()) + { + $this->defaults = array_merge( + $this->defaults, + $defaults + ); + } + + /** + * {@inheritdoc} + */ + public function apply(OODBBean $bean, $eventName, array $options) + { + $fields = $options['events'][$eventName]; + $parts = array(); + foreach ($fields as $name) { + $parts[] = $bean[$name]; + } + + $fieldName = $options['options']['fieldName']; + + if ($parts) { + $bean->setMeta(sprintf('cast.%s', $fieldName), 'string'); + $bean->$fieldName = $this->sluggify(join(' ', $parts)); + } + } + + /* + * Sluggify + * + * @param string + * + * @return string + */ + public function sluggify($text) + { + // replace non letter or digits by - + $text = preg_replace('~[^\\pL\d]+~u', '-', $text); + // trim + $text = trim($text, '-'); + // transliterate + if (function_exists('iconv')) { + $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); + } + // lowercase + $text = strtolower($text); + // remove unwanted characters + $text = preg_replace('~[^-\w]+~', '', $text); + if (empty($text)) { + return 'n-a'; + } + + return $text; + } + + /** + * {@inheritdoc} + */ + public function getEventsName() + { + return array_keys($this->defaults['events']); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sluggable'; + } + + /** + * {@inheritdoc} + */ + public function validate(array $configuration) + { + if (!isset($configuration['events'])) { + throw new \RuntimeException(sprintf('You must specify an "events" key, with the targetted fields for the "%s" helper', $this->getName())); + } + + if (!isset($configuration['options']['fieldName'])) { + $configuration['options']['fieldName'] = $this->defaults['options']['fieldName']; + } + + return $configuration; + } +} diff --git a/Helper/Timestampable.php b/Helper/Timestampable.php new file mode 100644 index 0000000..a805269 --- /dev/null +++ b/Helper/Timestampable.php @@ -0,0 +1,78 @@ + array( + 'dispense' => array('created_at'), + 'update' => array('updated_at') + ), + 'options' => array( + 'dateFormat' => 'Y-m-d H:i:s' + ) + ); + + /** + * {@inheritdoc} + */ + public function configure($defaults = array()) + { + $this->defaults = array_merge( + $this->defaults, + $defaults + ); + } + + /** + * {@inheritdoc} + */ + public function apply(OODBBean $bean, $eventName, array $options) + { + $fields = $options['events'][$eventName]; + $dateFormat = $options['options']['dateFormat']; + + foreach ($fields as $name) { + $bean->$name = (new \DateTime())->format($dateFormat); + } + } + + /** + * {@inheritdoc} + */ + public function getEventsName() + { + return array_keys($this->defaults['events']); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'timestampable'; + } + + /** + * {@inheritdoc} + */ + public function validate(array $configuration) + { + if (!isset($configuration['events'])) { + $configuration['events'] = $this->defaults['events']; + } + + if (!isset($configuration['options']['dateFormat'])) { + $configuration['options']['dateFormat'] = $this->defaults['options']['dateFormat']; + } + + return $configuration; + } +} diff --git a/Logger.php b/Logger.php new file mode 100644 index 0000000..c1f8530 --- /dev/null +++ b/Logger.php @@ -0,0 +1,106 @@ +logger = $logger; + $this->throwException = $throwException; + $this->debugQuery = $debugQuery; + $this->mode = self::C_LOGGER_ARRAY; + } + + /** + * Writes a query for logging with all bindings / params filled + * in. + * + * @param string $newSql the query + * @param array $bindings the bindings to process (key-value pairs) + * + * @return string + */ + private function writeQuery($newSql, $newBindings) + { + //avoid str_replace collisions: slot1 and slot10 (issue 407). + uksort($newBindings, function ($a, $b) { + return (strlen($b) - strlen($a)); + }); + + $newStr = $newSql; + foreach ($newBindings as $slot => $value) { + if (strpos($slot, ':') === 0) { + $newStr = str_replace($slot, $this->fillInValue($value), $newStr); + } + } + return $newStr; + } + + public function log() + { + if (func_num_args() < 1) { + return; + }; + + $message = ''; + if ($this->debugQuery) { + $sql = func_get_arg(0); + + if (func_num_args() < 2) { + $bindings = array(); + } else { + $bindings = func_get_arg(1); + } + + if (is_array($bindings)) { + $newSql = $this->normalizeSlots($sql); + $newBindings = $this->normalizeBindings($bindings); + $message = $this->writeQuery($newSql, $newBindings); + } else { + $message = $sql; + } + + if ($message) { + $this->doLog($message); + } + } else { + foreach (func_get_args() as $argument) { + if (is_array($argument)) { + $argument = array_filter($argument); + $message = print_r($argument, true); + $this->doLog($message); + } else { + $this->doLog($argument); + } + } + } + } + + protected function doLog($message) + { + $level = preg_match('@error@i', $message) ? Monolog::CRITICAL : Monolog::DEBUG; + $this->logger->addRecord($level, $message); + + if ($level === Monolog::CRITICAL && $this->throwException) { + throw new RedException($message); + } + } +} diff --git a/Model/BaseModel.php b/Model/BaseModel.php new file mode 100644 index 0000000..f47bc7e --- /dev/null +++ b/Model/BaseModel.php @@ -0,0 +1,16 @@ +bean->getMeta('type'); + } +} diff --git a/Model/ModelHelper.php b/Model/ModelHelper.php new file mode 100644 index 0000000..7e64799 --- /dev/null +++ b/Model/ModelHelper.php @@ -0,0 +1,69 @@ + $options) { + $helper = new $class; + $helper->configure($options); + $this->addHelper($helper); + } + } + + public function addHelper(HelperInterface $helper) + { + $this->helpers[$helper->getName()] = $helper; + } + + public function addModel($name, $configuration) + { + foreach (array_keys($configuration) as $helperName) { + if (null === $helper = $this->getHelper($helperName)) { + throw new \RuntimeException(sprintf('No helper registered with that name "%s"', $helperName)); + } + + $configuration[$helperName] = $helper->validate($configuration[$helperName]); + } + + $this->models[$name] = $configuration; + } + + public function getHelper($name) + { + return isset($this->helpers[$name]) ? $this->helpers[$name] : null; + } + + public function getModel($name) + { + return isset($this->models[$name]) ? $this->models[$name] : null; + } + + public function onEvent($eventName, $bean) + { + $type = $bean->getMeta('type'); + if (isset($this->models[$type])) { + $model = $this->getModel($type); + foreach ($model as $helperName => $options) { + $helper = $this->getHelper($helperName); + if (!$helper || !isset($options['events'][$eventName])) { + continue; + } + + $helper->apply($bean, $eventName, $options); + } + } + } +} diff --git a/README.md b/README.md index aae4cc5..b35c534 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,66 @@ -# readbean-silex-provider +# redbean-silex-provider A RedBean service provider for Silex + +## Provider + + // src/app.php + + use Fazb\RedBean\RestServiceProvider + + $app['data_dir'] = __DIR__ . '/../var/data'; + + $app['rb.options'] = array( + 'dsn' => sprintf('sqlite://%s/app.db', $app['data_dir']), + 'namespace' => 'Site\\Model\\', + 'freeze' => !$app['debug'] + ); + + + $app->register(new RedBeanServiceProvider()); + +## Model + + firstName, $this->lastName); + } + } + +## Model Helper + +sluggable, timestampable event based + + $app['redbean.model.helpers'] = array( + 'article' => array( + 'sluggable' => array( + 'events' => array( + 'update' => array('title') + ), + // Default + 'options' => array( + 'fieldName' => 'slug' + ) + ), + 'timestampable' => array( + // Default + 'events' => array( + 'dispense' => array('created_at'), + 'update' => array('updated_at') + ), + 'options' => array( + 'dateFormat' => 'Y-m-d H:i:s' + ) + ) + ) + ); diff --git a/RedBean.php b/RedBean.php new file mode 100644 index 0000000..f6fa821 --- /dev/null +++ b/RedBean.php @@ -0,0 +1,33 @@ +setup($options['dsn'], $options['username'], $options['password']); + $this->freeze($options['freeze']); + $this->setAutoResolve(true); + } + + /** + * Magic method + * Passes all calls to RedBean's singleton + * + * @return mixed + */ + public function __call($method, $params) + { + $return = call_user_func_array('\RedBeanPHP\Facade::' . $method, $params); + + return $return; + } +} diff --git a/RedBeanServiceProvider.php b/RedBeanServiceProvider.php new file mode 100644 index 0000000..ccf1b91 --- /dev/null +++ b/RedBeanServiceProvider.php @@ -0,0 +1,83 @@ +share(function () use ($app) { + $options = $app['rb.options']; + if ($namespace = isset($options['namespace']) ? $options['namespace'] : '') { + $this->setModelNamespace($namespace); + unset($options['namespace']); + } + + $options = array_merge(array( + 'dsn' => null, + 'username' => null, + 'password' => null, + 'freeze' => false + ), $options); + + $rb = new RedBean($options); + + $logger = new Logger($app['logger']); + $db = $rb->getDatabaseAdapter()->getDatabase(); + $db->setLogger($logger); + $db->setEnableLogging(true); + + if (isset($app['redbean.helpers']) && isset($app['redbean.model.helpers'])) { + $modelHelper = new ModelHelper($app['redbean.helpers']); + $modelHelper->attachEventListeners($rb->getRedBean()); + + foreach ($app['redbean.model.helpers'] as $model => $options) { + $modelHelper->addModel($model, $options); + } + + $app['redbean.model_helper'] = $app->share(function () use ($modelHelper) { + return $modelHelper; + }); + } + + return $rb; + }); + } + + /** + * Bootstraps the application + * + * @return void + */ + public function boot(Application $app) + { + } + + /** + * Sets RedBean Model namespace + * + * @return void + */ + protected function setModelNamespace($namespace) + { + if (!defined('REDBEAN_MODEL_PREFIX')) { + define('REDBEAN_MODEL_PREFIX', $namespace); + } + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..cda9906 --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "faz-b/redbean-silex-provider", + "description": "A RedBean service provider for Silex", + "type": "library", + "keywords": ["redbean", "silex", "orm", "php"], + "require": { + "php": ">=5.3.0", + "silex/silex": "~1.0", + "gabordemooij/redbean": "~4.0" + }, + "license": "MIT", + "authors": [ + { + "email": "eric@faz-b.com", + "homepage": "http://www.faz-b.com" + } + ], + "autoload": { + "psr-4": {"Fazb\\RedBean\\": "src"} + } +}