Skip to content

Commit 84edc93

Browse files
committed
inital commit
0 parents  commit 84edc93

13 files changed

Lines changed: 577 additions & 0 deletions

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
### JetBrains template
3+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5+
6+
# User-specific stuff:
7+
.idea
8+
9+
## File-based project format:
10+
*.iws
11+
12+
## Plugin-specific files:
13+
14+
# IntelliJ
15+
/out/
16+
17+
# mpeltonen/sbt-idea plugin
18+
.idea_modules/
19+
20+
# JIRA plugin
21+
atlassian-ide-plugin.xml
22+
23+
# Crashlytics plugin (for Android Studio and IntelliJ)
24+
com_crashlytics_export_strings.xml
25+
crashlytics.properties
26+
crashlytics-build.properties
27+
fabric.properties
28+
### Composer template
29+
composer.phar
30+
/vendor/
31+
32+
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
33+
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
34+
composer.lock

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# dk-controller
2+
3+
This package contains controller like middleware to be used inside a DotKernel or Expressive application. It provides base classes for action based controllers similar to ZF3 controller component. It is more lightwight though, but supports controller plugins.
4+
5+
## Installation
6+
7+
We use this in our web starter packages, so it will automatically be installed if you use them. For manual installation, just add the appropriate package to your `composer.json`
8+
9+
`composer install n3vrax/dk-controller`
10+
11+
For default module dependencies, you need to merge the ConfigProvider class output to your config. Again, if you are using the web started packages, this is already handled.
12+
13+
By doing this, you "enable" the module to be available functionally for your project
14+
15+
## Usage
16+
17+
Middleware controllers act as a handler for multiple routes. Some conventions were made:
18+
- register controllers in the routes array just like any expressive middleware. The requirement is you should define an `action` route parameter(possibly optional) anywhere inside the route(e.g `/user[/{action}]`)
19+
- action parameter value is converted to a method name inside the controller. Underscore, dot and line characters are removed and the action name is converted to camel-case suffixed by the string `Action`. For example a route and action pair like `/user/forgot-password` will be converted to method `forgotPasswordAction` inside a `UserController` class(as defined in routes config)
20+
- the default action value, if not present in the URI is `index`, so you should always define an `indexAction` within your controllers for displaying a default page or redirecting.
21+
22+
In order to create your action based controllers, you must extend the abstract class `DotKernel\DotController\AbstractActionController`
23+
24+
##### Example 1
25+
Creating a UserController with default action and a register action. Will handle routes `/user` and `/user/register`
26+
##### UserController.php
27+
```php
28+
29+
use DotKernel\DotController\AbstractActionController;
30+
31+
class UserController extends AbstractActionController
32+
{
33+
public function indexAction()
34+
{
35+
//...
36+
}
37+
38+
public function registerAction()
39+
{
40+
//...
41+
}
42+
}
43+
```
44+
45+
Then register this controller in the `routes` config(we assume you use FastRouter and ZF3 service manager)
46+
##### routes.global.php
47+
```php
48+
'dependencies' => [
49+
//add UserController class as a dependency, in invokable or factories etc.
50+
//for example
51+
'factories' => [
52+
\Your\Namespace\UserContoller::class => \Your\Namespace\UserContollerFactory::class,
53+
]
54+
],
55+
56+
'routes' => [
57+
[
58+
'name' => 'user',
59+
'path' => '/user[/{action}]',
60+
'middleware' => Your\Namespace\UserController::class,
61+
]
62+
],
63+
```
64+
65+
### Multiple controllers for the same route
66+
67+
Use case: You have defined a controller inside some package, with default actions. You want to add actions that fall into the same controller name(or route name more exactly). You want to do this without extending the controller provided by the package. In this case you can do the following
68+
- create your own controller, independent of the package's controller which adds or overwrites actions
69+
- ZE lets you define an array o middleware for a route, so you can register this controller before the package's controller
70+
71+
##### Example
72+
```php
73+
'routes' => [
74+
[
75+
'name' => 'user',
76+
'path' => '/user[/{action}]',
77+
'middleware' => [Your\Namespace\UserController::class, Package\UserController::class],
78+
]
79+
]
80+
```
81+
82+
Now when a request for this route comes in, your controller will run first. Controllers are designed to ignore requests that cannot be matched to one of its methods, so if no action matches, it will call the next middleware, in our case, the second controller. If this is the last controller, and action does not match here, it will go to the default 404 Not found page. There is a simple middleware defined in dk-base that makes sure any request that does not match will be converted explicitly to a 404 error.
83+
84+
## Controller plugins
85+
86+
Controllers support controller plugins, much like controllers in a ZF3 application. The module comes packed with a few common plugins, but you can extend controller functionality with your own plugins too.
87+
88+
### Usage
89+
90+
Controller plugins must implement `DotKernel\DotController\Plugin\PluginInterface`. You can add them to the cconfig file, at key `['dk_controller']['plugin_manager']`. The design pattern uses the AbstractPluginManager provided by ZF3 service manager component. So, registration of a plugin under the aformentioned config key looks the same as the declaration of regular dependencies, as AbstractPluginManager actually extends ServiceManager.
91+
92+
Once registered, a plugin can be directly accessed in any controller, by calling a method with the plugin's name(the service name or the key at which the plugin is registered inside the manager)
93+
94+
Controller plugins offer the advantage of globally accessible functionality in any controller without to manually inject dependencies. Plugins should be used for functions that are common to any controller. Do not clutter controller's code with unecessary plugins.
95+
96+
##### Example
97+
```php
98+
//inside a controller
99+
//assume we've already registered a plugin called testPlugin
100+
$this->testPlugin(); //will return the TestPlugin class so you can call any public defined method on it
101+
$this->testPlugin()->someMethod();
102+
```
103+
104+
### Already provided plugins
105+
Note: Each of these plugins requires the associated DK3 or ZE packages to be installed and available in your project.
106+
Although these are optional, if a package is missing, the controller will not have the associated funcionality available
107+
108+
- `authentication` which wraps a AuthenticationInterface and defines methods `hadIdentity()` and `getIentity()`
109+
- `authorization` which wraps an AuthorizationInterface and defines method `isGranted($permission, array $roles = [], $context = null)`
110+
- `flashMessenger` wraps FlashMessenger for one time session based messages support
111+
- `template` wraps TemplateInterface provided by ZE, to make template engine accessible to any controller
112+
- `urlHelper` wraps the UrlHelper class provided by ZE helpers package. Used to generate URIs from routes

