l0gin / slim-3-poo-skeleton
Slim Framework 3 POO skeleton with Web, Secured API and Cli endpoints.
Requires
- php: >=5.5.0
- league/climate: ^3.2
- mongodb/mongodb: ^1.0.0
- monolog/monolog: ^1.13
- php-middleware/php-debug-bar: ^1.1
- slim/flash: ^0.1.0
- slim/slim: ^3.0
- slim/twig-view: ^2.0
- tuupola/slim-basic-auth: ^2.0
- tuupola/slim-jwt-auth: ^2.3
- zeuxisoo/slim-whoops: 0.5.*
This package is auto-updated.
Last update: 2017-05-05 09:47:45 UTC
README
This is a full POO skeleton project for Slim 3 that includes the following usefull dependencies / middlewares :
- Twig
- Slim Flash Messages
- Monolog Seldaek/monolog
- PDO
- MongoDB mongo-php-library - Mongo Doc
- PHP Debug Bar maximebf/php-debugbar
- CLImate thephpleague/climate
- Whoops filp/whoops
This skeleton provide:
--> A Web controller (app/src/Core/Controller/WebController.php
) that you can extend to get default depencies loads.
It load the following services :
- Twig
- Monolog
- Flash
It also include two Twig templates based on Material Design Lite. The following Front & Back office templates :
- Front: https://getmdl.io/templates/android-dot-com/index.html
- Back: https://getmdl.io/templates/dashboard/index.html
--> A secured authenticated API controller using Basic Auth, MongoDB and JSON Web Token (JWT) middlewares.
- GET -
api/auth
to a get a JWT on succesfull authentication. - GET -
api/user
to retrieves User informations.
--> A CLI controller (app/src/Core/Controller/CliController.php
) witch load Monolog and CLImate and provide some usefull functions.
A Setup
Class is provided to help initiate MongoBD structure and creates users.
Create your project
$ composer create-project -n -s dev l0gin/slim-3-poo-skeleton my-app
Run it
$ cd my-app
$ php -S 0.0.0.0:8888 -t public public/index.php
- Browse to http://localhost:8888
Prefer using Apache 2.4 and PHP 7.0 (FPM).
Key directories
app
: Application codeapp/src
: All class files within theApp
namespaceapp/templates
: Twig template filesapp/conf
: Custom / environment settingscache/twig
: Twig's Autocreated cache fileslog
: Log filespublic
: Webserver rootpublic/assets
: Public ressources (css, js, img, ...)vendor
: Composer dependencies
Key files
public/index.php
: Entry point to applicationapp/Core/Bootstrap/HttpBootstrap.php
: HTTP Bootstrap class for Web and API endpoints - Load and setup Appapp/Core/Bootstrap/CliBootstrap.php
: CLI Bootstrap class for CLI endpoints - Load and setup Appapp/Core/Settings.php
: Default configurationapp/Core/Dependencies.php
: Services for Slim DI Containerapp/Core/Middlewares.php
: Application middlewaresapp/Core/Routes.php
: All application routes are hereapp/src/Core/Controller/WebController.php
: Web Controller super class - For Web endpoint usingapp/src/Core/Controller/ApiController.php
: API Controller super class - For API endpoint usingapp/src/Core/Controller/CliController.php
: CLI Controller super class - For CLI endpoint usingapp/src/Controller/Web/Front.php
: Controller class for the home pageapp/src/Controller/Web/Back.php
: Controller class for the dashboard pageapp/src/Controller/Api/Auth.php
: Controller class for API Authenticationapp/src/Controller/Api/User.php
: Controller class for User action through APIapp/src/Controller/Cli/Setup.php
: Controller class for app setupapp/conf/local.settings.php.dist
: Copy this file as local.settings.php and add your custom / environment settingsapp/templates/layouts/front.twig
: Main Twig template file for front layout pagesapp/templates/layouts/back.twig
: Main Twig template file for back layout pagesapp/templates/pages/front/*
: Twig template files for front pagesapp/templates/pages/back/*
: Twig template files for back pages
Use dependencies
In your controllers override the parent contructor and load your dependency from Slim Container.
Load PDO dependency
<?php
final class MyController extends WebController
{
/**
* @var \PDO PDO Instance
*/
private $pdo;
/**
* MyController constructor override
*
* @param Container $c Slim App Container
*/
public function __construct(Container $c)
{
$this->pdo = $c->get('pdo');
parent::__construct($c);
}
//...
}
Override / Set local (environment) settings
Copy local.settings.php.dist
to local.settings.php
and add your env settings.
<?php
return [
'settings' => [
//Slim Settings
'displayErrorDetails' => true, //Display Slim Errors
// PDO settings
'pdo' => [
'driver' => 'mysql',
'host' => '127.0.0.1',
'database' => 'mydb',
'user' => 'foo',
'passwd' => 'bar'
],
// Mongo DB settings
'mongo' => [
'host' => '127.0.0.1',
'port' => 27017,
'options' => [
//"username" => 'foo',
//"password" => 'bar'
],
'driverOptions' => [],
'default_db' => 'database'
],
//Google Analytics
'google_analytics' => [
'api_key' => 'UA-XXXXX-Y',
'anonymize_ip' => false
]
]
];
Adding Dependencies / Middlewares / Routes
App\Core\Dependencies
, App\Core\Middlewares
and App\Core\Routes
classes have
an auto-load function witch call every methods starting with load
(such as loadTwig
).
So, to add a service, a middleware or a new route function (multiple routes can be added into each load
function),
add a function like the following services setup :
<?php
/**
* Load Faker
*/
protected function loadFaker()
{
/**
* Faker Factory Service
*
* @param Container $c
*
* @return \Faker\Factory
*/
$this->dic['faker'] = function (Container $c) {
return Faker\Factory::create();
};
}
/**
* Load AMQP Stream Connection
*/
protected function loadAMQP()
{
/**
* AMQP Stream Connection
*
* @param Container $c
*
* @return \PhpAmqpLib\Connection\AMQPStreamConnection
*/
$this->dic['amqp'] = function (Container $c) {
$settings = $c->get('settings')['amqp']; //@TODO: Add this setting into Settings.php and/or local.settings.php
return new \PhpAmqpLib\Connection\AMQPStreamConnection(
$settings['amqp']['host'],
$settings['amqp']['port'],
$settings['amqp']['user'],
$settings['amqp']['pass']
);
};
}
To simply add a front-office
route, just add it into loadFrontRoutes()
function.
(Directly inside Routes
class or inside an extended class).
<?php
// /!\ Directly inside Routes class
/**
* Load front-office routes
*/
protected function loadFrontRoutes()
{
$this->app->get('/', 'App\Controller\Front:homeAction')
->setName('homepage');
$this->app->get('/contact', 'App\Controller\Front:contactAction')
->setName('contact');
}
// /!\ Inside a Routes extended class
class MyRoutes extends \App\Core\Routes
{
//...
/**
* Load front-office routes
*/
protected function loadFrontRoutes()
{
parent::loadFrontRoutes();
$this->app->get('/contact', 'App\Controller\FrontController:contactAction')
->setName('contact');
}
//...
}
Debug Bar
Displays a debug bar in the browser with information from php. No more var_dump() in your code!
https://github.com/maximebf/php-debugbar
maximebf/php-debugbar
is handled with php-middleware/phpdebugbar
from https://github.com/php-middleware/phpdebugbar
It load the following collectors:
Default collectors:
- PhpInfoCollector
- MessagesCollector
- RequestDataCollector
- TimeDataCollector
- MemoryCollector
- ExceptionsCollector
Extra collectors:
- ConfigCollector (based on app settings)
- MonologCollector
- PDOCollector
Command Line Interface (PHP CLI) Endpoint
This skeleton provide a CLI endpoint to help you create cli script using core app dependency injection and all other stuff.
For example, to run init()
function from App\CLI\Setup
:
$ php bin/cli.php -s Setup::init -v
Add some verbosity:
$ php bin/cli.php -s Setup::init -v
# Or
$ php bin/cli.php -s Setup::init --verbose
Print help:
$ php bin/cli.php -s Setup::init -h
# Or
$ php bin/cli.php -s Setup::init --help
Add or update an User by calling user()
function from App\Controller\Cli\Setup
:
$ php bin/cli.php -s Setup::user -l admin2 -p coucou -g test -v
Add argument and help
To add argument check, override checkParameters() function like the Setup.php
does
and use addParameter()
function to:
- Add short and long option (related to getOpt())
- Describe argument
- Add example(s)
Description and example(s) are automaticaly printed by printHelp()
function.
<?php
/**
* Custom parameter check
*
* @return bool Return false will automaticaly call printHelp() function and stop script execution
*/
public function checkParameters()
{
// Add custom parameter
$this->addParameter('a', 'all', 'Setup All', '--all');
if(parent::checkParameters()) {
// Check custom parameter
$aOpt = $this->getArg('a');
if ($aOpt !== null) {
$this->initAll = true;
}
return true;
} else {
return false;
}
}
Asset Management
To fully deploy project you have some other setup steps to follow:
#Install gulp globally:
$ npm install --global gulp
#Install gulp in your project devDependencies:
$ npm install --save-dev gulp`
Install Gulp dependencies:
$ npm i
Run Gulp tasks:
# CSS task: clean CSS files
$ gulp beautify_css
# Deploy assets for production: minify CSS, JS
$ gulp
# Or
$ gulp prod
# Build as you dev (Auto launch 'gulp prod' on file changing)
$ gulp wath
Troubleshooting
Gulp-autoprefixer issue
If you have an error like that while running gulp build
#!shell
/var/www/websites/weather-dashboard/web/node_modules/gulp-autoprefixer/node_modules/postcss/lib/lazy-result.js:157
this.processing = new Promise(function (resolve, reject) {
^
ReferenceError: Promise is not defined
at LazyResult.async (/var/www/websites/weather-dashboard/web/node_modules/gulp-autoprefixer/node_modules/postcss/lib/lazy-result.js:157:31)
Take a look at here: http://stackoverflow.com/a/32502195
Twig Extensions
This skeleton provide a custom Twig Extension that implement the following Twig functions:
getCssUrl(asset): Return minified css file URL if ['assets']['min'] == true and files exists. Otherwise return original css file URL if exists
<link rel="stylesheet" href="{{ getCssUrl('front.main.css') }}">
getJsUrl(asset): Return minified js file URL if ['assets']['min'] == true and files exists. Otherwise return original js file URL if exists
<script type="text/javascript" src="{{ getJsUrl('front.main.js') }}"></script>
Add Twig Functions
Feel free to add twig functions by editing or overriding App\Core\Twig\AssetTwigExtension
.
To add a Twig function, just add a function starting with the twig function name and ending with Function
.
<?php
/**
* Twig Function to get JS asset URL
*
* @param $filename
*
* @return string
* @throws \Exception
*/
public function getJsUrlFunction($filename)
{
return $this->getAssetUrl('js', $filename);
}
System Auth
Basic Autentication & JWT:
Get a JSON Web Token by authenticating in /api/auth
then just deal with the token.
$ curl -X GET -H "Authorization: Basic YWRtaW46Y291Y291" -H "Cache-Control: no-cache" "http://www.foo.bar/api/auth"
Response:
{
"status": "ok",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Njc4MDU2MDQsImV4cCI6MTQ2NzgxMjgwNCwianRpIjoiR2NRa3VoM3poeVBhRUJyVUVtaGsrdz09Iiwic3ViIjoiNTc3Y2Q1ZWNkNDJhYTQyYzVkMWZmMzQyIn0.4GectmgSi4qOforBGm31Z8Qd4b2kM_EFrNC9TfQXkos"
}
Get User informations using the JWT from api auth:
$ curl -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Njc4MDU2MDQsImV4cCI6MTQ2NzgxMjgwNCwianRpIjoiR2NRa3VoM3poeVBhRUJyVUVtaGsrdz09Iiwic3ViIjoiNTc3Y2Q1ZWNkNDJhYTQyYzVkMWZmMzQyIn0.4GectmgSi4qOforBGm31Z8Qd4b2kM_EFrNC9TfQXkos" -H "Cache-Control: no-cache" "http://www.foo.bar/api/user"
Response:
{
"login": "admin",
"created_at": "48482-09-12 15:03:51",
"updated_at": "48482-09-12 15:03:51",
"group_name": "test",
"group_id": "577cd5ebd42aa42c5d1ff341"
}
Roadmap
- Unit test endpoint
- PHP Doc generator
- Rest API Doc generator (Phinx, APIJS, ...)
- Bower to install public vendor assets