contributte / planette-site

💀 [DISCONTINUED] All the roads go through the Planette
https://planette.vercel.app
MIT License
9 stars 3 forks source link

2011-01-30: http-routovani-v-cli #133

Open paveljanda opened 7 years ago

paveljanda commented 7 years ago

Potřebujete spouštět různé úlohy z příkazové řádky, ve kterých je třeba tvořit url adresy nadefinované routami, a nevíte jak na to? Čtěte dále...

Problém

Jak docílím routování na správné presentery a akce v CLI módu (příkazová řádka) stejně jako u HTTP (url adresa), abych nemusel psát url adresy "natvrdo" a těžce vypocené routy tak nepřišly vniveč?

Nejjednoduší je problém vysvětlit na nějakém příkladě:

Náš příklad obsahuje tři akce. Dvě pro HTTP požadavek a jednu pro CLI požadavek.

Jednoduchý Presenter obsluhující tyto tři akce by mohl vypadat následovně:

use Nette\Application\Responses\TextResponse,
        Nette\Security\AuthenticationException;

class DefaultPresenter extends BasePresenter
{

        public function renderDefault()
        {
                $source = '<h1>Úvodní stránka</h1><a href="' . $this->link('login') . '">Přihlášení</a>';$
                $this->sendResponse(new TextResponse($source));
        }

        public function renderLogin()
        {
                $source = '<h1>Přihlášení</h1>';
                $this->sendResponse(new TextResponse($source));
        }

        public function actionCron()
        {
                if (!$this->getContext()->params['consoleMode']) {
                        throw new AuthenticationException;
                }

                $link = $this->link('//login');

                // odeslani napr. mailu s linkem
                echo $link;

                $this->terminate();
        }

}

Routy by pak mohly vypadat takto:

use Nette\Application\Routers\Route,
        Nette\Application\Routers\CliRouter;

if ($container->params['consoleMode']) {
        $application->allowedMethods = FALSE;
        $router[] = new CliRouter('Default:default');
} else {
        $router[] = new Route('index.php', 'Default:default', Route::ONE_WAY);
        $router[] = new Route('prihlaseni', 'Default:login');
        $router[] = new Route('<presenter>/<action>[/<id>]', 'Default:default');
}

CliRouter definovaný výše mi nefungoval, následující řešení ano. (MartyIX k 4.10.2011)

$this[] = new CliRouter(array("action"=>'Default:default'));

Přes webový prohlížeč se dostaneme k akcím:

Přes příkazovou řádku se dostaneme k akci:

O routování HTTP požadavků se nám postará Nette, které zjistí na jaké doméně je akce volána, jaký je použit protokol, port, atp. Problém ale je, že v režimu příkazové řádky HTTP požadavek neexistuje, tudíž Nette nemá jak zjistit doménu, protokol, apod.

Řešení

Řešení je velmi jednoduché. Je potřeba "podstrčit" Nette vlastní nadefinovaný objekt reprezentující HTTP požadavek. Nette s ním pak pracuje jako by si ho vytvořil sám ;-).

Díky tomu že Nette používá k získaní objektu [api:Nette\Http\Request] službu, k jejiž inicializaci je použita továrna, není nic jednoduššího než říct Nette: "Pro vytvoření Http\Request nevolej svou továrnu, ale volej mou lepší továrnu :-)"

A vzhledem k tomu, že další text by byl zbytečný, tak zde je upravený bootstrap.php, který se o vše postará:

use Nette\Diagnostics\Debugger,
        Nette\Utils\Strings,
        Nette\Application\Routers\Route,
        Nette\Application\Routers\CliRouter,
        Nette\Application\Routers\SimpleRouter,
        Nette\Http\Url,
        Nette\Http\UrlScript,
        Nette\Http;

// Load Nette Framework
$params['libsDir'] = __DIR__ . '/../libs';
require $params['libsDir'] . '/Nette/loader.php';

// Enable Nette Debugger for error visualisation & logging
Debugger::$logDirectory = __DIR__ . '/../log';
Debugger::$strictMode = TRUE;
Debugger::enable();

// Load configuration from config.neon file
$configurator = new Nette\Configurator;
$configurator->container->params += $params;
$configurator->container->params['tempDir'] = __DIR__ . '/../temp';
$container = $configurator->loadConfig(__DIR__ . '/config.neon');

// Registrace vlastni tovarny na vytvareni 'Nette\Http\IRequest'.
if ($container->params['consoleMode']) {
        $container->removeService('httpRequest');
        $container->addService('httpRequest', function() {
                // Podle potreby muzeme pouzit nastaveni z configu nebo vzit z parametru prikazove radky, aj.
                $uri = new UrlScript;
                $uri->scheme = 'http';
                $uri->port = Url::$defaultPorts['http'];
                $uri->host = 'example.com';
                $uri->path = '/';
                $uri->canonicalize();
                $uri->path = Strings::fixEncoding($uri->path);
                $uri->scriptPath = '/';
                return new Http\Request($uri, array(), array(), array(), array(), array(), 'GET', null, null);
    });
}

// Init application
$application = $container->application;

// Setup router
$router = $container->router;
if ($container->params['consoleMode']) {
        $application->allowedMethods = FALSE;
        $router[] = new CliRouter('Default:default');
} else {
        $router[] = new Route('index.php', 'Default:default', Route::ONE_WAY);
        $router[] = new Route('prihlaseni', 'Default:login');
        $router[] = new Route('<presenter>/<action>[/<id>]', 'Default:default');
}

// Configure and run the application!
$application = $container->application;
//$application->catchExceptions = TRUE;
$application->errorPresenter = 'Error';
$application->run();
paveljanda commented 7 years ago

author: mrtnzlml (mrtnzlml@gmail.com)