composer.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "dotkernel/dot-controller",
3+
"type": "library",
4+
"description": "Controller like middleware with plugin support",
5+
"minimum-stability": "dev",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "n3vrax",
10+
"email": "tibi@apidemia.com"
11+
}
12+
],
13+
"require": {
14+
"php": "^5.6 || ^7.0",
15+
"psr/http-message": "^1.0",
16+
"container-interop/container-interop": "^1.1",
17+
"zendframework/zend-servicemanager": "^3.0"
18+
},
19+
"require-dev": {
20+
"phpunit/phpunit": "^4.8",
21+
"squizlabs/php_codesniffer": "^2.3"
22+
},
23+
"autoload": {
24+
"psr-4": {
25+
"DotKernel\\DotController\\": "src/"
26+
}
27+
},
28+
"autoload-dev": {
29+
"psr-4": {
30+
"DotKernelTest\\DotController\\": "test/"
31+
}
32+
},
33+
"extra": {
34+
"branch-alias": {
35+
"dev-master": "1.0-dev"
36+
}
37+
}
38+
}

src/AbstractActionController.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/**
3+
* @copyright: DotKernel
4+
* @package: dot-controller
5+
* @author: n3vrax
6+
* Date: 8/15/2016
7+
* Time: 5:56 PM
8+
*/
9+
10+
namespace DotKernel\DotController;
11+
12+
use Psr\Http\Message\ResponseInterface;
13+
14+
/**
15+
* Class AbstractActionController
16+
* @package DotKernel\DotController
17+
*/
18+
class AbstractActionController extends AbstractController
19+
{
20+
/**
21+
* @return ResponseInterface
22+
*/
23+
public function dispatch()
24+
{
25+
$request = $this->request;
26+
$action = AbstractController::getMethodFromAction(
27+
strtolower($request->getAttribute('action', 'index')));
28+
29+
if (method_exists($this, $action)) {
30+
return $this->$action();
31+
}
32+
33+
//just go the the next middleware, it will eventually hit a 404 if no one handles the request
34+
$next = $this->getNext();
35+
return $next($this->getRequest(), $this->getResponse());
36+
}
37+
}

