diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a43c72b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +vendor/ +log/* +cache/* +app/src/models/ +sql/*.sqlite +composer.lock diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..4cbff8e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 vhchung + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..d1fc22a --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Slim 3 MVC Skeleton + +This is a simple skeleton project for Slim 3 that includes Doctrine, Twig, Flash messages and Monolog. + +Base on https://github.com/akrabat/slim3-skeleton + +## Prepare + +1. Create your project: + + `$ composer create-project -n -s dev agustim/slim3-skeleton-mvc-sqlite your-app` + +2. Create database: `$ cat sql/blog.sql | sqlite3 sql/blog.sqlite` +3. Generate models (Doctrine entities): + +``` + +$ cd your-app +$ php entities_generator.php + +``` + + Add namespace for each model: `namespace App\Model;` + + Notice: Delete all models before re-generate to update models. + +### Run it: + +1. `$ cd your-app` +2. `$ php -S 0.0.0.0:8888 -t public/` +3. Browse to http://localhost:8888 + +### Notice + +Set `logs` and `cache` folder permission to writable when deploy to production environment + +## Key directories + +* `app`: Application code +* `app/src`: All class files within the `App` namespace +* `app/templates`: Twig template files +* `cache/twig`: Twig's Autocreated cache files +* `log`: Log files +* `public`: Webserver root +* `vendor`: Composer dependencies +* `sql`: sql dump file for sample database + +## Key files + +* `public/index.php`: Entry point to application +* `app/settings.php`: Configuration +* `app/dependencies.php`: Services for Pimple +* `app/middleware.php`: Application middleware +* `app/routes.php`: All application routes are here +* `app/src/controllers/HomeController.php`: Controller class for the home page +* `app/src/models/Post.php`: Entity class for post table +* `app/templates/home.twig`: Twig template file for the home page diff --git a/app/dependencies.php b/app/dependencies.php new file mode 100755 index 0000000..6647526 --- /dev/null +++ b/app/dependencies.php @@ -0,0 +1,59 @@ +getContainer(); + +// ----------------------------------------------------------------------------- +// Service providers +// ----------------------------------------------------------------------------- + +// Twig +$container['view'] = function ($c) { + $settings = $c->get('settings'); + $view = new \Slim\Views\Twig($settings['view']['template_path'], $settings['view']['twig']); + + // Add extensions + $view->addExtension(new Slim\Views\TwigExtension($c->get('router'), $c->get('request')->getUri())); + $view->addExtension(new Twig_Extension_Debug()); + + return $view; +}; + +// Flash messages +$container['flash'] = function ($c) { + return new \Slim\Flash\Messages; +}; + +// ----------------------------------------------------------------------------- +// Service factories +// ----------------------------------------------------------------------------- + +// doctrine EntityManager +$container['em'] = function ($c) { + $settings = $c->get('settings'); + $config = \Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration( + $settings['doctrine']['meta']['entity_path'], + $settings['doctrine']['meta']['auto_generate_proxies'], + $settings['doctrine']['meta']['proxy_dir'], + $settings['doctrine']['meta']['cache'], + false + ); + return \Doctrine\ORM\EntityManager::create($settings['doctrine']['connection'], $config); +}; + +// monolog +$container['logger'] = function ($c) { + $settings = $c->get('settings'); + $logger = new \Monolog\Logger($settings['logger']['name']); + $logger->pushProcessor(new \Monolog\Processor\UidProcessor()); + $logger->pushHandler(new \Monolog\Handler\StreamHandler($settings['logger']['path'], \Monolog\Logger::DEBUG)); + return $logger; +}; + +// ----------------------------------------------------------------------------- +// Controller factories +// ----------------------------------------------------------------------------- + +$container['App\Controller\HomeController'] = function ($c) { + return new App\Controller\HomeController($c); +}; diff --git a/app/middleware.php b/app/middleware.php new file mode 100755 index 0000000..116a0ff --- /dev/null +++ b/app/middleware.php @@ -0,0 +1,4 @@ +add(new \Slim\Csrf\Guard); diff --git a/app/routes.php b/app/routes.php new file mode 100755 index 0000000..8762b83 --- /dev/null +++ b/app/routes.php @@ -0,0 +1,8 @@ +get('/', 'App\Controller\HomeController:dispatch') + ->setName('homepage'); + +$app->get('/post/{id}', 'App\Controller\HomeController:viewPost') + ->setName('view_post'); diff --git a/app/settings.php b/app/settings.php new file mode 100755 index 0000000..7cce5c9 --- /dev/null +++ b/app/settings.php @@ -0,0 +1,38 @@ + [ + // comment this line when deploy to production environment + 'displayErrorDetails' => true, + // View settings + 'view' => [ + 'template_path' => __DIR__ . '/templates', + 'twig' => [ + 'cache' => __DIR__ . '/../cache/twig', + 'debug' => true, + 'auto_reload' => true, + ], + ], + + // doctrine settings + 'doctrine' => [ + 'meta' => [ + 'entity_path' => [ + __DIR__ . '/src/models' + ], + 'auto_generate_proxies' => true, + 'proxy_dir' => __DIR__.'/../cache/proxies', + 'cache' => null, + ], + 'connection' => [ + 'driver' => 'pdo_sqlite', + 'path' => __DIR__.'/../sql/blog.sqlite' + ] + ], + + // monolog settings + 'logger' => [ + 'name' => 'app', + 'path' => __DIR__ . '/../log/app.log', + ], + ], +]; diff --git a/app/src/controllers/BaseController.php b/app/src/controllers/BaseController.php new file mode 100644 index 0000000..3c23339 --- /dev/null +++ b/app/src/controllers/BaseController.php @@ -0,0 +1,20 @@ +view = $c->get('view'); + $this->logger = $c->get('logger'); + $this->flash = $c->get('flash'); + $this->em = $c->get('em'); + } +} \ No newline at end of file diff --git a/app/src/controllers/HomeController.php b/app/src/controllers/HomeController.php new file mode 100755 index 0000000..abecd9f --- /dev/null +++ b/app/src/controllers/HomeController.php @@ -0,0 +1,35 @@ +logger->info("Home page action dispatched"); + + $this->flash->addMessage('info', 'Sample flash message'); + + $this->view->render($response, 'home.twig'); + return $response; + } + + public function viewPost(Request $request, Response $response, $args) + { + $this->logger->info("View post using Doctrine with Slim 3"); + + $messages = $this->flash->getMessage('info'); + + try { + $post = $this->em->find('App\Model\Post', intval($args['id'])); + } catch (\Exception $e) { + echo $e->getMessage(); + die; + } + + $this->view->render($response, 'post.twig', ['post' => $post, 'flash' => $messages]); + return $response; + } +} diff --git a/app/templates/home.twig b/app/templates/home.twig new file mode 100755 index 0000000..3a769ad --- /dev/null +++ b/app/templates/home.twig @@ -0,0 +1,13 @@ + + + + Slim 3 + + + + +

Slim

+
a microframework for PHP
+ + + diff --git a/app/templates/post.twig b/app/templates/post.twig new file mode 100644 index 0000000..764f48a --- /dev/null +++ b/app/templates/post.twig @@ -0,0 +1,28 @@ + + + + Slim 3 + + + + +

Slim

+
a microframework for PHP
+ +{% if flash %} +
+ {% for f in flash %} +

{{ f }}

+ {% endfor %} +
+{% endif %} + +
+ {% if post %} +

{{ post.title }}

+
+

{{ post.content }}

+ {% endif %} +
+ + \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..ed0a7bd --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "vhchung/slim3-skeleton-mvc", + "description": "Simple Slim Framework 3 skeleton with Twig, Monolog, Doctrine.", + "license": "MIT", + "type": "project", + "keywords": ["slim-framework", "skeleton", "mvc"], + "authors": [ + { + "name": "Vu Hoang Chung", + "email": "chung.it@live.com", + "homepage": "http://chungvh.xyz" + } + ], + "autoload": { + "psr-4": { + "App\\Controller\\": "app/src/controllers", + "App\\Model\\": "app/src/models" + } + }, + "require": { + "slim/slim": "~3.0", + "slim/twig-view": "^2.0", + "slim/flash": "^0.1.0", + "monolog/monolog": "^1.17", + "doctrine/orm": "2.5.*" + } +} diff --git a/entities_generator.php b/entities_generator.php new file mode 100644 index 0000000..32512bf --- /dev/null +++ b/entities_generator.php @@ -0,0 +1,37 @@ +register(); +$classLoader = new \Doctrine\Common\ClassLoader('Proxies', __DIR__); +$classLoader->register(); +// config +$config = new \Doctrine\ORM\Configuration(); +$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver(__DIR__ . '/app/src/models')); +$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); +$config->setProxyDir(__DIR__ . '/Proxies'); +$config->setProxyNamespace('Proxies'); +$connectionParams = array( + 'driver' => 'pdo_sqlite', + 'path' => __DIR__.'/sql/blog.sqlite' +); +$em = \Doctrine\ORM\EntityManager::create($connectionParams, $config); +// custom datatypes (not mapped for reverse engineering) +$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('set', 'string'); +$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); +// fetch metadata +$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( + $em->getConnection()->getSchemaManager() +); +$em->getConfiguration()->setMetadataDriverImpl($driver); +$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory($em); +$cmf->setEntityManager($em); +$classes = $driver->getAllClassNames(); +$metadata = $cmf->getAllMetadata(); +$generator = new Doctrine\ORM\Tools\EntityGenerator(); +$generator->setUpdateEntityIfExists(true); +$generator->setGenerateStubMethods(true); +$generator->setGenerateAnnotations(true); +$generator->generate($metadata, __DIR__ . '/app/src/models'); +print '-------------------------------------------' . PHP_EOL; +print ' Done! Generated models to `app\src\models`' . PHP_EOL; +print '-------------------------------------------' . PHP_EOL; diff --git a/public/.htaccess b/public/.htaccess new file mode 100755 index 0000000..0071532 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,16 @@ +# For production, put your rewrite rules directly into your VirtualHost +# directive and turn off AllowOverride. + + + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} -s [OR] + RewriteCond %{REQUEST_FILENAME} -l [OR] + RewriteCond %{REQUEST_FILENAME} -d + RewriteRule ^.*$ - [NC,L] + + + RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$ + RewriteRule ^(.*) - [E=BASE:%1] + RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L] + diff --git a/public/css/style.css b/public/css/style.css new file mode 100755 index 0000000..322b3bf --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,18 @@ +body { + margin: 50px 0 0 0; + padding: 0; + width: 100%; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + text-align: center; + color: #aaa; + font-size: 18px; +} + +h1 { + color: #719e40; + letter-spacing: -3px; + font-family: 'Lato', sans-serif; + font-size: 100px; + font-weight: 200; + margin-bottom: 0; +} diff --git a/public/index.php b/public/index.php new file mode 100755 index 0000000..d24a30e --- /dev/null +++ b/public/index.php @@ -0,0 +1,30 @@ +run(); diff --git a/sql/blog.sql b/sql/blog.sql new file mode 100644 index 0000000..dea3d42 --- /dev/null +++ b/sql/blog.sql @@ -0,0 +1,5 @@ +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; +CREATE TABLE post (id int primary key not null, title char(100) default null, slug char(200) not null, content text not null); +INSERT INTO "post" VALUES(1,'First blog post','first-blog-post','This is sample blog post. If you see this content, doctrine is working fine.'); +COMMIT;