PHPackages                             talesoft/tale-controller - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. talesoft/tale-controller

ActiveLibrary[Framework](/categories/framework)

talesoft/tale-controller
========================

A controller middleware for talesoft/tale-app

0.1(10y ago)015MITPHPPHP &gt;=5.5.0

Since Feb 29Pushed 10y ago3 watchersCompare

[ Source](https://github.com/Talesoft/tale-controller)[ Packagist](https://packagist.org/packages/talesoft/tale-controller)[ Docs](http://docs.talesoft.io/tale-framework/tale/controller)[ RSS](/packages/talesoft-tale-controller/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (5)Versions (2)Used By (0)

Tale Controller
===============

[](#tale-controller)

**A Tale Framework Component**

What is Tale Controller?
========================

[](#what-is-tale-controller)

A middleware for `talesoft/tale-app` that allows easy instanciation and handling of controllers.

You can either use single, static controllers or use a dispatcher that automatically handles everything.

Installation
============

[](#installation)

Install via Composer

```
composer require "talesoft/tale-controller:*"
composer install
```

Usage
=====

[](#usage)

Single usage
------------

[](#single-usage)

A single controller can make up a whole website. This is really useful for small websites with 5-10 sub-pages. No configuration needed.

```
use Tale\App;
use Tale\Controller;

//Define a controller of some kind
class MyController extends Controller
{

    //GET|POST /
    public function indexAction()
    {

        $res = $this->getResponse();
        $res->getBody()->write('Hello index!');
        return $res;
    }

    //GET|POST /?action=about-us
    public function aboutUsAction()
    {

        $res = $this->getResponse();
        $res->getBody()->write('About us!');
        return $res;
    }

    //GET /?action=contact
    public function getContactAction()
    {

        $res = $this->getResponse();
        $res->getBody()->write('Contact form!');
        return $res;
    }

    //POST /?action=contact
    public function postContactAction()
    {

        //Handle contact form
        $res = $this->getResponse();
        $res->getBody()->write('Success!');
        return $res;
    }
}

//Create a new app context
$app = new App();

//Make sure we can target the "action" somehow.
//Normally you'd use a router, we use a simple GET-variable in this case
//"index.php?action=about-us" would dispatch "MyController->aboutUsAction"

//This is a simple middleware mapping query's "action" to the required request attribute "action"
$app->append(function($req, $res, $next) {

    $params = $req->getQueryParams();
    $action = isset($params['action']) ? $params['action'] : null;

    if ($action)
        $req = $req->withAttribute('action', $action);

    return $next($req, $res);
});

//Append our controller middleware
$app->append(MyController::class);

//Display the app
$app->display();
```

Using the Dispatcher
--------------------

[](#using-the-dispatcher)

When apps get larger, you want to split functionality into single modules. With the Dispatcher you can control an automatic controller dispatching mechanism.

Imagine the following controller structure:

```
/
    /index.php
    /app
        /controllers
            IndexController.php
            ContactController.php
            PortfolioController.php
            /Admin
                /IndexController.php

```

This is a common case that the dispatcher can handle with a low configuration profile.

```
use Tale\App;
use Tale\Controller\Dispatcher;

//Create a new app context
$app = new App([
    'controller' => [
        'nameSpace' => 'My\\Controllers',
        'loader' => ['path' => __DIR__.'/app/controllers']
    ]
]);

//This is a middleware mapping "module", "controller" and "action" GET-values to
//ServerRequestInterface-attributes
$app->append(function($req, $res, $next) {

    $params = $req->getQueryParams();
    $module = isset($params['module']) ? $params['module'] : null;
    $controller = isset($params['controller']) ? $params['controller'] : null;
    $action = isset($params['action']) ? $params['action'] : null;

    if ($module)
        $req = $req->withAttribute('module', $module);

    if ($controller)
        $req = $req->withAttribute('controller', $controller);

    if ($action)
        $req = $req->withAttribute('action', $action);

    return $next($req, $res);
});

//Append our dispatcher middleware
$app->append(Dispatcher::class);

//Display the app
$app->display();
```

Now you could call the `editAction` of the `Admin\IndexController` by requesting `index.php?module=admin&action=edit`

Notice that all values are completely optional.

ServerRequestInterface attributes
---------------------------------

[](#serverrequestinterface-attributes)

The following attributes are handled by the dispatcher:

### `module` (Default: `null`)

[](#module-default-null)

Tells the dispatcher which namespace to find controllers in. The `controller.nameSpace` option will be prepended in any case.

### `controller` (Default: `index`)

[](#controller-default-index)

Tells the dispatcher, which controller to load. `my-blog` will be parsed to `MyBlogController`

The following attributes are handled by the controllers:

### `action` (Default: `index`)

[](#action-default-index)

Tells the controller which action to call. `edit-user` will be parsed to `editUserAction`

If there's an `getEditUserAction`-method, that one will only listen to `GET`-requests The same goes for `POST`-requests with `postEditUserAction`. Not prefixing will handle all request methods.

### `id` (Default: null)

[](#id-default-null)

Specifies the first parameter given to the action. Allowed values are numerical values and canonical strings (`some-user-name`)

### `format` (Default: html)

[](#format-default-html)

Specifies the format the result should appear in. This mostly equals the file extension of the called URI (`/some-file.xml` will yield format `xml`)

This format is to be used by some kind of output formatter/renderer.

Handle 404-errors
-----------------

[](#handle-404-errors)

What if there's no fitting controller/it doesn't extend the correct class/the input is malformed etc.

That's all checked by tale-controller. Upon any kind of failure, control will be passed on to the next middleware.

Handling 404 is as simple as adding an "end"-middleware that results in said 404-error

```
$app->append(Dispatcher::class)
    ->append(function($req, $res) {

        $res->getBody()->write('404 - Not found!');
        return $res->withStatus(404);
    });
```

Shorten things up
-----------------

[](#shorten-things-up)

This module is specially designed to work with the `Tale\Router`. You can use it stand-alone, but it will require extra-work (but is still really cool!)

Here's an example of how it could look like by installing `talesoft/tale-router` via composer

**env.json**

```
{
    "middlewares": ["Tale\\Router"],
    "routes": {
        "/blog/:action?/:id?": "My\\Controller\\BlogController",
        "/:controller?/:action?/:id?.:format?": "Tale\\Controller\\Dispatcher"
    },
    "controller": {
        "nameSpace": "My\\Controller",
        "loader": {
            "path": "{{path}}/app/controllers"
        }
    }
}
```php

**index.php**
```php

use Tale\App;

$app = new App(['path' => __DIR__]);
$app->display();
```

Configuration options
---------------------

[](#configuration-options)

All configuration options.

```
controller.defaultModule            The default module to use (Default: null)
controller.defaultController        The default controller to use (Default: index)
controller.defaultAction            The default action to use (Default: index)
controller.defaultId                The default ID to use (Default: null)
controller.defaultFormat            The default format to use (Default: html)

controller.nameSpace                The namespace where controllers reside in (Default: null)
controller.modules                  A map [module-name => namespace] for module mapping

controller.controllerPattern        The pattern for controllers (Default: %sController)
controller.controllerInflection     How to inflect the controller name (Default: [Tale\Inflector, camelize]

controller.actionPattern            The pattern for actions (Default: %sAction)
controller.actonInflection          How to inflect the action name (Default: [Tale\Inflector, variablize]
controller.getActionPattern         The pattern for GET actions (Default: get%sAction)
controller.getActonInflection       How to inflect the GET action name (Default: [Tale\Inflector, camelize]
controller.postActionPattern        The pattern for POST actions (Default: post%sAction)
controller.postActonInflection      How to inflect the POST action name (Default: [Tale\Inflector, camelize]

controller.loader.enabled           Enable an auto-loader for controllers (Default: true)
controller.loader.path              The path for controller classes (Default: getcwd()/controllers)
controller.loader.pattern           The pattern for controller loading (Default: %s.php)

```

Using multiple dispatchers
--------------------------

[](#using-multiple-dispatchers)

Using multiple dispatchers is as easy as extending the dispatcher. You can set an option namespace to load different configuration values.

```
class FirstDispatcher
{

    public function getOptionNameSpace()
    {

        return 'firstDispatcher';
    }
}

class SecondDispatcher
{

    public function getOptionNameSpace()
    {

        return 'secondDispatcher';
    }
}

$app->get(Router::class)
    ->all('/:controller?/:action?', FirstDispatcher::class)
    ->all('/sub-module/:controller?/:action?', SecondDispatcher::class);

$app->display();
```

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

3722d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/75a80e0830e63c723808d021d3a1648a2643db60f4ac2e40842da05f227f956b?d=identicon)[TorbenKoehn](/maintainers/TorbenKoehn)

---

Top Contributors

[![TorbenKoehn](https://avatars.githubusercontent.com/u/1403556?v=4)](https://github.com/TorbenKoehn "TorbenKoehn (7 commits)")

---

Tags

controllerdispatcher

### Embed Badge

![Health badge](/badges/talesoft-tale-controller/health.svg)

```
[![Health](https://phpackages.com/badges/talesoft-tale-controller/health.svg)](https://phpackages.com/packages/talesoft-tale-controller)
```

###  Alternatives

[htmlburger/wpemerge

A micro framework which modernizes WordPress as a CMS development by providing tools to implement MVC and more.

456137.8k8](/packages/htmlburger-wpemerge)[yiisoft/middleware-dispatcher

PSR-15 middleware dispatcher

17369.2k13](/packages/yiisoft-middleware-dispatcher)[cakephp/event

CakePHP event dispatcher library that helps implementing the observer pattern

23319.7k13](/packages/cakephp-event)[martynbiz/slim3-controller

Provides controller functionality to Slim Framework v3. Also includes PHPUnit TestCase for testing controllers.

2814.4k1](/packages/martynbiz-slim3-controller)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