src/AbstractController.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
/**
3+
* @copyright: DotKernel
4+
* @package: dot-controller
5+
* @author: n3vrax
6+
* Date: 8/15/2016
7+
* Time: 5:56 PM
8+
*/
9+
10+
namespace DotKernel\DotController;
11+
12+
use DotKernel\DotController\Plugin\PluginManager;
13+
use DotKernel\DotController\Plugin\PluginManagerAwareInterface;
14+
use Psr\Http\Message\ResponseInterface;
15+
use Psr\Http\Message\ServerRequestInterface;
16+
17+
/**
18+
* Class AbstractController
19+
* @package DotKernel\DotController
20+
*/
21+
abstract class AbstractController implements PluginManagerAwareInterface
22+
{
23+
/** @var PluginManager */
24+
protected $pluginManager;
25+
26+
/** @var ServerRequestInterface */
27+
protected $request;
28+
29+
/** @var ResponseInterface */
30+
protected $response;
31+
32+
/** @var callable */
33+
protected $next;
34+
35+
/**
36+
* @param ServerRequestInterface $request
37+
* @param ResponseInterface $response
38+
* @param callable|null $next
39+
* @return ResponseInterface
40+
*/
41+
public function __invoke(
42+
ServerRequestInterface $request,
43+
ResponseInterface $response,
44+
callable $next = null
45+
) {
46+
$this->request = $request;
47+
$this->response = $response;
48+
$this->next = $next;
49+
50+
return $this->dispatch();
51+
52+
}
53+
54+
/**
55+
* @return ServerRequestInterface
56+
*/
57+
public function getRequest()
58+
{
59+
return $this->request;
60+
}
61+
62+
/**
63+
* @return ResponseInterface
64+
*/
65+
public function getResponse()
66+
{
67+
return $this->response;
68+
}
69+
70+
/**
71+
* @return callable
72+
*/
73+
public function getNext()
74+
{
75+
return $this->next;
76+
}
77+
78+
public abstract function dispatch();
79+
80+
/**
81+
* Method overloading: return/call plugins
82+
*
83+
* If the plugin is a functor, call it, passing the parameters provided.
84+
* Otherwise, return the plugin instance.
85+
*
86+
* @param string $method
87+
* @param array $params
88+
* @return mixed
89+
*/
90+
public function __call($method, $params)
91+
{
92+
$plugin = $this->plugin($method);
93+
if (is_callable($plugin)) {
94+
return call_user_func_array($plugin, $params);
95+
}
96+
return $plugin;
97+
}
98+
99+
/**
100+
* Get plugin instance
101+
*
102+
* @param string $name Name of plugin to return
103+
* @param null|array $options Options to pass to plugin constructor (if not already instantiated)
104+
* @return mixed
105+
*/
106+
public function plugin($name, array $options = null)
107+
{
108+
return $this->getPluginManager()->get($name, $options);
109+
}
110+
111+
/**
112+
* @return PluginManager
113+
*/
114+
public function getPluginManager()
115+
{
116+
return $this->pluginManager;
117+
}
118+
119+
/**
120+
* @param PluginManager $pluginManager
121+
* @return $this
122+
*/
123+
public function setPluginManager(PluginManager $pluginManager)
124+
{
125+
$this->pluginManager = $pluginManager;
126+
return $this;
127+
}
128+
129+
/**
130+
* Transform an "action" token into a method name
131+
*
132+
* @param string $action
133+
* @return string
134+
*/
135+
public static function getMethodFromAction($action)
136+
{
137+
$method = str_replace(['.', '-', '_'], ' ', $action);
138+
$method = ucwords($method);
139+
$method = str_replace(' ', '', $method);
140+
$method = lcfirst($method);
141+
$method .= 'Action';
142+
return $method;
143+
}
144+
145+
146+
}

0 commit comments

Comments
 (0)